rfc:throw_expression

This is an old revision of the document!


PHP RFC: throw expression

Introduction

Since in PHP throw is a statement it makes it impossible to throw exceptions in places where only expressions are allowed, like arrow functions, the coalesce operator and the ternary/elvis operator. This RFC proposes converting the throw statement into an expression so that these cases become possible.

Proposal

Allow using throw in any context where an expression is accepted. These examples come to mind that seem useful:

$callable = fn() => throw new Exception();
// This was previously not possible since arrow functions only accept a single expression while throw was a statement.
 
$value = $nullableValue ?? throw new InvalidArgumentException();
// $value can now be considered non-nullable.
 
$value = $falsableValue ?: throw new InvalidArgumentException();
// $value can now be considered truthy.
 
$value = !empty($array)
    ? reset($array)
    : throw new InvalidArgumentException();
// $value is only set if the array is not empty.

There are other places where it could be used which are more controversial. These cases are allowed in this RFC.

$condition && throw new Exception();
$condition || throw new Exception();
$condition and throw new Exception();
$condition or throw new Exception();
// An if statement could make the intention clearer

Operator precedence

If throw becomes an expression operator precedence becomes relevant. There are few operators that would even work in combination with the throw statement today. The ones that do are =, ??= and ??.

try {
    throw $exception = new Exception('How useless');
} catch (Exception $e) {}
echo $exception->getMessage();
//> How useless
 
try {
    $exception = null;
    throw $exception ??= new Exception('Still pretty useless');
} catch (Exception $e) {}
echo $exception->getMessage();
//> Still pretty useless
 
try {
    $exception = null;
    throw $exception ?? new Exception('Kind of useful');
} catch (Exception $e) {
  echo $e->getMessage();
}
//> Kind of useful

While these cases are probably more rare it's important they continue working.

Although any binary operator can be used in the right-hand side of the throw statement none of them can evaluate to a Throwable value.

throw new Exception() + 1;
//> Notice: Object of class Exception could not be converted to number in ...
//> Fatal error: Uncaught Error: Can only throw objects in ...

This could change if the RFC: Userspace operator overloading gets accepted. There's already a C extension that allows you to do the same thing.

For these reasons, this RFC proposes to use the lowest precedence for binary operators possible. This way all current code, even if broken, continues behaving the same way while also leaving the possibility of custom operators open in the future.

One minor draw back is that the throw expression couldn't be used between two binary operators:

$condition || throw new Exception('$condition must be truthy')
  && $condition2 || throw new Exception('$condition2 must be truthy');
// Evaluated as
$condition || (throw new Exception('$condition must be truthy') && $condition2 || (throw new Exception('$condition2 must be truthy')));
// Instead of
$condition || (throw new Exception('$condition must be truthy'))
  && $condition2 || (throw new Exception('$condition2 must be truthy'));

But I see little use for code like this.

Backward Incompatible Changes

None, specifically because the lowest precedence was chosen.

Other languages

The same was implemented in C# 7.0 in 2017. 1)

There aren't many other languages that allow this. There's an ECMAScript proposal because the same issues exist there. 2)

Proposed PHP Version(s)

Proposed version is PHP 8.

RFC Impact

To SAPIs

None.

To Existing Extensions

None.

To Opcache

None.

Proposed Voting Choices

As this is a language change, a 2/3 majority is required. The vote is a straight Yes/No vote for accepting the RFC and merging the patch.

References

Old discussion in internals list

rfc/throw_expression.1584889803.txt.gz · Last modified: 2020/03/22 15:10 by ilijatovilo