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

发布于 2024-12-21 07:55:37 字数 301 浏览 1 评论 0原文

假设我们有类 CFoo。在下面的示例中,什么时候调用 CFoo::__destruct()

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

  . . .

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

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

Let's say that we have class CFoo. In the following example when is CFoo::__destruct() called?

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

  . . .

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

In this example would the destructor be called when the script exits the scope of MyPHPFunc because $foo would no longer be accessible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

孤独陪着我 2024-12-28 07:55:37

在 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达到0zval就会被释放并调用对象析构函数。

以下是refcount达到0的一些示例:

  • 取消设置变量:

    $a = 新 B; // 引用计数=1
    未设置($a); // 引用计数=0 => __破坏!
    

    但是:

    $a = 新 B; // 引用计数=1
    $b = $a; // 引用计数=2
    未设置($a); // 引用计数=1 =>没有破坏作为引用计数> 0,即使调用了 unset()!
    
  • 离开函数(或方法)作用域

    函数 a() {
        $a = 新 B; // 引用计数=1
    } // 引用计数=0 => __破坏! (因为 $a 不再存在)
    
  • 脚本执行结束

    $a = 新 B; // 引用计数=1
    死(); // 引用计数=0 => __破坏! (在脚本执行结束时,所有变量都被释放)
    // 不需要是die(),正常执行结束即可
    

这些显然不是导致refcount 减少的所有条件,而是您最常遇到的条件。

另外我应该提到,由于 PHP 5.3 循环引用也会被检测到。因此,如果对象 $a 引用对象 $b$b 引用 $a 并且没有任何进一步的内容对 $a$b 的引用,两者的 refcount 将为 1,但它们仍将被释放(和__destructed)。在这种情况下,尽管破坏的顺序是未定义的行为。

In PHP all values are saved in so called zvals. Those zvals contain the actual data, type information and - this is important for your question - a reference count. Have a look at the following snippet:

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

As soon as the refcount reaches 0 the zval is freed and the object destructor is called.

Here are some examples of the refcount reaching 0:

  • unseting a variable:

    $a = new B; // refcount=1
    unset($a);  // refcount=0 => __destruct!
    

    But:

    $a = new B; // refcount=1
    $b = $a;    // refcount=2
    unset($a);  // refcount=1 => no destruct as refcount > 0, even though unset() was called!
    
  • leaving function (or method) scope

    function a() {
        $a = new B; // refcount=1
    }               // refcount=0 => __destruct! (as $a does not exist anymore)
    
  • script execution end

    $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
    

These obviously are not all conditions leading to a reduction of refcount, but the ones you will most commonly meet.

Also I should mention that since PHP 5.3 circular references will be detected, too. So if object $a references object $b and $b references $a and there aren't any further references to $a or $b the refcounts of both will be 1, but they still will be freed (and __destructed). In this case though the order of destruction is undefined behavior.

傾旎 2024-12-28 07:55:37

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

PHP 5 introduces a destructor concept similar to that of other
object-oriented languages, such as C++. The destructor method will be
called as soon as there are no other references to a particular
object, or in any order during the shutdown sequence.
- PHP Manual

If you want to see the process in action, 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
空心↖ 2024-12-28 07:55:37

这些信息位于手册中,尽管有些神秘:

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

含义:当对象被销毁(= 例如 unset())或脚本关闭时,将调用析构函数。

其他有用信息:

与构造函数一样,父析构函数不会被引擎隐式调用。为了运行父析构函数,必须在析构函数主体中显式调用parent::__destruct()。

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

The information is in the manual, albeit somewhat cryptic:

PHP 5 introduces a destructor concept similar to that of other object-oriented languages, such as C++. The destructor method will be called as soon as there are no other references to a particular object, or in any order during the shutdown sequence.

Meaning: The destructor will be called when the object gets destroyed (= e.g. unset()), or when the script shuts down.

Additional useful info:

Like constructors, parent destructors will not be called implicitly by the engine. In order to run a parent destructor, one would have to explicitly call parent::__destruct() in the destructor body.

The destructor will be called even if script execution is stopped using exit(). Calling exit() in a destructor will prevent the remaining shutdown routines from executing.

方圜几里 2024-12-28 07:55:37

最好的了解方法是进行测试。

然而简单的答案是 __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 一样,并且至少在实践中,脚本清理的末尾是按照创建的相反顺序完成的。

the best way to know is to test.

however the simple answer is that __destruct is called during garbage cleanup. coarse that does not help anyone as garbage cleanup is an ongoing process that cleans up local variables when there is no scope that can call them.

however here is some sample code, and the result which fully explains what happens when exiting scope internally to the script.

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

the output of this strange set of classes function and vars is this

I am number 2
I am number 1
I am number 3
I am not a number
I am number 4

this shows us that the end of the function calls the __destruct, as does unset, and that at least in practice that end of script cleanup is done in reverse order created.

清风无影 2024-12-28 07:55:37

如果创建类的实例并使用该对象。完成所有任务后,如果您调用析构函数并在下一行中再次使用相同的对象来执行其他任务,您将无法再使用。这意味着你的析构函数被成功调用

if create instance of a class and use the object.after finishing all your tasks if u call the destructor and again use the same object in the next line to perform some other task u will be no longer able to use. That means ur destructor is called successfully

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文