読者です 読者をやめる 読者になる 読者になる

PHP の E_RECOVERABLE_ERROR エラーレベル

PHP

PHP Advent Calendar 2013 in Adventar の24日目です。


PHPset_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_ERRORset_error_handler で拾える? 最初から例外投げればいいと思うよ。


set_error_handler から例外を投げる場合、絶妙にニッチな問題もあるので微妙に注意かもしれません。