PHP の「エラー処理ハンドラ」「シャットダウンハンドラ」「例外処理ハンドラ」の挙動

エラーレポートレベルの指定

PHP には様々なレベルのエラーが定義されています。

定数
1E_ERROR
2E_WARNING
4E_PARSE
8E_NOTICE
16E_CORE_ERROR
32E_CORE_WARNING
64E_COMPILE_ERROR
128E_COMPILE_WARNING
256E_USER_ERROR
512E_USER_WARNING
1024E_USER_NOTICE
2048E_STRICT
4096E_RECOVERABLE_ERROR
8192E_DEPRECATED
16384E_USER_DEPRECATED
(PHP バージョンに依存)E_ALL

error_reporting 関数に上記の定数を渡すことで、どのレベルのエラーをレポートすべきか PHP に指示することができます。

全てのエラーをレポートするには、E_ALL を指定します。PHP4 と PHP5 では既定では E_ALL & ~E_NOTICE が指定されます。

エラーハンドラの指定

エラーハンドラは、set_error_handler 関数で指定します。

次の例では $i = 5/0; の行で 0 除算の警告が発生しますが、その箇所でユーザー定義の エラーハンドラである my_error_handler 関数が呼ばれます。

<?php
set_error_handler( 'my_error_handler', E_ALL ); 
$i = 5/0;

function my_error_handler ( $errno, $errstr, $errfile, $errline, $errcontext ) {
     echo "[$errno] $errstr $errfile($errline)\n";
}
?>
> php error2.php
[2] Division by zero C:\Temp\error2.php(3)

>

Fatal Error の処理 ~ スクリプトのシャットダウン関数を利用

ただし、次のタイプのエラーはこのユーザー定義のエラーハンドラで処理できない、と PHP のサイトに書いてあります。

  • E_ERROR
  • E_PARSE
  • E_CORE_ERROR
  • E_CORE_WARNING
  • E_COMPILE_ERROR
  • E_COMPILE_WARNING

試しに、次のように未定義の関数 foo() を呼び出してみます。

<?php
set_error_handler( 'my_error_handler', E_ALL );

$i = 5/0;
foo();

function my_error_handler ( $errno, $errstr, $errfile, $errline, $errcontext ) {
     echo "[$errno] $errstr $errfile($errline)\n";
}
?>

すると、次のように警告はユーザー定義のエラーハンドラで処理されていることがわかりますが、 肝心の Fatal Error が処理されません。

> php error2.php
[2] Division by zero C:\Temp\error2.php(4)

Fatal error: Call to undefined function foo() in C:\Temp\error2.php on line 5

>

これは Fatal error (致命的エラー) の箇所で、既にスクリプトの修了処理が始まってしまい、ユーザー定義のエラーハンドラ関数が呼び出されないため、 と考えられます。

そこで、上記のようなエラーハンドラ関数で処理できないエラーについては、 スクリプトのシャットダウン関数を利用して処理します。 シャットダウン関数は register_shutdown_function 関数で指定します。

<?php
ini_set( 'display_errors' , 0 );
error_reporting( E_ALL );
set_error_handler( 'my_error_handler', E_ALL );
register_shutdown_function( 'my_shutdown_handler' );

echo "Hello\n";
$i = 5/0;
foo();
echo "Bye\n";


function my_error_handler ( $errno, $errstr, $errfile, $errline, $errcontext ) {
     echo "[$errno] $errstr $errfile($errline)\n";
}

function my_shutdown_handler(){
     $isError = false;
     if ($error = error_get_last()){
          switch($error['type']){
          case E_ERROR:
          case E_PARSE:
          case E_CORE_ERROR:
          case E_CORE_WARNING:
          case E_COMPILE_ERROR:
          case E_COMPILE_WARNING:
               $isError = true;
                      break;
              }
         }
     if ($isError){
          echo my_error_handler( 
               $error['type'], 
               $error['message'], 
               $error['file'], 
               $error['line'], 
               null );
     }
}
?>

この実行結果は次のようになります。

> php error3.php
Hello
[2] Division by zero C:\Temp\error3.php(10)
[1] Call to undefined function foo() C:\Temp\error3.php(11)

"Bye" という文字を出力する行へ復帰は出来ませんが、シャットダウン関数からエラーハンドラを呼びだせています。

致命的なエラーだが、復帰できる場合

それでは Fatal エラーは常に復帰できないか、というと実はそうでもなく、try-catch で例外をキャッチすることで、 プログラムの実行を継続することが可能な場合もあります。

次の例を見てください。

<?php
set_error_handler( 'my_error_handler', E_ALL );

echo "Hello!\n";
$x = new COM('AAA.BBB');
echo "Bye!\n";

function my_error_handler ( $errno, $errstr, $errfile, $errline, $errcontext ) {
     echo "[$errno] $errstr $errfile($errline)\n";
}
?>

このコードでは new COM('AAA.BBB') という箇所で、デタラメなプログラム識別子を渡して処理を失敗させています。

これを実行すると、次の結果となります。

>php error4.php
Hello!

Fatal error: Uncaught exception 'com_exception' with message 
'Failed to create COM object `AAA.BBB': Invalid syntax' in C:\Temp\error4.php:5
Stack trace:
#0 C:\Temp\error4.php(5): com->com('AAA.BBB')
#1 {main}
  thrown in C:\Temp\error4.php on line 5

エラーハンドラをセットしているにもかかわらず、エラーハンドラに入りません。

エラーメッセージを見ると、例外が発生していることがわかりますので、 次のように try-catch で例外を捕捉します。

<?php
set_error_handler( 'my_error_handler', E_ALL );

echo "Hello!\n";

try {
     $x = new COM('AAA.BBB');
}
catch( Exception $e ) {
     echo $e->getMessage();
}

echo "Bye!\n";

function my_error_handler ( $errno, $errstr, $errfile, $errline, $errcontext ) {
     echo "[$errno] $errstr $errfile($errline)\n";
}
?>

これによって、次のような実行結果を得ました。

>php error4.php
Hello!
Failed to create COM object `AAA.BBB': Invalid syntax
Bye!

確かに Bye! という文字が表示されており、スクリプトの処理が継続されていることがわかります。

エラーハンドラと同様な構文の set_exception_handler 関数がありますので、これが利用できないか検討するため、 set_exception_handler 関数でデフォルトの例外処理ハンドラをセットすると次のようになります。

<?php
set_error_handler( 'my_error_handler', E_ALL );
set_exception_handler( 'my_exception_handler' );

echo "Hello!\n";
$x = new COM('AAA.BBB');
echo "Bye!\n";

function my_error_handler ( $errno, $errstr, $errfile, $errline, $errcontext ) {
     echo "[$errno] $errstr $errfile($errline)\n";
}

function my_exception_handler ( $e ) {
     echo $e->getMessage() . ' ' . $e->getFile() . '(' . $e->getLine() . ")\n";
}
?>

実行結果は次の通りです。

> php error4.php
Hello!
Failed to create COM object `AAA.BBB': Invalid syntax
 C:\Temp\error4.php(6)

確かに例外処理ハンドラが呼び出されていることはわかりますが、処理は元の場所へ復帰するわけではなく、 そこで終了します。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説