🚪

PHP8.4で新しくなったexit関数

に公開

去年11月ごろにPHP8.4がリリースされ、それに合わせて公式日本語ドキュメントの修正を一部お手伝いさせてもらいました。その内、あまり話題にはなっていないけど言語仕様的に大きな変更にあたるexit(die)の挙動を紹介します。

RFC

https://wiki.php.net/rfc/exit-as-function

和訳は以下の記事で出ています。
https://qiita.com/rana_kualu/items/60912b8cac8deeaa3560

実装

https://github.com/php/php-src/pull/13483

オペコードZEND_EXITが消されているのが分かります。

概要

  • 基本的な使い方は変わらない
  • 言語構造から関数になった
  • 引数の型変換が通常の型変換のセマンティクスに従うようになった
  • declare(strict_types=1)が効くようになった
  • 名前付き引数でステータスを渡せるようになった
  • "exit"()が可能になった
  • 括弧無しでの呼び出しはできるまま

基本的な使い方は変わらない

基本的にはexit(0)で正常終了、それ以外の引数での呼び出しは異常終了を表します。

言語構造から関数になった

PHPは実行までに字句構文解析→AST変換→オペコード変換→実行の順を経ますが、これまでは専用のZEND_EXITというオペコードに変換されていました。これが8.4以降では他の関数同様、関数呼び出しに変わります。
vldを使って8.4でのオペコードを出力してみると、以下の通りexitはINIT_FCALL(ZEND_INIT_FCALL)になっていました。

line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    3     0  E > > INIT_FCALL                                               'exit'
          1*       SEND_VAL                                                 0
          2*       DO_ICALL                                                 
          3*     > RETURN                                                   1

もとのコード

<?php
exit(0);

字句構文とASTではこれまで同様、exit専用のトークンが用意されています。

引数の型変換が通常の型変換のセマンティクスに従うようになった

平たく言うと、引数の型の自動変換(Type juggling)が普通の関数と同じになりました。

たとえば、8.3以前ではbooleanを渡すと、strictモードに関係なくstringに変換されるので終了コードは0でした。これが8.4以降では通常の関数での型の自動変換同様、booleanが数値に変換されるので、true/falseで終了コードが異なります。(strictモードでbooleanを渡すとエラー)

// PHP:8.3

exit(true);
// exit code: 0
exit(false);
// exit code: 0
// PHP:8.4

exit(true);
// exit code: 1
exit(false);
// exit code: 0

declare(strict_types=1)が効く

strictモードでは引数statusにstring型かint型以外を渡すとTypeErrorが呼ばれるようになりました。

<?php declare(strict_types=1);

exit(true);
// Fatal error: Uncaught TypeError: exit(): Argument #1 ($status) must be of type string|int, false given

名前付き引数でステータスを渡せるようになった

以下の書き方が可能になりました。

<?php
exit(satus: 1);

"exit"()が可能になった

他の関数同様、関数名の文字列から関数を呼び出す可変関数として使用することができるようになりました。

<?php
"exit"();

コールバックとして渡すことも可能です。

<?php
function run(callable $callback) {
    $callback("終了します\n");
}
run('exit');

括弧無しでの呼び出しはできるまま

公式ドキュメントの以下の通り、字句構文とAST上では特別扱いされているので、通常の関数とは違って、これまで通り括弧無しで呼び出せます。

exit() は特殊な関数です。 パーサーに専用のトークンがあるため、文として使用して(つまり、括弧なしで)、 デフォルトのステータスコードでスクリプトを終了させることができます。

RFCのFuture scopeにあるように、将来的には無くなるかも。

感想

翻訳の残タスクをざっと見て、「exitなら知ってる!」と思って翻訳作業を始めましたが、途中から結構難しいことに気が付きました。翻訳量自体は少なかったですが、ちゃんと意味を理解しようと思うと、そこそこ難易度が高かったです。
また、PHPを使う側としては特にそれほど注目されるような変更ではないので、PHP8.4の新機能をまとめたような記事でもさらっとしか触れられていないものが多いですが、言語仕様的には大きな変更だと思ったので記事にしてみました。

Discussion