web-dev-qa-db-fra.com

Puis-je faire confiance à la méthode PHP __destruct () pour être appelée?

En PHP5, la méthode __destruct () est-elle garantie d'être appelée pour chaque instance d'objet? Des exceptions dans le programme peuvent-elles empêcher cela?

42
Casey Watson

Le destructeur sera appelé lorsque toutes les références seront libérées ou lorsque le script se terminera. Je suppose que cela signifie que le script se termine correctement. Je dirais que des exceptions critiques ne garantiraient pas l'appel du destructeur.

Le documentation PHP est un peu mince, mais il dit que les exceptions dans le destructeur causeront des problèmes.

36
Geoff

Il convient également de mentionner que, dans le cas d'une sous-classe qui a son propre destructeur, le destructeur parent est pas appelé automatiquement.

Vous devez appeler explicitement parent :: __ destruct () depuis la sous-classe __ destruct () méthode si la classe parente effectue le nettoyage requis.

44
Mark Biek

D'après mon expérience, les destructeurs seront toujours appelés dans PHP 5.3, mais sachez que si un morceau de code appelle exit () ou si une erreur fatale se produit, PHP appellera des destructeurs dans " tout "ordre (je pense que l'ordre réel est l'ordre dans la mémoire ou l'ordre dans lequel la mémoire a été réservée aux objets. En pratique, cet ordre est presque toujours problématique). Ceci est appelé "séquence d'arrêt" dans la documentation PHP.

documentation PHP des destructeurs dit:

PHP 5 introduit un concept de destructeur similaire à celui des autres langages orientés objet, tels que C++. La méthode destructor sera appelée dès qu'il n'y aura pas d'autres références à un objet particulier, ou dans n'importe quel ordre pendant la séquence d'arrêt.

Par conséquent, si vous avez la classe X qui contient une référence à Y, le destructeur de X peut être appelé APRÈS que le destructeur de Y ait déjà été appelé. Espérons que la référence à Y n'était pas si importante ... Officiellement ce n'est pas un bug car il a été documenté.

Cependant, il est très difficile de contourner ce problème car officiellement PHP ne fournit aucun moyen de savoir si le destructeur est appelé normalement (les destructeurs sont appelés dans le bon ordre) ou les destructeurs sont appelés dans "n'importe quel" ordre où vous ne pouvez pas utiliser de données à partir d'objets référencés, car ceux-ci ont peut-être déjà été détruits. On pourrait contourner ce manque de détection en utilisant debug_backtrace () et en examinant la pile. L'absence de pile normale semble impliquer une "séquence d'arrêt" avec PHP 5.3 mais cela aussi n'est pas défini. Si vous avez des références circulaires, les destructeurs de ces objets ne seront pas du tout appelés avec PHP 5.2 ou moins et seront appelés dans "n'importe quel" ordre pendant la "séquence d'arrêt" dans PHP 5.3 ou supérieur. Pour les références circulaires, il n'existe pas d'ordre logiquement "correct" donc "n'importe quel" ordre est bon pour celles-ci.

Il y a quelques exceptions (c'est PHP après tout):

  • si exit() est appelé dans un autre destructeur, les destructeurs restants ne seront pas appelés ( http://php.net/manual/en/language.oop5.decon.php )
  • si une erreur FATAL se produit n'importe où (de nombreuses causes possibles, par exemple essayer de lever une exception à partir de n'importe quel autre destructeur pourrait en être une), les destructeurs restants ne seront pas appelés.

Bien sûr, si le moteur PHP rencontre une erreur de segmentation ou un autre bogue interne se produit, alors tous les paris sont désactivés.

Si vous voulez comprendre l'implémentation actuelle de la "séquence d'arrêt", voir https://stackoverflow.com/a/14101767 . Notez que cette implémentation peut changer dans les futures versions PHP.

13
Mikko Rantalainen

Il existe un bogue actuel avec des références circulaires qui empêche la méthode de destruction d'être appelée implicitement. http://bugs.php.net/bug.php?id=33595 Cela devrait être corrigé dans 5.3

10
MOdMac

Utilisez une fonction d'arrêt si vous voulez être sûr: register_shutdown_function ()

8
zupa