PHP Advent Calendar 2013 in Adventar の24日目です。
PHP は set_error_handler
で↓のようにエラーをハンドリングできます。
<?php set_error_handler(function ($errno, $errstr, $errfile, $errline) { echo "Error[$errno]: $errstr\n"; }); echo $undefined;
出力
Error[8]: Undefined variable: undefined
ですが Fatal error になるエラーのほとんどは set_error_handler
では拾うことはできません。
<?php set_error_handler(function ($errno, $errstr, $errfile, $errline) { echo "Error[$errno]: $errstr\n"; }); undefined();
出力
Fatal error: Call to undefined function undefined() in /in/bbsF2 on line 6 Process exited with code 255.
が、E_RECOVERABLE_ERROR
は放置すると Fatal error になりますが set_error_handler
で拾って処理を続行することが出来ます。
E_RECOVERABLE_ERROR
はリファレンスによると キャッチできる致命的なエラー とのことです。
どういう状況で発生するか、その全てはわかりませんが、とりあえずタイプヒンティングに違反すると発生します。
なので・・次のようにタイプヒンティングをガン無視することが出来ます・・・おお、こわいこわい
<?php set_error_handler(function ($errno, $errstr, $errfile, $errline) {}); function func(stdClass $obj) { var_dump($obj); } func(123);
出力
int(123)
実用的な使い方としては次のように ErrorException(あるいはその派生)を投げることかなと思います。
<?php set_error_handler(function ($errno, $errstr, $errfile, $errline) { if ($errno == E_RECOVERABLE_ERROR) { throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } }); function func(stdClass $obj) { var_dump($obj); } try { func(123); } catch (ErrorException $ex) { echo "ErrorException: {$ex->getMessage()}\n"; }
出力
ErrorException: Argument 1 passed to func() must be an instance of stdClass, integer given, called in /in/Ba3v2 on line 16 and defined
ざっくり調べた感じタイプヒント以外にも次のような状況で発生します(grep するとこれら以外にも見つかったので、他にもあります)。
Closure を直接インスタンス化しようとしたとき
<?php // Catchable fatal error: Instantiation of 'Closure' is not allowed new Closure();
Closure のプロパティを取得/設定したとき
<?php $f = function () {}; // Catchable fatal error: Closure object cannot have properties $f->x; // Catchable fatal error: Closure object cannot have properties $f->x = 1;
Generator を直接インスタンス化しようとしたとき
<?php // Catchable fatal error: The "Generator" class is reserved for internal use and cannot be manually instantiated new Generator();
__toString が文字列を返さなかったとき
<?php class Hoge { public function __toString() { return 0; } } // Catchable fatal error: Method Hoge::__toString() must return a string value echo new Hoge;
勿論これらのエラーも set_error_handler(function ($errno, $errstr, $errfile, $errline) {});
ガン無視できます。なんの役にも立ちませんが。
E_USER_ERROR
も set_error_handler
で拾える? 最初から例外投げればいいと思うよ。
set_error_handler から例外を投げる場合、絶妙にニッチな問題もあるので微妙に注意かもしれません。