前置き
タイトルの通りです。
どうやら、メモリリーク(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でもメモリリークやタイムアウトは捕まえられないので、ほんと困ってましたが、やっとこれで解決。