【PHP】フレームワーク抜きでPHP5.0~PHP8.1に対応する言語の警告やエラーを逃さない様にするコード

  • 2022年4月7日
  • 2022年4月7日
  • PHP

 PHP は1995年から始まり長らく多くの web サーバーで用いられています。現在は規模が大きくなった案件に対しても適切なプログラミングと運用の役に立つ型や例外といった機能が増えていますが、以前はなんやかんや動くための言語仕様が少なからずありました。そういった言語仕様の部分は今も残っており、厳格なプログラミングを行うためにはそういった部分もきっちりプログラムが異常終了するエラーにして、適宜問題があったことを通知したくあります。

 問題があるがプログラムの実行が続行される代表的な例は NOTICE、WARNING といったあまり深刻なレベルでない PHP のエラーです。これはを無視すると結果的に期待通りに動かないことはよくあっても言語レベルの実行を止めるほどではない、といったあたりのエラーです。PHP にはこれが発生次第、ユーザー定義で適宜処理するための関数が備わっています。それが次リンクにある set_error_handler です。

PHP: set_error_handler – Manual

 set_error_handler は PHP 内部でエラーが発生した場合、それに反応して処理を行う関数を定義する関数です。これを用いて次の様にエラーレベルに関わらずエラーが発生した場合、エラーメッセージを表示して処理が止まります。

<?php

function error_handler($errno, $errstr, $errfile, $errline)
{
    // エラーの種別を定数から文字列化するための対応をまとめた連想配列
    $errMap = [// match式は PHP8 から、[]による配列宣言構文は PHP5.4 から有効です
        E_ERROR             => 'ERROR',
        E_WARNING           => 'WARNING',
        E_NOTICE            => 'NOTICE',
        E_CORE_ERROR        => 'CORE_ERROR',
        E_CORE_WARNING      => 'CORE_WARNING',
        E_COMPILE_ERROR     => 'COMPILE_ERROR',
        E_COMPILE_WARNING   => 'COMPILE_WARNING',
        E_USER_ERROR        => 'USER_ERROR',
        E_USER_WARNING      => 'USER_WARNING',
        E_USER_NOTICE       => 'USER_NOTICE',
        E_STRICT            => 'STRICT',
        // E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR', // PHP5.2 から有効な定数です
        // E_DEPRECATED        => 'DEPRECATED',      // PHP5.3 から有効な定数です
        // E_USER_DEPRECATED   => 'USER_DEPRECATED', // PHP5.3 から有効な定数です
        E_ALL               => 'ALL',
    ];
    // 上記のエラー種別配列からエラーの種別を文字列化
    // null 合体演算子は PHP7.0 から有効です
    $errLevel = isset($errMap[$errno]) ? $errMap[$errno] : 'UNKNOWN No.'.$errno;

    // 例外を投げる前にログファイルに追記、メールを送る、Slack の webhook API を叩く等いろいろをここに書く

    // エラーの内容を元に例外を投げます
    if (version_compare(PHP_VERSION, '5.1.0') >= 0) {
        // @see https://www.php.net/manual/ja/class.errorexception.php PHP: ErrorException - Manual
        // @see https://qiita.com/mpyw/items/c69da9589e72ceac470c お前は PHP 7 における Fatal Error / Catchable Fatal Error / Error / ErrorException / Exception の違いを言えるか?
        // PHP のエラーを例外に変換する時のための組み込み例外クラスのインスタンスを投げる
        throw new ErrorException("{$errLevel}: {$errstr} {$errfile} {$errline}", 0, $errno, $errfile, $errline);
    } elseif (version_compare(PHP_VERSION, '5.0.0') >= 0) {
        // PHP5.1 より前は ErrorException クラスが存在しないので代わりに Exception クラスを使う
        throw new Exception("{$errLevel}: {$errstr} {$errfile} {$errline}", 0, $errno, $errfile, $errline);
    }
}
set_error_handler('error_handler'); // 無名関数によるコールバックは PHP5.3 から有効です

// 例
echo $a;
/*
Fatal error: Uncaught ErrorException: WARNING: Undefined variable $a /in/tGHtO 41 in /in/tGHtO:41
Stack trace:
#0 /in/tGHtO(41): error_handler(2, 'Undefined varia...', '/in/tGHtO', 41)
#1 {main}
  thrown in /in/tGHtO on line 41

Process exited with code 255.
 */

上記コード実行デモ

 これをエントリーポイントなりなんなりのスクリプト開始位置付近に置くことでエラーが起きていればレベルに関わらず全て致命的なエラーになります。これで実はまともに動いていないが緩いレベルのエラーしか出ておらず問題に気づけない、ということを減らせます。この強制処理中断ロジック例にログの追記、メール送信、チャットツールへの送信など追加することでエラーに即応できる様にもなります。
 また、より広汎にエラーと例外を取り扱う目的であれば以下の set_exception_handler や register_shutdown_function を加えて使うのがよいです。

PHP: set_exception_handler – Manual
PHP: register_shutdown_function – Manual

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG