假设我们有班级CFoo
。在以下示例中,CFoo::__destruct()
被称为?
function MyPHPFunc()
{
$foo = new CFoo();
. . .
// When/where/how does $foo get destroyed/deleted?
}
在这个示例中,当脚本退出MyPHPFunc
范围时会调用析构函数,因为$foo
将不再可访问?
答案 0 :(得分:35)
在PHP中,所有值都保存在所谓的zval
中。那些zval
包含实际数据,类型信息和 - 这对您的问题很重要 - 引用计数。请看下面的代码段:
$a = new B; // $a points to zval(new B) with refcount=1
$b = $a; // $a, $b point to zval(new B) with refcount=2 (+1)
$c = $b; // $a, $b, $c point to zval(new B) with refcount=3 (+1)
unset($a); // $b, $c point to zval(new B) with refcount=2 (-1)
只要refcount
到达0
,就会释放zval
并调用对象析构函数。
以下是refcount
到达0
的一些示例:
unset
变量:
$a = new B; // refcount=1
unset($a); // refcount=0 => __destruct!
可是:
$a = new B; // refcount=1
$b = $a; // refcount=2
unset($a); // refcount=1 => no destruct as refcount > 0, even though unset() was called!
离开函数(或方法)范围
function a() {
$a = new B; // refcount=1
} // refcount=0 => __destruct! (as $a does not exist anymore)
脚本执行结束
$a = new B; // refcount=1
die(); // refcount=0 => __destruct! (on script execution end all vars are freed)
// doesn't need to be die(), can be just normal execution end
这些显然不是导致refcount
减少的所有条件,而是你最常见的那些。
另外我应该提一下,因为PHP 5.3循环引用也会被检测到。因此,如果对象$a
引用了对象$b
和$b
引用$a
,并且没有对$a
或$b
进一步引用refcount
1}}两者都是1
,但它们仍然会被释放(__destruct
ed。在这种情况下,虽然破坏的顺序是未定义的行为。
答案 1 :(得分:4)
PHP 5引入了类似于其他概念的析构函数概念 面向对象的语言,例如C ++。析构函数方法将是 只要没有其他特定参考,就会调用 对象,或在关闭序列期间的任何顺序。 - PHP Manual
如果您想查看正在运行的流程you can run this code here。
<?php
class A
{
public function __construct() { var_dump('Creating: '. get_class($this)); }
public function __destruct() { var_dump('Removing: '. get_class($this)); }
}
class B extends A {}
$A = new A();
/*
* When this block is called later on
*/
function create_b()
{
$B = new B();
} // At this point the function scope ends, and since $B is not referenced anymore it's removed.
var_dump('B is next');
create_b(); // Run above block, create, then destroy be
var_dump('B is now gone');
// At this point the PHP file parser ends, $A is destroyed since it's not used anymore
答案 2 :(得分:2)
信息在manual中,虽然有点神秘:
PHP 5引入了类似于其他面向对象语言(如C ++)的析构函数概念。只要没有对特定对象的其他引用,或者在关闭序列期间以任何顺序,就会调用析构函数方法。
含义:当对象被销毁(=例如unset()
)或脚本关闭时,将调用析构函数。
其他有用信息:
与构造函数一样,引擎不会隐式调用父析构函数。为了运行父析构函数,必须在析构函数体中显式调用parent :: __ destruct()。
即使使用exit()停止脚本执行,也会调用析构函数。在析构函数中调用exit()将阻止剩余的关闭例程执行。
答案 3 :(得分:1)
最好的方法就是测试。
然而,简单的答案是在垃圾清理期间调用__destruct。粗糙对任何人都没有帮助,因为垃圾清理是一个持续的过程,当没有可以调用它们的作用域时清理局部变量。
但是这里有一些示例代码,结果完整地解释了在脚本内部退出范围时会发生什么。
<?php
class testingdestructor {
public function __construct($num) {
$this->num = $num;
}
public function __destruct() {
echo "I am number {$this->num}\n";
}
}
class testing2{
public function __construct($num) {
$this->classtest = new testingdestructor($num);
}
public function __destruct() {
echo "I am not a number\n";
}
}
$iam1 = new testingdestructor(1);
$iam4 = new testing2(4);
function testfunction() {
$iam2 = new testingdestructor(2);
}
testfunction();
$iam3 = new testingdestructor(3);
unset($iam1);
这个奇怪的类函数和变量的输出是这个
I am number 2
I am number 1
I am number 3
I am not a number
I am number 4
这告诉我们函数的结尾调用__destruct,就像unset一样,并且至少在实践中,脚本清理的结束是按照创建的相反顺序完成的。
答案 4 :(得分:0)
如果创建一个类的实例并使用该对象。在完成所有任务后,如果你调用析构函数并再次使用下一行中的相同对象来执行其他任务,则将无法再使用该任务。这意味着你的析构函数被成功调用