Безопасное обнаружение ошибки "Разрешенная память"
У меня есть шлюз script, который возвращает JSON обратно клиенту.
В script я использую set_error_handler, чтобы ловить ошибки и все еще иметь отформатированный возврат.
Он подвержен ошибкам "Разрешенные размеры памяти", а не увеличивает ограничение памяти с помощью ini_set ('memory_limit', '19T')., я просто хочу вернуть, что пользователь должен попробовать что-то еще, потому что он использовал много памяти.
Есть ли хорошие способы для обнаружения фатальных ошибок?
Ответы
Ответ 1
Как этот ответ, вы можете использовать register_shutdown_function()
для регистрации обратного вызова, который будет проверять error_get_last()
.
Вам все равно придется управлять выводами, генерируемыми из-за нарушения кода, независимо от оператора @
(shut up), или 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
не генерируется, потому что фатальная ошибка запускает завершение script, впоследствии попадая только в функцию выключения.
Вы можете проверить массив $error
в функции выключения для получения подробной информации о причине и ответить соответствующим образом. Одно предложение могло бы повторно отослать запрос обратно на ваше веб-приложение (на другом адресе или с разными параметрами) и вернуть захваченный ответ.
Я рекомендую сохранить error_reporting()
высокий (значение -1
) и использовать (как другие предложили) обработку ошибок для всего остального с помощью set_error_handler()
и ErrorException
.
Ответ 2
Если вам необходимо выполнить бизнес-код при возникновении этой ошибки (ведение журнала, резервное копирование контекста для будущих отладок, отправка по электронной почте или тому подобное), регистрации функции выключения недостаточно: вы должны освободить память в некотором роде.
Одним из решений является выделение какой-либо аварийной памяти где-то:
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;
}
Ответ 3
с помощью этой функции вы можете получить размер памяти, уже занятой процессом, с документацией memory_get_peak_usage. Документация http://www.php.net/manual/en/function.memory-get-peak-usage.php. Я думаю, было бы проще, если бы вы могли добавить условие для перенаправления или остановки процесса до того, как предел памяти будет почти достигнут процесс. :)
Ответ 4
Хотя решение @alain-tiemblo работает отлично, я поставил этот сценарий, чтобы показать, как можно зарезервировать часть памяти в сценарии php вне области объекта.
Короткая версия
// memory is an object and it is passed by reference
function shutdown($memory) {
// unsetting $memory does not free up memory
// I also tried unsetting a global variable which did not free up the memory
unset($memory->reserve);
}
$memory = new stdClass();
// reserve 3 mega bytes
$memory->reserve = str_repeat('❤', 1024 * 1024);
register_shutdown_function('shutdown', $memory);
Полный образец скрипта
<?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