Безопасно поймать ошибку "допустимый размер памяти исчерпан" в PHP
У меня есть скрипт шлюза, который возвращает JSON обратно клиенту. В скрипте я использую set_error_handler чтобы поймать ошибки и все еще иметь форматированный возврат.
он подвержен ошибкам "разрешенный размер памяти исчерпан", но вместо того, чтобы увеличить лимит памяти с чем-то вроде ini_set ('memory_limit','19T'), Я просто хочу вернуть, что пользователь должен попробовать что-то еще, потому что он использовал много памяти.
есть ли хорошие способы чтобы поймать фатальные ошибки?
4 ответа:
как ответ предполагает, что вы можете использовать
register_shutdown_function()
чтобы зарегистрировать обратный вызов, который будет проверятьerror_get_last()
.вам все равно придется управлять выводом, сгенерированным из оскорбительного кода, будь то
@
(заткнись оператор), илиini_set('display_errors', false)
ini_set('display_errors', false); error_reporting(-1); set_error_handler(function($code, $string, $file, $line){ throw new ErrorException($string, null, $code, $file, $line); }); register_shutdown_function(function(){ $error = error_get_last(); if(null !== $error) { echo 'Caught at shutdown'; } }); try { while(true) { $data .= str_repeat('#', PHP_INT_MAX); } } catch(\Exception $exception) { echo 'Caught in try/catch'; }
при запуске выводит
Caught at shutdown
. К сожалению,ErrorException
объект исключения не выбрасывается, потому что неустранимая ошибка вызывает завершение сценария, впоследствии пойман только в функции выключения.вы можете проверить
$error
массив в функции выключения для деталей на причине, и отвечает соответственно. Одним из предложений может быть переиздание запроса обратно в ваше веб-приложение (по другому адресу, или с другими параметрами конечно) и вернуть захваченный ответ.я рекомендую держать
error_reporting()
высокий (значение-1
) хотя, и используя (как и другие предложил) обработка ошибок для всего остального сset_error_handler()
иErrorException
.
Если вам нужно выполнить бизнес-код, когда происходит эта ошибка (ведение журнала, резервное копирование контекста для будущих отладок, отправка по электронной почте или т. д.), регистрации функции выключения недостаточно: вы должны освободить память в некотором роде.
одним из решений является выделение некоторой аварийной памяти где-то:
public function initErrorHandler() { // This storage is freed on error (case of allowed memory exhausted) $this->memory = str_repeat('*', 1024 * 1024); register_shutdown_function(function() { $this->memory = null; if ((!is_null($err = error_get_last())) && (!in_array($err['type'], array (E_NOTICE, E_WARNING)))) { // $this->emergencyMethod($err); } }); return $this; }
вы можете получить размер памяти, уже потребленной процессом, используя эту функцию memory_get_peak_usage documentations are at http://www.php.net/manual/en/function.memory-get-peak-usage.php я думаю, что было бы проще, если бы вы могли добавить условие для перенаправления или остановки процесса до того, как предел памяти будет почти достигнут процессом. :)
в то время как решение @alain-tiemblo работает отлично, я поставил этот скрипт, чтобы показать, как вы можете зарезервировать некоторую память в PHP-скрипте, вне области объекта:
<?php function getMemory(){ return ((int) (memory_get_usage() / 1024)) . 'KB'; } // memory is an object and it is passed by reference function shutdown($memory) { echo 'Start Shut Down: ' . getMemory() . PHP_EOL; // unsetting $memory does not free up memory // I also tried unsetting a global variable which did not free up the memory unset($memory->reserve); echo 'End Shut Down: ' . getMemory() . PHP_EOL; } echo 'Start: ' . getMemory() . PHP_EOL; $memory = new stdClass(); // reserve 3 mega bytes $memory->reserve = str_repeat('❤', 1024 * 1024); echo 'After Reserving: ' . getMemory() . PHP_EOL; unset($memory); echo 'After Unsetting: ' . getMemory() . PHP_EOL; $memory = new stdClass(); // reserve 3 mega bytes $memory->reserve = str_repeat('❤', 1024 * 1024); echo 'After Reserving again: ' . getMemory() . PHP_EOL; // passing $memory object to shut down function register_shutdown_function('shutdown', $memory);
и выход будет такой:
Start: 349KB After Reserving: 3426KB After Unsetting: 349KB After Reserving again: 3426KB Start Shut Down: 3420KB End Shut Down: 344KB