什么时候__destruct不会被PHP调用?
class MyDestructableClass { function __construct() { print "\nIn constructor\n"; $this->name = "MyDestructableClass"; } function __destruct() { print "\nDestroying " . $this->name . "\n"; } } $obj = new MyDestructableClass();
当上面的脚本处于一个复杂的环境中时,__destruct在exit
时不会被调用,但是我不能很容易地重现它。有没有人注意到这个?
编辑
我将在这里发布整个东西,这是symfony的testing环境,这意味着如果你熟悉框架,你可以很容易地重现它:
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('Ymd H:i:s',time())); $news->setUpdatedAt(date('Ymd H:i:s',time())); $news->save(); exit();
__destruct
不会被调用:
- 如果在另一个析构函数中调用
exit
- 取决于PHP版本:如果在使用
register_shutdown_function
的closures函数中调用exit
- 如果在代码中有某个致命的错误
- 如果另一个析构函数抛出exception
- 如果您尝试在析构函数中处理exception(PHP> = 5.3.0)
猜猜这就是我现在所能想到的
帕斯卡尔·马丁(Pascal MARTIN)说。 这是debugging的第一步。
在屏幕上没有输出并不意味着析构函数没有被调用:可以使用output_buffering来捕获ouptut (也许这样做,能够在它上面工作?) ,并且在脚本结束时不会被回显。
出于testing目的,您可以尝试写入__destruct
方法中的文件,而不是仅回显一些文本。
(只要确保您的应用程序/ PHP具有写入目标文件所需的权限)
(我已经遇到了在一个析构函数中看不到输出的情况 – 但实际上它被调用了)
如果脚本在CLI上运行并且收到SIGTERM( Ctrl + C ),则__destruct
方法也不会被调用。
正如PHP文档所说:
即使使用
exit()
停止脚本执行,也会调用析构函数。 在析构函数中调用exit()
将阻止执行剩余的closures例程。
我知道我对聚会有点晚了,但是对于那些也希望在CTRL + C和/或致命错误发生时执行__destruct
人,可以试试这个(下面是一个testing用例):
的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
注意:调用exit或在析构函数或closures函数中抛出exception将导致脚本立即终止。
不熟悉Doctrine,但请检查一点:检查__construct()/ __ destruct()中可能出现的exception,它们可能会产生致命错误。