【PHP8.x】set_exception_handler関数の使い方

作成日: 更新日:

set_exception_handler関数は、PHPアプリケーション内で処理されずに残ってしまった(未捕捉の)例外が発生した際に、開発者が事前に指定した特定の関数を自動的に実行するよう設定する関数です。この機能により、予期せぬエラーによってプログラムが突然停止してしまうことを防ぎ、システム全体の安定性を高めることができます。

通常、例外がスローされたにもかかわらず、その例外を捕捉するtry-catchブロックが存在しない場合、PHPはエラーとして処理を中断します。しかし、このset_exception_handler関数を用いてカスタム例外ハンドラを設定しておくと、未捕捉の例外が発生した瞬間に、登録された関数が呼び出されます。このハンドラ関数には、発生した例外の情報を格納したExceptionオブジェクトが引数として渡されるため、エラーの種類や発生箇所などを詳細に把握し、適切な処理を行うことが可能です。

例えば、ウェブアプリケーションにおいては、ユーザーに技術的なエラーメッセージではなく、「現在、システムに問題が発生しています」といった分かりやすいメッセージを表示したり、エラー情報をログファイルに記録して後で分析できるようにしたりする用途で利用されます。これにより、ユーザー体験を向上させるとともに、システム管理者が問題を迅速に特定し、解決するための手助けとなります。また、この関数は、以前に設定されていた例外ハンドラを戻り値として返すため、必要に応じて一時的にハンドラを変更し、処理後に元の状態に戻すといった柔軟な運用も可能です。システムの信頼性を向上させるために欠かせない機能の一つです。

基本的な使い方

構文(syntax)

<?php

set_exception_handler(function (Throwable $exception) {
    // 例外処理
});

引数(parameters)

?callable $callback

  • ?callable $callback: 例外が発生した際に呼び出されるコールバック関数またはメソッド。nullを指定すると、デフォルトの例外ハンドラが設定されます。

戻り値(return)

callable|null

set_exception_handler関数は、例外処理のコールバック関数を登録します。この関数の戻り値は、以前に登録されていた例外処理コールバック関数、またはコールバック関数が登録されていなかった場合はNULLです。

サンプルコード

PHP set_exception_handler で例外を捕捉する

<?php

/**
 * カスタムの例外ハンドラを提供するクラス。
 * 未捕獲の例外が発生した際に、このクラスの静的メソッドが呼び出されます。
 */
class CustomExceptionHandler
{
    /**
     * 未捕獲の例外を処理する静的メソッド。
     *
     * このメソッドは、PHPスクリプト内で `try...catch` ブロックで捕捉されなかった
     * 例外が発生したときに `set_exception_handler` によって呼び出されます。
     *
     * @param Throwable $exception 捕捉された例外オブジェクト。
     * @return void
     */
    public static function handle(Throwable $exception): void
    {
        // ここで例外の情報をログに記録したり、ユーザーにエラーページを表示したりします。
        // システムエンジニアとしては、本番環境では詳細なエラーメッセージを直接表示せず、
        // ログに記録するだけに留めるのが一般的です。
        error_log(
            "Unhandled Exception: " . $exception->getMessage() .
            " in " . $exception->getFile() .
            " on line " . $exception->getLine() .
            "\nStack trace:\n" . $exception->getTraceAsString()
        );

        // 開発環境では、デバッグのためにエラー情報を出力しても良いでしょう。
        echo "<h1>システムエラーが発生しました</h1>";
        echo "<p>申し訳ありませんが、予期せぬエラーが発生しました。</p>";
        echo "<p>管理者によってログに記録されました。</p>";

        // デバッグ情報 (本番環境では非表示にすべき)
        echo "<pre>";
        echo "<strong>Exception Message:</strong> " . htmlspecialchars($exception->getMessage()) . "\n";
        echo "<strong>File:</strong> " . htmlspecialchars($exception->getFile()) . "\n";
        echo "<strong>Line:</strong> " . htmlspecialchars($exception->getLine()) . "\n";
        echo "<strong>Stack Trace:</strong>\n" . htmlspecialchars($exception->getTraceAsString()) . "\n";
        echo "</pre>";

        // HTTPステータスコードを500 (Internal Server Error) に設定
        http_response_code(500);

        // 例外ハンドラが処理を終えたら、通常はスクリプトの実行を終了します。
        exit(1);
    }
}

// グローバルな未捕獲例外ハンドラを設定します。
// ここでは、CustomExceptionHandlerクラスの静的メソッド 'handle' を指定しています。
set_exception_handler(['CustomExceptionHandler', 'handle']);

echo "スクリプトが開始されました。\n";

// 意図的に例外を発生させて、ハンドラが動作することを確認します。
// この例外は `try...catch` ブロックで捕捉されていないため、
// 上記で設定した `CustomExceptionHandler::handle()` が呼び出されます。
throw new Exception("これはテストのために意図的に発生させた例外です。");

// 上記の例外が発生し、ハンドラが `exit(1)` を呼び出すため、
// この行以降のコードは実行されません。
echo "このメッセージは表示されません。\n";

?>

PHPのset_exception_handler関数は、スクリプト内でtry...catchブロックによって捕捉されなかった(未捕捉の)例外が発生した際に、その例外を処理するためのカスタム関数やメソッドを設定します。

引数?callable $callbackには、例外を処理する関数またはクラスの静的メソッドを指定します。サンプルコードでは、CustomExceptionHandlerクラスの静的メソッドhandleが設定されています。このメソッドは、Throwable型の例外オブジェクトを受け取り、未捕捉の例外の詳細情報を取得して処理します。戻り値は、以前に設定されていた例外ハンドラ(存在すれば)を返します。これにより、必要に応じて元のハンドラに戻すことも可能です。

サンプルコードでは、set_exception_handlerによりCustomExceptionHandler::handleメソッドがグローバルな例外ハンドラとして登録されています。意図的に例外を発生させると、このhandleメソッドが呼び出され、例外の詳細がシステムログに記録されます。同時に、ユーザーには一般的なエラーメッセージを表示し、HTTPステータスコードを500(Internal Server Error)に設定した後、スクリプトを終了させます。システムエンジニアとしては、このように本番環境でユーザーに詳細なエラーメッセージを直接表示せず、ログに記録して対応することが重要です。これにより、予期せぬエラー発生時でも、安全にシステムを停止し、エラー情報を収集する仕組みを構築できます。

set_exception_handlerは、try...catchで捕捉されなかった例外のみを処理するグローバルなハンドラです。ハンドラ内でexit()を呼び出すと、それ以降のスクリプトは実行されません。本番環境では、詳細なエラーメッセージをユーザーに直接表示せず、error_logなどを使ってログに記録するだけに留めてください。これは、セキュリティ上のリスク(機密情報漏洩や攻撃のヒントとなる情報提供)を避けるため非常に重要です。開発時にはデバッグのために詳細を表示しても良いですが、必ず本番環境のリリース前に適切な処理に切り替える必要があります。このハンドラはアプリケーション全体に影響するため、慎重に設計してください。

PHPでset_exception_handlerが効かない原因を解説する

<?php

/**
 * Custom global exception handler.
 *
 * This function is registered using `set_exception_handler()` and will be
 * called for any uncaught exceptions in the script.
 * If an exception is caught by a local `try-catch` block, this global handler
 * will NOT be triggered for that specific exception, which is a common reason
 * why `set_exception_handler` might appear "not working".
 *
 * @param Throwable $exception The uncaught exception object.
 */
function myGlobalExceptionHandler(Throwable $exception): void
{
    echo "--- GLOBAL EXCEPTION HANDLER ACTIVATED ---\n";
    echo "An uncaught exception occurred:\n";
    echo "Type: " . get_class($exception) . "\n";
    echo "Message: " . $exception->getMessage() . "\n";
    echo "File: " . $exception->getFile() . "\n";
    echo "Line: " . $exception->getLine() . "\n";
    // In a real application, you would typically:
    // 1. Log the exception details for debugging.
    // 2. Optionally send an alert to developers.
    // 3. Display a user-friendly error page (e.g., "Something went wrong").
    // It's common practice to terminate the script after an uncaught exception.
    exit(1);
}

// Register the custom global exception handler.
// This must be called before any uncaught exceptions are thrown for it to take effect.
set_exception_handler('myGlobalExceptionHandler');

echo "--- Scenario 1: Exception caught by a local try-catch block ---\n";
echo "The global exception handler will NOT be triggered here because the exception is handled locally.\n\n";

try {
    echo "Attempting to throw an exception inside a try-catch block...\n";
    throw new InvalidArgumentException("This is a locally caught exception.");
    // This line will not be executed because the exception is thrown above it.
} catch (InvalidArgumentException $e) {
    echo "--> Successfully caught by local try-catch block!\n";
    echo "    Exception Type: " . get_class($e) . "\n";
    echo "    Message: " . $e->getMessage() . "\n";
    echo "Local handling complete. Script continues as normal.\n\n";
    // The global handler was NOT called because this exception was handled within this block.
}

echo "--- Scenario 2: Uncaught exception ---\n";
echo "The global exception handler WILL be triggered in this scenario.\n";
echo "This will be the last output before the global handler takes over and exits the script.\n\n";

// This exception is NOT wrapped in a try-catch block.
// Therefore, it is "uncaught" and will be passed to the global exception handler
// that we registered with `set_exception_handler()`.
throw new RuntimeException("This is an uncaught exception that will trigger the global handler.");

echo "This line will NOT be executed because the script will exit via the global handler above.\n";

set_exception_handler関数は、PHPスクリプト内でtry-catchブロックによって捕捉されなかった「未処理(uncaught)」の例外をグローバルに処理するためのコールバック関数を設定します。引数$callbackには、未処理の例外が発生した際に呼び出される関数を指定し、この関数はThrowable型の例外オブジェクトを受け取ります。戻り値は、以前に設定されていたハンドラ関数、または何も設定されていなかった場合はnullです。

サンプルコードでは、myGlobalExceptionHandler関数をグローバルハンドラとして登録しています。このハンドラは、未処理の例外が発生した場合に、その種類、メッセージ、発生ファイルと行番号を出力し、通常はスクリプトを終了させます。

シナリオ1では、例外がtry-catchブロックによって捕捉されるため、その例外はローカルで処理され、グローバルハンドラは呼び出されません。これは、「set_exception_handlerが動作しない」と誤解される主な理由の一つです。 一方、シナリオ2では、例外がtry-catchブロックで囲まれていないため未処理となり、登録されたグローバルハンドラであるmyGlobalExceptionHandlerが呼び出されます。これによりスクリプトは中断し、グローバルハンドラが実行されてから終了します。このことから、グローバルハンドラは未処理の例外に対してのみ機能することが理解できます。

set_exception_handlerは、try-catchブロックで捕捉されなかった「未処理の例外」のみを処理します。ローカルで捕捉された例外はグローバルハンドラの対象外となるため、ハンドラが「機能していない」と感じる主な原因はここにあります。このハンドラは、例外が発生する前に登録しておく必要があります。グローバル例外ハンドラが呼び出された際は、例外の詳細を適切にログに記録し、その後のスクリプトの動作に影響を及ぼさないよう、安全に終了させることが非常に重要です。実際のアプリケーションでは、ユーザーには一般的なエラーメッセージを表示し、開発者には詳細なエラー通知を行うなどの運用を検討してください。

PHP 8: set_exception_handlerで例外を捕捉する

<?php

/**
 * カスタム例外ハンドラ関数。
 * この関数は、PHPスクリプト内でキャッチされない例外が発生した場合に呼び出されます。
 *
 * @param Throwable $exception 発生した例外オブジェクト
 * @return void
 */
function myCustomExceptionHandler(Throwable $exception): void
{
    // 例外発生のメッセージと詳細情報を出力
    echo "--- カスタム例外ハンドラが起動しました ---" . PHP_EOL;
    echo "例外の種類: " . get_class($exception) . PHP_EOL;
    echo "メッセージ: " . $exception->getMessage() . PHP_EOL;
    echo "ファイル: " . $exception->getFile() . PHP_EOL;
    echo "行番号: " . $exception->getLine() . PHP_EOL;
    echo "--- 例外処理を終了します ---" . PHP_EOL;

    // 通常、未処理の例外が発生した場合はスクリプトを終了します。
    // ここでエラーログへの記録やユーザーへのエラーページ表示などを行います。
    exit(1);
}

// set_exception_handler 関数を使って、カスタム例外ハンドラを登録します。
// これにより、この関数が実行された後、PHPスクリプト内で
// try-catch ブロックで捕捉されない例外が発生した場合、
// myCustomExceptionHandler 関数が自動的に呼び出されるようになります。
set_exception_handler('myCustomExceptionHandler');

echo "スクリプトが開始されました。" . PHP_EOL;

// 以下で意図的に例外をスローし、try-catch で捕捉しないことで、
// 上で登録したカスタム例外ハンドラが呼び出されることを確認します。
echo "これから未捕捉の例外を発生させます..." . PHP_EOL;
throw new Exception("これは意図的に発生させた未捕捉の例外です!");

// 例外ハンドラ内でスクリプトが終了するため、この行は実行されません。
echo "この行は実行されません。" . PHP_EOL;

?>

PHPのset_exception_handler関数は、PHPスクリプト内でtry-catchブロックで捕捉されない例外が発生した場合に、自動的に実行される独自の処理(カスタム例外ハンドラ)を登録する機能です。このサンプルコードでは、まずmyCustomExceptionHandlerという関数を定義しています。この関数は、発生した例外の情報を画面に表示し、スクリプトを終了させる役割を持ちます。

set_exception_handler('myCustomExceptionHandler');の行で、このmyCustomExceptionHandler関数をカスタム例外ハンドラとして登録しています。これにより、この行以降で意図的にスローされた未捕捉の例外が発生すると、PHPはスクリプトの通常の実行を中断し、登録されたmyCustomExceptionHandler関数を呼び出します。その結果、例外の種類やメッセージ、発生場所などが表示され、exit(1)によってスクリプトが終了します。

引数?callable $callbackには、登録したいカスタム例外ハンドラ関数を指定します。nullを指定するとハンドラを解除します。戻り値は、以前に登録されていた例外ハンドラ(存在しない場合はnull)を返します。この関数を使うことで、スクリプト全体の例外処理を一元的に管理し、予期せぬエラー発生時でも安定した動作をさせることができます。

set_exception_handlerは、try-catchブロックで捕捉されない例外が発生した際の最終的な処理を定義するものです。カスタム例外ハンドラ関数内では、例外を適切に処理した後、通常はexit()などでスクリプトを終了させることが重要です。これを怠ると、PHPのデフォルトのエラー処理が継続され、意図しない動作につながる可能性があります。また、本番環境では、例外の詳細な情報を直接ユーザーに表示せず、セキュリティ上の理由からエラーログに記録するようにしてください。この関数を再度呼び出すと、以前に登録されたハンドラは新しいもので上書きされますのでご注意ください。

【PHP8.x】set_exception_handler関数の使い方 | いっしー@Webエンジニア