ツイート
シェア
LINEで送る
B! はてぶでブックマーク
Pocketでブックマーク
RSSフィード

PHP8,『Throwは式になりました』ってどういうこと? 見た目から式だと思ってた。

php
イラストダウンロードサイト【イラストAC】
の画像をもとに加工しています。

PHP8では、例外をスローするのに使うthrowキーワードは式になったそう。

聞いてすぐは、SNSでの報告みたいな文面だなーと訳の分からない感想を持っただけでしたが、これ、いろんなことができそう。

でも、式ってことはif文の条件に使えるってことだよね? どういう動きになるの?

PHP8から式。それより前は文。

一般的に式とは、ひとつの処理のことを言います。変数代入や足し算引き算の演算、関数コールなど。

特徴として、イコール(=)で代入でき、最後にセミコロン(;)を付け、if文などの条件に使えるもの。

条件に使う変数そのものも式です。

足し算・引き算は式というのは小学校で習うのでだれでも分かりますね?

中学(高校かな?)で習う2次関数も式なので、関数コールもイメージしやすい。

プログラミングでは、学校で習わない、=や+,-などの演算子を使わないものも式になるのがポイント。

一方、文は複数の式をまとめて任意の処理をするもの。

if文、for文、function定義など。一般的に中括弧({})で囲い、終端文字にセミコロン(;)を使いません。

そういう意味ではクラス定義も文の亜種と言える。

throwは見た目は式だが文だった

PHP8より前は例外スローは文でした。しかし、throwキーワードは中括弧を使いません。

例外スロー
throw new Exception();

セミコロンで終わってるし見た目は式です。

ひとついい忘れてた。式の特徴は、その場で何らかの値が返せるものです。

throwは見た目は式でも、処理としてその場で何かが返ってきません。例外を発生させたらそこで処理は終了。

例外の値は、例外スローを含む処理を呼び込んだ元でキャッチして参照できます。

例外スローは見た目は式でも処理は文という特殊な文だったんですね?

throwが文から式になってできること

さて、基本情報はこのへんにして本題です。throwが文から式になったことで例外スローができる範囲が広がりました。

そのへんをひとつひとつ見ていきましょう。

throwがアロー関数で使える

アロー関数の処理はひとつの式しか書けない制限があるので、これまで例外スローはできませんでした。

これがPHP8からできるようになります。

<?php

$callable = fn() => throw new Exception();
$callable();
php80の実行結果
PHP Fatal error:  Uncaught Exception in /home/vagrant/php-test.php:3
Stack trace:
#0 /home/vagrant/php-test.php(4): {closure}()
#1 {main}
  thrown in /home/vagrant/php-test.php on line 3

PHP7以前は、文法はどうみても間違ってないのにシンタックスエラーになってイライラいした人もいたでしょう。

例外スローが見た目が式の弊害。

それがなくなったので、良い仕様変更だと思います。

throwが三項演算子で使える

すでに、お察しだと思いますが、三項演算子でもthrow例外処理が使えるようになりました。

<?php

$flag = true;
$result = $flag ? 'OK' : throw new Exception();

echo $result . PHP_EOL;

$flag = false;
$result = $flag ? 'OK' : throw new Exception();
php80の実行結果
OK
PHP Fatal error:  Uncaught Exception in /home/vagrant/php-test.php:9
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test/php8-test.php on line 9

PHP7以前ではシンタックスエラーになるのは上に同じ。

throwがNull合体演算子で使える

すでにお察しパート2。Null合体演算子でもthrow例外処理が使えます。

<?php

$str = '123';
$result = $str ?? throw new Exception();

echo $str . PHP_EOL;

$str = null;
$result = $str ?? throw new Exception();

echo $str . PHP_EOL;
PHP Fatal error:  Uncaught Exception in /home/vagrant/php-test.php:9
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test/.php on line 9

PHP7以前ではシンタックスエラーになるのは上に同じ。

まったく意味は無いけどシンタックス上はOKなthrow式

throwが式になったことで、ある疑問を持った人もいると思います。

『変数代入したらどうなるの?』

『if文の条件に使ったらどうなるの?』

その答えは、文法上はエラーにならない。しかも処理は動く。

throw式の変数代入
<?php

$cond = throw new Exception();
echo 'program end' . PHP_EOL;
php80の実行結果
PHP Fatal error:  Uncaught Exception in /home/vagrant/php-test.php:3
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test.php on line 3
if文の条件で使うthrow式
<?php

$flag = true;

if ($flag && throw new Exception()) {
    echo 'flags is true' . PHP_EOL;
}

echo 'program end' . PHP_EOL;
php80の実行結果
PHP Fatal error:  Uncaught Exception in /home/vagrant/php-test.php:5
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test.php on line 5

throw式のところで必ず例外スローが発生するので、まったく意味はありません。

PHP RFCでは、括弧(())を使った優先順位とかにも触れていて、次のようなコメントがあります。

The common theme here is that everything after the throw keyword has a higher precedence. For this reason this RFC proposes to use the lowest operator precedence possible. All the current code, even if broken or strange, will continue behaving the same way. This isn't a problem because generally throw should be the last operator you're using as every expression after it wouldn't be evaluated anyway.

ここで共通するのは、throwキーワードの後にあるものはすべて優先順位が高いということです。このため、このRFCでは演算子の優先順位をできるだけ低くすることを提案しています。現在のコードはすべて、たとえ壊れていても、奇妙であっても、同じように動作し続けるでしょう。一般に throw は最後に使う演算子であるべきで、それ以降の式はどうせ評価されませんから、これは問題ではありません。

But I see little use for code like this.

しかし、このようなコードにはほとんど使い道がありません。

PHP RFC

ようは、

  • 見た目おかしなものでも文法上まちがいではない(壊れてもはすごいヤダ。)
  • 同じ動きをする(例外スローしてその先の処理は行わない。)

というか、最後の最後に意味は無いって思いっきり言ってる。!!!

文法上まちがいじゃなくても、この使い方はやめましょう。というかロジックとして意味不明だから書かないですが。

ここでの挙動で気になるところはまだありますが、意味がないので割愛します。

意味がないのにあーだこーだいうと、一部正しいと思われて混乱とかんちがいを招くので。

紹介したPHP RFCの文面の少し前に

『もっと議論を呼ぶような使い方ができる場所がある』

の前フリがあって説明が始まってるので、このおかしさは確信犯。

想像でしかないけど、数ある式の中でthrowだけを条件での使用NGという処理を作るのが面倒だったんじゃないかな?

(もしくは、そう簡単にできるものじゃなかったか。)

だからヘンテコリンでも文法上OKになっちゃってるんだろう。

前の投稿
PHP8, namespaceの名前空間に予約語が使える。トークンとして扱うからできるんだって。
PHP8, 例外処理(throw)がアロー関数で使えるようになった。
次の投稿
コメントを残す

*