throw expressionというRFCが投票中です。
最初のアイデアは2019/12/06のSebastiaan Andewegによるツイート。
Really liking the arrow functions in PHP 7.4 so far! Unfortunately already found one missing feature: you can't throw Exceptions from them. The following results in a 'unexpected T_THROW' syntax error:
— Sebastiaan Andeweg (@sebandeweg) 2019年12月5日
fn() => throw new Exception('nope');
Would've cleaned up my test, but alas.
それに対して2020/03/19にCarusoが反応し、そしてその日のうちにiluuu1994が最初のプルリクを出しました。
はえーよ。
throw expression
Introduction
PHPのthrowは文であるため、アロー関数や三項演算子、NULL合体演算子などの式しか許されない場所から例外を投げることができません。
このRFCでは、それらを可能にするためthrow文を式に変更することを提案します。
Proposal
式を記述可能なあらゆるコンテキストでthrowが利用可能になります。
以下は思いついた便利そうな例です。
// アロー関数
$callable = fn() => throw new Exception();
// nullチェック
$value = $nullableValue ?? throw new InvalidArgumentException();
// trueっぽいチェック
$value = $falsableValue ?: throw new InvalidArgumentException();
// 空ではない配列チェック
$value = !empty($array)
? reset($array)
: throw new InvalidArgumentException();
他にも、議論の余地のある使用方法があります。
このRFCでは、以下のような記述も許可されています。
// ifを使った方が意図が明確になる
$condition && throw new Exception();
$condition || throw new Exception();
$condition and throw new Exception();
$condition or throw new Exception();
Operator precedence
throwが式になると、優先順位を決める必要があります。
以下は現時点で有効な書式の例です。
throw $this->createNotFoundException();
// こうなる
throw ($this->createNotFoundException());
// こうではない
(throw $this)->createNotFoundException();
throw static::createNotFoundException();
// こうなる
throw (static::createNotFoundException());
// こうではない
(throw static)::createNotFoundException();
throw $userIsAuthorized ? new ForbiddenException() : new UnauthorizedException();
// こうなる
throw ($userIsAuthorized ? new ForbiddenException() : new UnauthorizedException());
// こうではない
(throw $userIsAuthorized) ? new ForbiddenException() : new UnauthorizedException();
throw $maybeNullException ?? new Exception();
// こうなる
throw ($maybeNullException ?? new Exception());
// こうではない
(throw $maybeNullException) ?? new Exception();
throw $exception = new Exception();
// こうなる
throw ($exception = new Exception());
// こうではない
(throw $exception) = new Exception();
throw $exception ??= new Exception();
// こうなる
throw ($exception ??= new Exception());
// こうではない
(throw $exception) ??= new Exception();
throw $condition1 && $condition2 ? new Exception1() : new Exception2();
// こうなる
throw ($condition1 && $condition2 ? new Exception1() : new Exception2());
// こうではない
(throw $condition1) && $condition2 ? new Exception1() : new Exception2();
共通して言えるのは、全てがthrowキーワードより高い優先順位を持つということです。
このため、このRFCではthrowキーワードの優先順位を可能な限り低くすることを提案します。
現在有効なコードは、たとえ直感に反する動作だったとしても、今後も同じ動作をし続けます。
なぜなら、一般的にthrowは最後に使用するべき演算子であり、それ以降に記述した式は評価されないからです。
低い優先順位の唯一の欠点は、短絡評価のために括弧が必須になることです。
$condition || throw new Exception('$condition must be truthy') && $condition2 || throw new Exception('$condition2 must be truthy');
// こうなる
$condition || (throw new Exception('$condition must be truthy') && $condition2 || (throw new Exception('$condition2 must be truthy')));
// こうではない
$condition || (throw new Exception('$condition must be truthy')) && $condition2 || (throw new Exception('$condition2 must be truthy'));
もっとも、こんなコードはほぼ使われていないでしょう。
Backward Incompatible Changes
後方互換性のない変更はありません。
Other languages
C#では同じ文法が2017年に実装されました。
このような言語は他にはあまりありません。
ECMAScriptにプロポーザルがありますが、これは同じ問題を抱えているからです。
Proposed PHP Version(s)
PHP8。
投票
投票は2020/04/19まで、2/3の賛成で受理されます。
2020/04/06時点では賛成14、反対1で、受理される見込みです。
過去のML
9年前とか15年前にも同じ発想があったようですが、そのときは立ち消えになりました。
当時とはPHPのおかれた環境やRFCの出し方などがだいぶ異なることと、そしてなにより実物のプルリクがあるというのは大きいでしょう。
感想
ややこしいよね文と式。
全てが式になればいいのに。
というわけで、今後はもっと気軽にthrowすることができるようになります。
それどころかアロー関数で引数によって値を返したり例外Aを出したり例外Bを出したりすることもできちゃいますよ。
まあ正直、throwを出すようなややこしい式をアロー関数に書くんじゃないよと思ったりはするわけですが。