2008-10-25 10 views
10

J'ai une classe PHP qui crée une image PNG à la volée et l'envoie au navigateur. Le manuel PHP dit que je dois m'assurer que la fonction imagedestroy est appelée à la fin pour libérer la mémoire. Maintenant, si je n'utilise pas une classe, j'aurais un code comme ceci:PHP: destructeur vs register_shutdown_function

function shutdown_func() 
{ 
    global $img; 
    if ($img) 
     imagedestroy($img); 
} 
register_shutdown_function("shutdown_func"); 

Cependant, je crois que bon endroit pour ma classe serait de placer un appel à imagedestroy en classe destructor .

J'ai échoué à savoir si les destructeurs sont appelés de la même façon que les fonctions d'arrêt? Par exemple, si l'exécution s'arrête lorsque l'utilisateur appuie sur le bouton STOP dans le navigateur.

Remarque: peu importe ce que vous écrivez dans votre réponse, veuillez pointer vers un article ou une page de manuel (URL) qui le supporte.

+0

J'apprécierais quand vous ne modifieriez pas mes réponses pour enlever des sections que vous trouvez «non pertinentes», c'est irrespectueux. En ce qui concerne la collecte des ordures, c'était pertinent, mais ça vous va. – Tomalak

+0

@Tomalak: L'autre partie de votre réponse était pertinente et ponctuelle, j'ai même voté. Pourriez-vous s'il vous plaît le remettre? –

Répondre

11

Je viens de tester avec Apache, PHP étant utilisé comme module Apache. J'ai créé une boucle sans fin comme celui-ci:

<?php 
class X 
{ 
    function __destruct() 
    { 
     $fp = fopen("/var/www/htdocs/dtor.txt", "w+"); 
     fputs($fp, "Destroyed\n"); 
     fclose($fp); 
    } 
}; 

$obj = new X(); 
while (true) { 
    // do nothing 
} 
?> 

Voici ce que j'ai découvert:

  • appuyant sur le bouton STOP dans Firefox ne s'arrête pas ce script
  • Si je ferme Apache, destructor ne reçoit pas appelé
  • il arrête quand il atteint max_execution_time PHP et destuctor ne soit pas appelé

Toutefois, en procédant ainsi:

<?php 
function shutdown_func() { 
    $fp = fopen("/var/www/htdocs/dtor.txt", "w+"); 
    fputs($fp, "Destroyed2\n"); 
    fclose($fp); 
} 
register_shutdown_function("shutdown_func"); 

while (true) { 
    // do nothing 
} 
?> 

shutdown_func est appelée. Cela signifie donc que la classe destuctor n'est pas aussi bonne que les fonctions d'arrêt.

+1

Pouvez-vous ajouter un lien vers quoi que ce soit dans le manuel php ou qui prend en charge votre test? Ce serait très avantageux pour nous tous. –

+1

Si vous avez une configuration fonctionnelle, copiez/collez simplement les exemples que j'ai donnés et essayez vous-même. Il a été expliqué dans le manuel PHP ou sinon je ne demanderais pas ici à SO. –

+0

Ce comportement n'est pas "par conception" donc il peut changer dans les futures versions. Si aucune sortie n'est envoyée au navigateur, le bouton d'arrêt n'a aucun effet. "echo" est une fonction qui peut provoquer une erreur fatale (tuyau cassé). Merci pour votre recherche, mais dans le cas de GD img tout le monde devrait utiliser la méthode destructor. –

0

Je pense qu'une grande chose que vous avez manquée est que toute la mémoire allouée par PHP pendant l'exécution du script est libérée une fois le script terminé. Même si l'utilisateur appuie sur le bouton d'arrêt, PHP traite le script jusqu'à ce qu'il soit terminé, le renvoie au démon HTTP pour être servi au visiteur (ou non, selon l'habilité du démon). Ainsi, la libération explicite de la mémoire à la fin de l'exécution du script est un peu redondante. Certains pourraient soutenir que ce serait une bonne chose à faire, mais c'est toujours redondant. Mais, en ce qui concerne les destructeurs de classes, ils sont appelés chaque fois que l'objet est détruit, soit explicitement par unset(), soit à la fin/fin du script. La recommandation du développeur libérant explicitement la mémoire utilisée dans la manipulation de l'image est de s'assurer de ne pas avoir de fuite de mémoire, car les bitmaps peuvent être difficiles du côté de la mémoire (hauteur * largeur * profondeur de bit * 3 (+ 1 si vous avez un canal alpha))

Pour satisfaire vos besoins Wikipédien:

+0

AFAIU, la mémoire n'est pas allouée par PHP car elle appelle GD pour créer des images et GD n'a pas de garbage collection. –

+0

J'ai déjà lu le manuel PHP, mais il ne dit pas explicitement si le destructeur est appelé si le script est terminé par le serveur web. –

+0

aussi AFAIK, GD est juste une bibliothèque qui est liée dynamiquement avec PHP. Cela impliquerait que si GD sait comment faire tout cela, c'est toujours la mémoire de PHP et donc la collection de garbage de PHP. –

2

Basé sur le principe que vous devriez finish what you start, je dirais que le destructeur est l'endroit approprié pour l'appel gratuit.

Le destructor sera appelé lorsque l'objet sera éliminé, tandis qu'un shutdown function ne sera pas appelé tant que l'exécution du script ne sera pas terminée. Comme l'a noté Wolfie, cela ne se produira pas forcément si vous arrêtez le serveur ou le script de force, mais à ce moment-là, la mémoire allouée par PHP sera libérée de toute façon.

Également noté par Wolfie, PHP va libérer des ressources de script lorsque le script se ferme, donc si vous instanciez seulement l'un de ces objets, alors vous ne remarquerez probablement pas une énorme différence. Cependant, si par la suite vous finissez par instancier ces choses, ou si vous le faites en boucle, alors vous ne voulez probablement pas avoir à vous soucier d'un pic soudain dans l'utilisation de la mémoire, donc pour la santé mentale future, je retourne à recommandation originale; le mettre dans le destructeur.

1

J'ai récemment eu des problèmes avec ceci parce que j'essayais de manipuler la destruction spécifiquement pour le cas où le serveur rencontre un délai d'expiration et que je voulais inclure des données de classe dans le journal des erreurs. Je recevrais une erreur en référence & $ ceci (bien que je l'ai vu fait dans quelques exemples, probablement un problème de version ou un effet secondaire de symfony), et la solution que j'ai trouvée était assez propre:

class MyClass 
{ 
    protected $myVar; 

    /** 
    * constructor, registers shutdown handling 
    */ 
    public function __construct() 
    { 
     $this->myVar = array(); 

     // workaround: set $self because $this fails 
     $self = $this; 
     // register for error logging in case of timeout 
     $shutdown = function() use (&$self) { 
      $self->shutdown(); 
     }; 
     register_shutdown_function($shutdown); 
    } 

    /** 
    * handle shutdown events 
    */ 
    public function shutdown() 
    { 
     $error = error_get_last(); 
     // if shutdown in error 
     if ($error['type'] === E_ERROR) { 
      // write contents to error log 
      error_log('MyClass->myVar on shutdown' . json_encode($this->myVar), 0); 
     } 
    } 

    ... 

Espérons que cela aide quelqu'un!

+0

code awsome, résoudre le mien –