PHP类构造函数中的范围展开

Ker*_* SB 10 php exception stack-unwinding

我正在学习PHP类和异常,并且,从C++背景来看,以下内容让我感到奇怪:

当派生类的构造函数抛出异常时,似乎基类的析构函数不会自动运行:

class Base
{
  public function __construct() { print("Base const.\n"); }
  public function __destruct()  { print("Base destr.\n"); }
}

class Der extends Base
{
  public function __construct()
  {
    parent::__construct();
    $this->foo = new Foo;
    print("Der const.\n");
    throw new Exception("foo"); // #1
  }
  public function __destruct()  { print("Der destr.\n"); parent::__destruct(); }
  public $foo;                  // #2
}

class Foo
{
  public function __construct() { print("Foo const.\n"); }
  public function __destruct()  { print("Foo destr.\n"); }
}


try {
  $x = new Der;
} catch (Exception $e) {
}
Run Code Online (Sandbox Code Playgroud)

这打印:

Base const.
Foo const.
Der const.
Foo destr.
Run Code Online (Sandbox Code Playgroud)

另一方面,如果构造函数(at )中存在异常,则会正确执行成员对象的析构函数#1.现在我想知道:如何在PHP的类层次结构中实现正确的范围展开,以便在发生异常时正确销毁子对象?

此外,似乎所有成员对象都被销毁(at #2)之后无法运行基础析构函数.也就是说,如果我们删除行#1,我们得到:

Base const.
Foo const.
Der const.
Der destr.
Base destr.
Foo destr.    // ouch!!
Run Code Online (Sandbox Code Playgroud)

如何解决这个问题?

更新:我仍然愿意接受进一步的贡献.如果某人有充分的理由为什么PHP对象系统永远不需要正确的破坏序列,我会给出另一个赏金(或者只是为了任何其他令人信服的争论答案).

Nik*_*kiC 6

我想解释为什么PHP以这种方式运行以及为什么它实际上使(某些)有意义.

在PHP中,只要没有对象的引用,对象就会被销毁.可以通过多种方式移除引用,例如通过unset()变量,通过保留范围或作为关闭的一部分.

如果你理解了这一点,你可以很容易地理解这里发生了什么(我将首先解释没有异常的情况):

  1. PHP进入关闭状态,因此删除了所有变量引用.
  2. 当删除由$x(对实例Der)创建的引用时,对象将被销毁.
  3. 调用派生的析构函数,它调用基础析构函数.
  4. 现在删除$this->foo了对Foo实例的引用(作为销毁成员字段的一部分.)
  5. 没有任何更多引用Foo,因此它也被销毁并且析构函数被调用.

想象一下,这不会以这种方式工作,并且会在调用析构函数之前销毁成员字段:您无法在析构函数中访问它们.我严重怀疑在C++中存在这样的行为.

在Exception的情况下,您需要了解对于PHP,从来没有真正存在类的实例,因为构造函数永远不会返回.你怎么能破坏从未构建过的东西?


我如何解决它?

你没有.你需要一个析构函数这一事实可能是设计糟糕的表现.而破坏秩序对你很重要的事实甚至更多.