douche3244 2012-12-31 01:44
浏览 19
已采纳

在PHP中破坏对象的顺序是什么?

What is the exact order of object deconstruction?

From testing, I have an idea: FIFO for the current scope.

class test1
{
    public function __destruct()
    {
        echo "test1
";
    }
}

class test2
{
    public function __destruct()
    {
        echo "test2
";
    }
}

$a = new test1();
$b = new test2();

Which produces the same results time and time again:

test1
test2

The PHP manual is vague (emphasis mine to highlight uncertainty): "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."

What is the exact order of deconstruction? Can anyone describe in detail the implementation of destruction order that PHP uses? And, if this order is not consistent between any and all PHP versions, can anyone pinpoint which PHP versions this order changes in?

  • 写回答

2条回答 默认 最新

  • dpauxqt1281 2012-12-31 12:43
    关注

    First of all, a bit on general object destruction order is covered here: https://stackoverflow.com/a/8565887/385378

    In this answer I will only concern myself with what happens when objects are still alive during the request shutdown, i.e. if they were not previously destroyed through the refcounting mechanism or the circular garbage collector.

    The PHP request shutdown is handled in the php_request_shutdown function. The first step during the shutdown is calling the registered shutdown functions and subsequently freeing them. This can obviously also result in objects being destructed if one of the shutdown functions was holding the last reference to some object (or if the shutdown function itself was an object, e.g. a closure).

    After the shutdown functions have run the next step is the one interesting to you: PHP will run zend_call_destructors, which then invokes shutdown_destructors. This function will (try to) call all destructors in three steps:

    1. First PHP will try to destroy the objects in the global symbol table. The way in which this happens is rather interesting, so I reproduced the code below:

      int symbols;
      do {
          symbols = zend_hash_num_elements(&EG(symbol_table));
          zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_CC);
      } while (symbols != zend_hash_num_elements(&EG(symbol_table)));
      

      The zend_hash_reverse_apply function will walk the symbol table backwards, i.e. start with the variable that was created last and going towards the variable that was created first. While walking it will destroy all objects with refcount 1. This iteration is performed until no further objects are destroyed with it.

      So what this basically does is a) remove all unused objects in the global symbol table b) if there are new unused objects, remove them too c) and so on. This way of destruction is used so objects can depend on other objects in the destructor. This usually works fine, unless the objects in the global scope have complicated (e.g. circular) interrelations.

      The destruction of the global symbol table differs significantly from the destruction of all other symbol tables. Normally symbol tables are destructed by walking them forward and just dropping the refcount on all objects. For the global symbol table on the other hand PHP uses a smarter algorithm that tries to respect object dependencies.

    2. The second step is calling all remaining destructors:

      zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC);
      

      This will walk all objects (in order of creation) and call their destructor. Note that this only calls the "dtor" handler, but not the "free" handler. This distinction is internally important and basically means that PHP will only call __destruct, but will not actually destroy the object (or even change its refcount). So if other objects reference the dtored object, it will still be available (even though the destructor was already called). They will be using some kind of "half-destroyed" object, in a sense (see example below).

    3. In case the execution is stopped while calling the destructors (e.g. due to a die) the remaining destructors are not called. Instead PHP will mark the objects are already destructed:

      zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC);
      

      The important lesson here is that in PHP a destructor is not necessarily called. The cases when this happens are rather rare, but it can happen. Furthermore this means that after this point no more destructors will be called, so the remainder of the (rather complicated) shutdown procedure does not matter anymore. At some point during the shutdown all the objects will be freed, but as the destructors have already been called this is not noticeable for userland.

    I should point out that this is the shutdown order as it currently is. This has changed in the past and may change in the future. It's not something you should rely on.

    Example for using an already destructed object

    Here is an example showing that it is sometimes possible to use an object that already had its destructor called:

    <?php
    
    class A {
        public $state = 'not destructed';
    
        public function __destruct() { $this->state = 'destructed'; }
    }
    
    class B {
        protected $a;
    
        public function __construct(A $a) { $this->a = $a; }
    
        public function __destruct() { var_dump($this->a->state); }
    }
    
    $a = new A;
    $b = new B($a);
    
    // prevent early destruction by binding to an error handler (one of the last things that is freed)
    set_error_handler(function() use($b) {});
    

    The above script will output destructed.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 在获取boss直聘的聊天的时候只能获取到前40条聊天数据
  • ¥20 关于URL获取的参数,无法执行二选一查询
  • ¥15 液位控制,当液位超过高限时常开触点59闭合,直到液位低于低限时,断开
  • ¥15 marlin编译错误,如何解决?
  • ¥15 有偿四位数,节约算法和扫描算法
  • ¥15 VUE项目怎么运行,系统打不开
  • ¥50 pointpillars等目标检测算法怎么融合注意力机制
  • ¥20 Vs code Mac系统 PHP Debug调试环境配置
  • ¥60 大一项目课,微信小程序
  • ¥15 求视频摘要youtube和ovp数据集