【PHP】メモリリークやタイムアウトは例外処理では拾えない!じゃあどうする?

PHP
スポンサーリンク

前置き

タイトルの通りです。
どうやら、メモリリーク(memory_limit)やタイムアウト(max_execution_time)の類のエラーはFatalエラーとなるため、try~catchの例外で拾うことができないようです。
Fatalエラーでスクリプトが終了してしまうっぽい。

ではどうやってエラーハンドリングをするか。

結論、PHP側で用意されている「register_shutdown_function」を使えば、
スクリプト終了時に発生したエラーを検出することができるので、エラーハンドリングできるとのこと。

register_shutdown_functionを使う

以下、サンプルコードです。

<?php
// 実装コードの前にregister_shutdown_functionを定義
register_shutdown_function(function() {
    $error = error_get_last();
    if ($error !== NULL) {
        if (strpos($error['message'], 'Allowed memory size') !== false) {
            // メモリリークが発生した場合
            error_log("メモリリークの発生 error-message: " . $error['message']);
            header("Location:https://サイト名.com/メモリリーク発生ページのリンク");
            exit();
        }
        if (strpos($error['message'], 'Maximum execution time') !== false) {
            // タイムアウトが発生した場合
            error_log("タイムアウトの発生 error-message: " . $error['message']);
            header("Location:https://サイト名.com/タイムアウト発生ページのリンク");
            exit();
        }
    }
});


// わざとFatalエラーを発生させる
ini_set("max_execution_time", 2);
ini_set('memory_limit', '2M');
// 大きな配列を生成してメモリを大量に消費
$data = [];
for ($i = 0; $i < 1000000; $i++) {
    $data[] = str_repeat('a', 1024 * 1024);
}

// 本来実装すべきコードを以下に書く
var_dump("このコードの場合、ここに到達せず、register_shutdown_functionがコールされます");
exit();

上記のサンプルはregister_shutdown_functionを匿名関数(クロージャ)で実装してみました。
巷のサンプル集では以下のような形が多いですが匿名関数(クロージャ)でも実装できそうです。

<?php
function shutdown()
{
    // ここにシャットダウンした時の処理を書く(ログをだしたり、エラーページへリダイレクトしたり。)
    echo 'Script executed with success', PHP_EOL;
}

register_shutdown_function('shutdown');

register_shutdown_functionについての参考リンク

PHP: register_shutdown_function - Manual

try~catchのThrowableでもメモリリークやタイムアウトは捕まえられないので、ほんと困ってましたが、やっとこれで解決。

タイトルとURLをコピーしました