在 PHP 中,什么决定了何时销毁类对象?

2022-08-30 22:52:08

假设我们有类。在下面的示例中,何时调用?CFooCFoo::__destruct()

function MyPHPFunc()
{
  $foo = new CFoo();

  . . .

  // When/where/how does $foo get destroyed/deleted?
}

在此示例中,当脚本退出范围时,是否会调用析构函数,因为将不再可访问?MyPHPFunc$foo


答案 1

在 PHP 中,所有值都保存在所谓的 s 中。这些包含实际数据,类型信息和 - 这对您的问题很重要 - 参考计数。请看以下代码段:zvalzval

$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)

一旦到达,就会释放并调用对象析构函数。refcount0zval

以下是一些达到范围的例子:refcount0

  • unseting 一个变量:

    $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循环引用也会被检测到。因此,如果对象引用对象和引用,并且没有任何进一步的引用,或者两者的s将是,但它们仍然将被释放(和ed)。在这种情况下,破坏的顺序是未定义的行为。$a$b$b$a$a$brefcount1__destruct


答案 2

PHP 5 引入了一个类似于其他面向对象语言(如 C++)的析构函数概念。一旦没有对特定对象的其他引用,就会调用析构函数方法,或者在关闭序列期间以任何顺序调用析构函数方法。- PHP手册

如果要查看该过程的实际运行情况,可以在此处运行此代码

<?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

推荐