unlimited - Captura de forma segura un error de ''Tamaño de memoria permitido agotado'' en PHP
php memory_limit recommended value (4)
Tengo un script de puerta de enlace que devuelve JSON al cliente. En el script utilizo set_error_handler para detectar errores y todavía tengo un retorno formateado.
Está sujeto a errores de ''Capacidad de memoria agotada'', pero en lugar de aumentar el límite de memoria con algo como ini_set (''memory_limit'', ''19T'') , solo quiero devolver que el usuario debería probar algo más porque solía usar mucho memoria.
¿Hay alguna buena forma de detectar errores fatales?
Si bien la solución @ alain-tiemblo funciona a la perfección, puse este script para mostrar cómo se puede reservar memoria en un script php, fuera del alcance del objeto:
<?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);
Y la salida sería:
Start: 349KB
After Reserving: 3426KB
After Unsetting: 349KB
After Reserving again: 3426KB
Start Shut Down: 3420KB
End Shut Down: 344KB
Si necesita ejecutar el código comercial cuando ocurre este error (registro, copia de seguridad del contexto para futuros errores, correos electrónicos u otros), registrar una función de apagado no es suficiente: debe liberar memoria de alguna manera.
Una solución es asignar memoria de emergencia en alguna parte:
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;
}
podría obtener el tamaño de la memoria que ya consumió el proceso al usar esta función memory_get_peak_usage documentations en http://www.php.net/manual/en/function.memory-get-peak-usage.php Creo que sería será más fácil si puede agregar una condición para redirigir o detener el proceso antes de que el proceso alcance el límite de memoria. :)
Como sugiere esta respuesta , puede usar register_shutdown_function()
para registrar una devolución de llamada que verificará error_get_last()
.
Aún tendrá que administrar la salida generada por el código ofensivo, ya sea por el operador @
( shut up ), o 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'';
}
Cuando se ejecuta, esta salida es Caught at shutdown
. Desafortunadamente, el objeto de excepción ErrorException
no se lanza porque el error fatal desencadena la terminación del script, atrapado posteriormente solo en la función de apagado.
Puede verificar la matriz $error
en la función de apagado para obtener detalles sobre la causa y responder en consecuencia. Una sugerencia podría ser volver a enviar la solicitud a su aplicación web ( en una dirección diferente, o con diferentes parámetros, por supuesto ) y devolver la respuesta capturada.
Sin embargo, recomiendo mantener error_reporting()
alto ( un valor de -1
) y usar ( como otros han sugerido ) el manejo de errores para todo lo demás con set_error_handler()
y ErrorException
.