Attraper en toute sécurité une erreur "Taille de mémoire autorisée épuisée" en PHP

J'ai un script de passerelle qui renvoie JSON au client. Dans le script, j'utilise set_error_handler pour attraper les erreurs et avoir toujours un retour formaté.

Il est soumis à des erreurs' taille de mémoire autorisée épuisée', mais plutôt que d'augmenter la limite de mémoire avec quelque chose comme ini_set ('memory_limit', '19T') , je veux juste retourner que l'utilisateur devrait essayer autre chose car il avait beaucoup de mémoire.

Y a-t-il de bons moyens d'attraper fatal les erreurs?

54
demandé sur Matt R. Wilson 2011-12-09 06:49:35

4 réponses

, Comme cette réponse suggère, vous pouvez utiliser register_shutdown_function() pour enregistrer un rappel que vais vérifier error_get_last().

Vous devrez toujours gérer la sortie générée à partir du code incriminé, que ce soit par @ (taisez-vous ) opérateur, ou 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';
}

Lors de l'exécution, cela génère Caught at shutdown. Malheureusement, l'objet d'exception ErrorException n'est pas lancé car l'erreur fatale déclenche la fin du script, par la suite interceptée uniquement dans la fonction d'arrêt.

Vous peut vérifier le tableau $error dans la fonction d'arrêt pour plus de détails sur la cause, et répondre en conséquence. Une suggestion pourrait être de réémettre la requête contre votre application web ( à une adresse différente, ou avec des paramètres différents bien sûr) et renvoyer la réponse capturée.

Je recommande de garder error_reporting() élevé (une valeur de -1) cependant, et à l'aide de (comme d'autres l'ont suggéré) gestion des erreurs pour tout le reste avec set_error_handler() et ErrorException.

42
répondu Dan Lugg 2017-05-23 12:25:52

Si vous devez exécuter du code métier lorsque cette erreur se produit (journalisation, sauvegarde du contexte pour les débogages futurs, emailing ou autre), l'enregistrement d'une fonction d'arrêt ne suffit pas: vous devez libérer de la mémoire d'une manière.

Une solution consiste à allouer de la mémoire d'urgence quelque part:

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;
}
25
répondu Alain Tiemblo 2014-12-20 16:05:03

Vous pouvez obtenir la taille de la mémoire déjà consommée par le processus en utilisant cette fonction memory_get_peak_usage les documentations sont à http://www.php.net/manual/en/function.memory-get-peak-usage.php je pense que ce serait plus facile si vous pouviez ajouter une condition pour rediriger ou arrêter le processus avant que la limite de mémoire ne soit presque atteinte par le processus. :)

6
répondu Christopher Pelayo 2011-12-09 02:59:05

Alors que la solution @ alain-tiemblo fonctionne parfaitement, j'ai mis ce script pour montrer comment vous pouvez réserver de la mémoire dans un script php, hors de portée de l'objet:

<?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);

Et la sortie serait:

Start: 349KB
After Reserving: 3426KB
After Unsetting: 349KB
After Reserving again: 3426KB
Start Shut Down: 3420KB
End Shut Down: 344KB
2
répondu HPM 2018-06-07 17:52:50