web-dev-qa-db-fra.com

Quand __destruct ne sera-t-il pas appelé en PHP?

class MyDestructableClass {
   function __construct() {
       print "\nIn constructor\n";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "\nDestroying " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();

Lorsque le script ci-dessus est dans un environnement complexe, le __destruct ne sera pas appelé lorsque exit, mais je ne peux pas le reproduire facilement. Quelqu'un l'a-t-il déjà remarqué?

MODIFIER

Je vais poster tout ça ici, c'est l'environnement de test de symfony, ce qui signifie que vous pouvez facilement le reproduire si vous connaissez le framework:

require_once dirname(__FILE__).'/../bootstrap/Doctrine.php';


$profiler = new Doctrine_Connection_Profiler();

$conn = Doctrine_Manager::connection();
$conn->setListener($profiler);

$t = new Lime_test(0, new Lime_output_color());

class MyDestructableClass {
   function __construct() {
       print "\nIn constructor\n";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "\nDestroying " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();
$news = new News();

$news->setUrl('http://test');
$news->setHash('http://test');
$news->setTitle('http://test');
$news->setSummarize('http://test');
$news->setAccountId(1);
$news->setCategoryId(1);
$news->setThumbnail('http://test');
$news->setCreatedAt(date('Y-m-d H:i:s',time()));
$news->setUpdatedAt(date('Y-m-d H:i:s',time()));
$news->save();
exit();
50
user198729

Le __destruct sera pas appelé:

  • Si exit est appelé dans un autre destructeur
  • Selon la version PHP: si exit est appelé dans une fonction d'arrêt enregistrée avec register_shutdown_function
  • S'il y a une erreur fatale quelque part dans le code
  • Si un autre destructeur lève une exception
  • Si vous essayez de gérer une exception dans un destructeur (PHP> = 5.3.0)

Je suppose que c'est tout ce à quoi je peux penser en ce moment

& Ce que Pascal MARTIN a dit. C'est la première étape du débogage.

71
edorian

Ne pas avoir de sortie à l'écran ne signifie pas que le destructeur n'est pas appelé: le ouptut pourrait être capturé en utilisant output_buffering (peut-être que Lime le fait, pour pouvoir y travailler?), et non répercuté à la fin du script, par exemple.

À des fins de test, vous pouvez essayer d'écrire dans un fichier, dans votre __destruct, au lieu de simplement faire écho à du texte.
(Assurez-vous simplement que votre application/PHP a les privilèges requis pour écrire dans votre fichier de destination)

(J'ai déjà rencontré des situations où je ne verrais pas la sortie effectuée dans un destructeur - mais elle était en fait appelée)

14
Pascal MARTIN

Le __destruct la méthode ne sera pas non plus appelée si le script s'exécute sur CLI et reçoit un SIGTERM (Ctrl+C)

12
nickel715

Comme la PHP dit:

Le destructeur sera appelé même si l'exécution du script est arrêtée à l'aide de exit(). L'appel de exit() dans un destructeur empêchera l'exécution des routines d'arrêt restantes.

11

Je sais que je suis un peu en retard pour la fête, mais pour les gens qui cherchent également à obtenir __destruct à exécuter lorsque CTRL+C et/ou des erreurs fatales se produisent, vous pouvez essayer ceci (ci-dessous est un cas de test):

Index.php

<?php

// Setup CTRL+C and System kill message handler
// The only signal that cannot be caught is the SIGKILL (very hard kill)
declare(ticks = 1); // Required else it won't work.
pcntl_signal(SIGTERM, 'close'); // System kill (Unhappy Termination)
pcntl_signal(SIGINT, 'close'); // CTRL+C (Happy Termination)

// Shutdown functions will be executed even on fatal errors
register_shutdown_function('close');

function close($signal = null) // only pcntl_signal fills $signal so null is required
{
    // Check if there was an fatal error (else code below isn't needed)
    $err = error_get_last();
    if(is_array($err))
    {
        foreach(array_keys($GLOBALS) as $key)
        {
            if(in_array($key, ['_GET', '_POST', '_COOKIE', '_FILES', '_SERVER', '_REQUEST', '_ENV', 'GLOBALS']))
                continue;

            // This will automatically call __destruct
            unset($GLOBALS[$key]);
        }
    }
}

// Example
class blah
{
    private $id = '';

    public function __construct()
    {
        $this->id = uniqid();
        // note this piece of code, doesn't work on windows!
        exec('mkdir /tmp/test_'.$this->id);
    }

    public function __destruct()
    {
        // note this piece of code, doesn't work on windows!
        exec('rm /tmp/test_'.$this->id.' -R');
    }
}

// Test
$a = new blah();
$b = new blah();
$c = new blah();
$d = new blah();
$e = new blah();
$f = new blah();
$g = new blah();
$h = new blah();
$i = new blah();
$j = new blah();
$k = new blah();
$l = new blah();
$m = new blah();
$n = new blah();
$o = new blah();
$p = new blah();
$q = new blah();
$r = new blah();
$s = new blah();
$t = new blah();
$u = new blah();
$v = new blah();
$w = new blah();
$x = new blah();
$y = new blah();
$z = new blah();

// The script that causes an fatal error
require_once(__DIR__.'/test.php');

Test.php

<?php

// this will create a parse (E_PARSE) error.
asdsaddsaadsasd

Remarque: l'appel à exit ou le lancement d'exceptions dans les destructeurs ou les fonctions d'arrêt entraînera la fin immédiate du script.

3
WhiteFang