PHP中什么时候不调用__destruct?

3phpmpom  于 2023-05-05  发布在  PHP
关注(0)|答案(7)|浏览(172)
class MyDestructableClass {
   function __construct() {
       print "\nIn constructor\n";
       $this->name = "MyDestructableClass";
   }

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

$obj = new MyDestructableClass();

上面的脚本在复杂环境下,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();
7hiiyaii

7hiiyaii1#

__destruct被称为:

  • 如果exit在另一个析构函数中被调用
  • 根据PHP版本:如果在注册到register_shutdown_function的关闭函数中调用exit
  • 如果代码中的某个地方有致命错误
  • 如果另一个析构函数抛出异常
  • 如果你试图在析构函数中处理异常(PHP〉= 5.3.0)

我想我现在只能想到这些了
”Pascal Martin说。这是调试的第一步。

rseugnpd

rseugnpd2#

如果脚本在CLI上运行并收到SIGINT(Ctrl+C),也不会调用__destruct方法

6qqygrtg

6qqygrtg3#

屏幕上没有输出并不意味着析构函数没有被调用:可以使用output_buffering * 捕获输出(也许lime这样做,以便能够处理它?)*,并且在脚本结束时不回显。
出于测试的目的,您可以尝试在__destruct方法中写入文件,而不仅仅是回显一些文本。

  • (只需确保您的应用程序/ PHP具有写入目标文件所需的权限)*
  • (我已经遇到过在析构函数中看不到输出的情况--但实际上它被调用了)*
gudnpqoy

gudnpqoy4#

the PHP documentation说:
即使使用exit()停止脚本执行,析构函数也会被调用。在析构函数中调用exit()将阻止其余的关闭例程执行。

6g8kf2rb

6g8kf2rb5#

我知道我有点迟到了,但对于那些也希望在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

**注意:**在析构函数或shutdown函数中调用exit或抛出异常会导致脚本立即终止。

vhmi4jdf

vhmi4jdf6#

如果你将一个类的示例的引用传递给另一个类,并在该类外部调用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时正确地处理对类的引用计数和依赖顺序,并以正确的顺序调用析构函数。

mnowg1ta

mnowg1ta7#

不熟悉教义,但检查一点:在__construct()/__destruct()中检查可能的异常,它们可能会产生致命错误。

相关问题