class MyDestructableClass {
function __construct() {
print "\nIn constructor\n";
$this->name = "MyDestructableClass";
}
function __destruct() {
print "\nDestroying " . $this->name . "\n";
}
}
$obj = new MyDestructableClass();
当上述脚本位于复杂环境中时,__destruct
将无法调用exit
,但我无法轻易复制它。有人曾经注意到了吗?
修改
我会在这里发布所有内容,这是symfony的测试环境,这意味着如果你熟悉框架,你可以很容易地重现它:
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();
答案 0 :(得分:70)
__destruct
将不被调用:
exit
exit
注册的关闭功能中调用register_shutdown_function
猜猜我现在能想到的就是
&安培; Pascal MARTIN说的是什么。这是调试它的第一步。
答案 1 :(得分:14)
屏幕上没有输出并不意味着没有调用析构函数:可以使用output_buffering 捕获ouptut(也许石灰会这样做,以便能够处理它?),例如,当脚本结束时不会回显。
出于测试目的,您可以尝试使用__destruct
方法写入文件,而不是只回显一些文本。
(只需确保您的应用程序/ PHP具有写入目标文件所需的权限)
(我已经遇到过我不会在析构函数中看到输出的情况 - 但实际上已经调用了它)
答案 2 :(得分:12)
如果脚本在CLI上运行并且收到SIGTERM( Ctrl + C ),则也不会调用__destruct
方法
答案 3 :(得分:11)
即使使用
exit()
停止脚本执行,也会调用析构函数。在析构函数中调用exit()
将阻止剩余的关闭例程执行。
答案 4 :(得分:3)
我知道我参加聚会的时间有点晚,但对于那些希望在 CTRL + C 时执行__destruct
的人/或发生致命错误,你可以尝试这个(下面是测试用例):
<强>的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或抛出异常会导致脚本立即终止。
答案 5 :(得分:0)
如果将一个类的实例的引用传递给另一个类,并在该类之外调用exit,则不会调用该类的__destruct。对我来说似乎是个虫子。
$a = new classa();
$b = new classb($a); // where classb constructor is __construct(&a) {}
exit;
不会调用任何析构函数,并且不能保证破坏顺序。我已经尝试了多种方法来仅在析构函数中回显消息,除非我明确说出,否则它永远不会输出:
unset($b); // properly calls classb::__destruct
unset($a); // properly calls classa::__destruct
exit;
由于我无法获得任何结果,因此我无法确定这是析构函数的竞争条件还是仅是预期的结果;无论如何,unset()总是正确地调用析构函数。我知道这很痛苦,但是比忍受这个错误要好得多。他们需要在调用exit时正确处理对类和依赖项顺序的引用计数,并以正确的顺序调用析构函数。
答案 6 :(得分:-4)
不熟悉Doctrine,但请检查一点:检查__construct()/ __ destruct()中是否存在可能的异常,它们可能会产生致命错误。