web-dev-qa-db-fra.com

Comment intercepter l'erreur fatale: le temps d'exécution maximum de 30 secondes a été dépassé en PHP

J'ai joué avec un système que je suis en train de développer et j'ai réussi à le provoquer:

Erreur fatale: dépassement du temps d'exécution maximal de 30 secondes

Cela s'est produit lorsque je faisais quelque chose d'irréaliste, mais cela peut néanmoins arriver avec un utilisateur.

Est-ce que quelqu'un sait s'il existe un moyen d'attraper cette exception? J'ai lu, mais tout le monde semble suggérer d'augmenter le temps alloué.

54
ingh.am

Vos seules options sont d'augmenter le temps d'exécution autorisé (le mettre à 0 le rend infini, mais ce n'est pas recommandé) du script ou générer un nouveau thread et espérer le meilleur.

La raison pour laquelle ce n'est pas capturable est qu'il n'est pas vraiment jeté. Aucune ligne du code n'a réellement déclenché l'erreur, plutôt PHP dit: "Non, désolé, c'est trop long. Il est temps de fermer maintenant." Et cela a du sens. Imaginez avoir un script avec un temps d'exécution maximal de 30 secondes pour détecter cette erreur et prendre 30 secondes supplémentaires ... dans un programme mal conçu, qui ouvre des opportunités plutôt désagréables à exploiter. Au minimum, cela créera des opportunités pour DOS attaques.

26
cwallenpoole

Que diriez-vous d'essayer en tant que PHP (enfin ... au moins un de ses lecteurs), dites:

<?php 
function shutdown() 
 { 
     $a=error_get_last(); 
     if($a==null)   
         echo "No errors"; 
     else 
          print_r($a); 

 } 
register_shutdown_function('shutdown'); 
ini_set('max_execution_time',1 ); 
sleep(3); 
?>

Jetez un œil aux liens suivants:

  1. http://www.php.net/manual/en/function.set-error-handler.php#106061
  2. http://www.php.net/manual/en/function.register-shutdown-function.php
72
TheJanOnline

Ce n'est pas une exception, c'est une erreur. Il existe des différences importantes entre les exceptions et les erreurs, les erreurs d'abord et avant tout ne peuvent pas être détectées avec la sémantique try/catch.

Les scripts PHP sont construits autour d'un paradigme de temps d'exécution courts, donc PHP est configuré par défaut pour supposer que si un script fonctionne depuis plus de 30 secondes, il doit être pris dans une boucle infinie et il faut donc y mettre fin afin d'éviter qu'un script PHP errant provoquant un déni de service, soit par accident, soit par intention malveillante).

Cependant, les scripts nécessitent parfois plus de temps d'exécution qu'ils ne sont alloués par défaut.

Vous pouvez essayer de modifier le temps d'exécution maximal, soit en utilisant set_time_limit(), soit en modifiant la valeur de max_execution_time dans le fichier php.ini Pour augmenter la limite. vous pouvez également supprimer complètement la limite en définissant le temps d'exécution sur 0, bien que cela ne soit pas recommandé.

set_time_limit() peut être désactivé par des mécanismes tels que disable_functions afin qu'il ne soit pas disponible pour vous, de même vous pourriez ne pas avoir accès à php.ini. Si les deux sont le cas, vous devez contacter votre hôte pour obtenir de l'aide.

Une exception est PHP scripts exécutés à partir de la ligne de commande . Dans ces conditions de fonctionnement, PHP les scripts peuvent être interactifs et doivent passer beaucoup de temps à traiter des données ou à attendre leur entrée. Pour cette raison, il n'y a pas de limite max_execution_time sur les scripts exécutés à partir de la ligne de commande par défaut.


MODIFIER POUR AJOUTER : PHP 7 a fait l'objet d'une refonte majeure. Je pense que les erreurs et les exceptions sont désormais des sous-classes de Throwable. Cela peut rendre les éléments ci-dessus plus pertinents pour PHP7 +, bien que je doive examiner de plus près les détails du fonctionnement de la gestion des erreurs maintenant pour en être sûr.

18
GordonM

Vous ne pouvez rien y faire. mais vous pouvez avoir un arrêt gracieux en utilisant register_shutdown_function

<?php 

ini_set('display_errors', '0');         
ini_set("max_execution_time",15 ); //you can use this if you know your script should not take longer than 15 seconds to finish

register_shutdown_function('shutdown');


function shutdown() 
{ 
       $error = error_get_last();

       if ($error['type'] === E_ERROR) {

      //do your shutdown stuff here
      //be care full do not call any other function from within shutdown function
      //as php may not wait until that function finishes
      //its a strange behavior. During testing I realized that if function is called 
      //from here that function may or may not finish and code below that function
      //call may or may not get executed. every time I had a different result. 

      // e.g.

      other_function();

      //code below this function may not get executed

       } 

} 

while(true)
{

}

function other_function()
{
  //code in this function may not get executed if this function
  //called from shutdown function
} 

?>
8
pinkal vansia

Oui, j'ai testé la solution par TheJanOnline. sleep () ne compte pas dans le temps d'exécution de php alors voici la version WORKING avec boucle indéfinie:

<?php 
function shutdown() 
 { 
     $a=error_get_last(); 
     if($a==null)   
         echo "No errors"; 
     else 
          print_r($a); 

 } 
register_shutdown_function('shutdown'); 
ini_set('max_execution_time',1 ); 
while(1) {/*nothing*/}
// will die after 1 sec and print error
?>
5
Michal

Il existe une manière un peu délicate de gérer "Erreur fatale: dépassement du temps d'exécution maximal de 30 secondes" comme exception dans certains cas:

function time_sublimit($k = 0.8) {
    $limit = ini_get('max_execution_time'); // changes even when you set_time_limit()
    $sub_limit = round($limit * $k);
    if($sub_limit === 0) {
        $sub_limit = INF;
    }
    return $sub_limit;
}

Dans votre code, vous devez mesurer le temps d'exécution et lever l'exception avant que l'erreur fatale de délai d'attente puisse être déclenchée. $ k = 0,8 correspond à 80% du temps d'exécution autorisé, vous avez donc 20% de temps pour gérer l'exception.

try{
    $t1 = time(); // start to mesure time.
    while (true) { // put your long-time loop condition here
        time_spent = time() - $t1;
        if(time_spent >= time_sublimit()) {
            throw new Exception('Time sublimit reached');
        }
        // do work here
    }
} catch(Exception $e) {
    // catch exception here
}
4
user3901681

J'ai trouvé cela en fonction de la réponse donnée par pinkal-vansia. Je ne revendique donc pas une réponse originale, mais une réponse avec une application pratique. J'avais besoin d'un moyen pour que la page se rafraîchisse en cas d'expiration. Depuis que j'ai observé suffisamment de délais d'expiration de mon script cURL pour savoir que le code fonctionne, mais que parfois, pour une raison quelconque, il ne parvient pas à se connecter au serveur distant, ou à lire entièrement le code HTML servi, et que lors de l'actualisation, le problème disparaît, je suis d'accord avec le rafraîchissement du script pour "guérir" une erreur de délai d'exécution maximal.

<?php //script name: scrape_script.php

ini_set('max_execution_time', 300);

register_shutdown_function('shutdown');

function shutdown() 
{ 
    ?><meta http-equiv="refresh" content="0; url=scrape_script.php"><?php
    // just do a meta refresh. Haven't tested with header location, but
    // this works fine.
}

Pour info, 300 secondes n'est pas trop long pour le script de scraping que j'exécute, ce qui prend juste un peu moins que cela pour extraire les données des types de pages que je suis en train de gratter. Parfois, il ne passe que quelques secondes uniquement en raison d'irrégularités de connexion. Sachant que ce sont les temps de connexion qui échouent parfois, plutôt que le traitement de script, il est préférable de ne pas augmenter le délai d'attente, mais plutôt d'actualiser automatiquement la page et de réessayer.

4
TARKUS