php析构函数行为

时间:2010-05-06 01:49:01

标签: php oop destructor

我试图了解php构造函数和析构函数的行为。一切都按照构造函数的预期进行,但是我无法隐式地触发析构函数。我已经完成了php.net和相关网站上的所有阅读,但我无法找到这个问题的答案。

如果我有一个简单的类,比如:

class test{

     public function __construct(){
          print "contructing<br>";
     }

     public function __destruct(){
          print "destroying<br>";
     }
}

我称之为:

$t = new test;

它打印构造函数消息。但是,我希望当脚本结束并呈现页面时,析构函数应该触发。当然没有。

如果我打电话未设置($ t);当脚本结束时,析构函数当然会触发,但是有没有办法让它隐式触发?

感谢任何提示

5 个答案:

答案 0 :(得分:16)

这很容易测试。

<?php

class DestructTestDummy {
    protected $name;

    function __construct($name) {
        echo "Constructing $name\n";
        $this->name = $name;
    }

    function __destruct() {
        echo "Destructing $this->name\n";
        //exit;
    }
}

echo "Start script\n";

register_shutdown_function(function() {
    echo "Shutdown function\n";
    //exit
});

$a = new DestructTestDummy("Mr. Unset");
$b = new DestructTestDummy("Terminator 1");
$c = new DestructTestDummy("Terminator 2");

echo "Before unset\n";
unset($a);
echo "After unset\n";


echo "Before func\n";
call_user_func(function() {
    $c = new DestructTestDummy("Mrs. Scopee");
});
echo "After func\n";

$b->__destruct();

exit("Exiting\n");

在PHP 5.5.12中打印:

Start script
Constructing Mr. Unset
Constructing Terminator 1
Constructing Terminator 2
Before unset
Destructing Mr. Unset
After unset
Before func
Constructing Mrs. Scopee
Destructing Mrs. Scopee
After func
Destructing Terminator 1
Exiting
Shutdown function
Destructing Terminator 2
Destructing Terminator 1

所以我们可以看到析构函数在我们显式取消设置对象时被调用,当它超出范围时,在脚本结束时被调用。

答案 1 :(得分:6)

删除/销毁对象时使用__destruct()魔术函数(使用unset)。在关闭脚本期间不会调用它。当PHP脚本完成执行时,它会清理内存,但它不会“删除”这样的对象,因此不会调用__destruct()方法。

你可能会想到register_shutdown_function(),当你的PHP脚本完成执行时会被触发。

function shutdown()
{
    // code here
    echo 'this will be called last';
}

register_shutdown_function('shutdown');

答案 2 :(得分:3)

我的理解是,当脚本结束时,会自动为任何剩余的对象调用析构函数。

通过查看manual page on constructors and destructors,似乎唯一可以完全绕过析构函数的方法是从相关对象之前销毁的对象的析构函数中调用exit()

您是否在任何析构函数中使用exit()?您的脚本中是否还有多个对象?

如果不是太麻烦,也许您可​​以发布有问题的实际代码,而不是现在问题中的示例代码。除了示例构造函数中的拼写错误之外,该代码应该调用test对象的constuctor和析构函数。

答案 3 :(得分:0)

一旦所有对对象的引用都未设置,则将调用类的__destruct方法。

例如

$dummy = (object) new Class();

如果虚拟对象设置为null或退出脚本,则将自动调用析构函数。

unset($dummy); // or $dummy = null;
//exit(); //also possible

但是,要调用析构函数方法要注意三点:

首先,解扰器方法应该是公共方法,而不是受保护的或私有的。

第二,避免使用内部和循环引用。例如:

class NewDemo
{
     function __construct()
     {
          $this->foo = $this;
     } 
     function __destruct()
     {
          // this method will never be called 
          // and cause memory leaks
          // unset will not clear the internal reference
     }
}

以下内容也不起作用:

$a = new Class();
$b = new Class();
$a->pointer = $b;
$b->pointer = $a;

unset($a); // will not call destructor
unset($b); // will not call destructor

第三,确定在发送输出之后是否调用析构函数。使用

gc_collect_cycles() 

可以确定是否在将数据发送给用户之前调用了所有析构函数。

有关示例的魔术销毁方法的来源和详尽说明,请参见http://php.net/manual/en/language.oop5.decon.php

答案 4 :(得分:0)

方法__destruct()是无用的。最好做这样的事情:

class MyClass {
    function __construct() {
        register_shutdown_function([
            &$this,
            'the_end_game'
        ]);
    }

    function the_end_game() {
        /* Your last part of code, save logs or what you want */
    }
}