为什么在调用父级构造函数时会出现致命错误?

发布于 2024-10-11 14:44:16 字数 564 浏览 7 评论 0原文

我正在扩展 SPL(标准 PHP 库)类之一,但无法调用父类的构造函数。这是我收到的错误:

致命错误:无法调用构造函数

以下是 SplQueue 文档的链接:http://www.php.net/manual/en/class.splqueue.php

这是我的代码:

$queue = new Queue();

class Queue extends SplQueue {

    public function __construct() {
        echo 'before';
        parent::__construct();
        echo 'I have made it after the parent constructor call';
    }

}

exit;

什么可以阻止我调用父级的构造函数?

I am extending one of the SPL (Standard PHP Library) classes and I am unable to call the parent's constructor. Here is the error I am getting:

Fatal error: Cannot call constructor

Here is a link to the SplQueue's documentation: http://www.php.net/manual/en/class.splqueue.php

Here is my code:

$queue = new Queue();

class Queue extends SplQueue {

    public function __construct() {
        echo 'before';
        parent::__construct();
        echo 'I have made it after the parent constructor call';
    }

}

exit;

What could prevent me from calling the parent's constructor?

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

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

发布评论

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

评论(5

于我来说 2024-10-18 14:44:16

SplQueue 继承自SplDoublyLinkedList。这些类都没有定义自己的构造函数。因此,没有显式的父构造函数可供调用,并且您会收到这样的错误。该文档对此有一点误导(因为它对于许多 SPL 类来说)。

要解决该错误,请不要调用父构造函数。


现在,在大多数面向对象的语言中,如果类中没有声明显式构造函数,您将期望调用默认构造函数。但这里有一个问题:PHP 类没有默认构造函数!当且仅当定义了一个构造函数时,类才具有构造函数。

事实上,使用反射来分析 stdClass 类,我们甚至发现它缺少构造函数:

$c = new ReflectionClass('stdClass');
var_dump($c->getConstructor()); // NULL

尝试反射 SplQueueSplDoublyLinkedList 的构造函数也产生NULL

我的猜测是,当您告诉 PHP 实例化一个类时,它会执行新对象所需的所有内部内存分配,然后查找构造函数定义并调用它仅当定义为__construct() 或 ()。我去查看了源代码,似乎 PHP 在找不到要调用的构造函数时会崩溃并死掉,因为您在子类中明确地告诉了它(请参阅 zend_vm_def.hzend_vm_def.h)代码>)。

SplQueue inherits from SplDoublyLinkedList. Neither of these classes defines a constructor of its own. Therefore there's no explicit parent constructor to call, and you get such an error. The documentation is a little misleading on this one (as it is for many SPL classes).

To solve the error, don't call the parent constructor.


Now, in most object-oriented languages, you'll expect the default constructor to be called if there isn't an explicit constructor declared in a class. But here's the catch: PHP classes don't have default constructors! A class has a constructor if and only if one is defined.

In fact, using reflection to analyze the stdClass class, we see even that lacks a constructor:

$c = new ReflectionClass('stdClass');
var_dump($c->getConstructor()); // NULL

Attempting to reflect the constructors of SplQueue and SplDoublyLinkedList both yield NULL as well.

My guess is that when you tell PHP to instantiate a class, it performs all the internal memory allocation it needs for the new object, then looks for a constructor definition and calls it only if a definition of __construct() or <class name>() is found. I went to take a look at the source code, and it seems that PHP just freaks out and dies when it can't find a constructor to call because you told it explicitly to in a subclass (see zend_vm_def.h).

影子是时光的心 2024-10-18 14:44:16

通常,当 parent::__construct() 中引用的 parent 类实际上没有 __construct() 函数时,就会引发此错误。

This error gets thrown, usually, when the parent class being referenced in parent::__construct() actually has no __construct() function.

梦萦几度 2024-10-18 14:44:16

如果要调用最近祖先的构造函数,可以使用 循环祖先class_parents 并检查 method_exists 是否有构造函数。如果是,则调用构造函数;如果没有,则继续搜索下一个最近的祖先。您不仅可以防止覆盖父级的构造函数,还可以防止覆盖其他祖先的构造函数(如果父级没有构造函数):

class Queue extends SplQueue {

  public function __construct() {
    echo 'before';

    // loops through all ancestors
    foreach(class_parents($this) as $ancestor) {

      // check if constructor has been defined
      if(method_exists($ancestor, "__construct")) {

        // execute constructor of ancestor
        eval($ancestor."::__construct();");

        // exit loop if constructor is defined
        // this avoids calling the same constructor twice
        // e.g. when the parent's constructor already
        // calls the grandparent's constructor
        break;
      }
    }
    echo 'I have made it after the parent constructor call';
  }

}

为了代码重用,您还可以将此代码编写为函数,将 PHP 代码返回为 <代码>评估:

// define function to be used within various classes
function get_parent_construct($obj) {

  // loop through all ancestors
  foreach(class_parents($obj) as $ancestor) {

    // check if constructor has been defined
    if(method_exists($ancestor, "__construct")) {

      // return PHP code (call of ancestor's constructor)
      // this will automatically break the loop
      return $ancestor."::__construct();";
    }
  }
}

class Queue extends SplQueue {

  public function __construct() {
    echo 'before';

    // execute the string returned by the function
    // eval doesn't throw errors if nothing is returned
    eval(get_parent_construct($this));
    echo 'I have made it after the parent constructor call';
  }
}

// another class to show code reuse
class AnotherChildClass extends AnotherParentClass {

  public function __construct() {
    eval(get_parent_construct($this));
  }
}

If you want to call the constructor of the nearest ancestor, you can loop through the ancestors with class_parents and check with method_exists if it has a constructor. If so, call the constructor; if not, continue your search with the next nearest ancestor. Not only do you prevent overriding the parent's constructor, but also that of other ancestors (in case the parent doesn't have a constructor):

class Queue extends SplQueue {

  public function __construct() {
    echo 'before';

    // loops through all ancestors
    foreach(class_parents($this) as $ancestor) {

      // check if constructor has been defined
      if(method_exists($ancestor, "__construct")) {

        // execute constructor of ancestor
        eval($ancestor."::__construct();");

        // exit loop if constructor is defined
        // this avoids calling the same constructor twice
        // e.g. when the parent's constructor already
        // calls the grandparent's constructor
        break;
      }
    }
    echo 'I have made it after the parent constructor call';
  }

}

For code reuse, you could also write this code as a function that returns the PHP code to be evaled:

// define function to be used within various classes
function get_parent_construct($obj) {

  // loop through all ancestors
  foreach(class_parents($obj) as $ancestor) {

    // check if constructor has been defined
    if(method_exists($ancestor, "__construct")) {

      // return PHP code (call of ancestor's constructor)
      // this will automatically break the loop
      return $ancestor."::__construct();";
    }
  }
}

class Queue extends SplQueue {

  public function __construct() {
    echo 'before';

    // execute the string returned by the function
    // eval doesn't throw errors if nothing is returned
    eval(get_parent_construct($this));
    echo 'I have made it after the parent constructor call';
  }
}

// another class to show code reuse
class AnotherChildClass extends AnotherParentClass {

  public function __construct() {
    eval(get_parent_construct($this));
  }
}
岛徒 2024-10-18 14:44:16

你可以像这样破解它:

if (in_array('__construct', get_class_methods(get_parent_class($this)))) {
    parent::__construct();
}

但它是无助的。

只需为每个类显式声明构造函数即可。这是正确的行为。

You may hack it like this:

if (in_array('__construct', get_class_methods(get_parent_class($this)))) {
    parent::__construct();
}

but it's helpless.

just declare constructor explicitly for every class. it's the right behavior.

酷到爆炸 2024-10-18 14:44:16

我遇到了同样的错误。我通过在父类中定义一个空构造函数解决了这个问题。这样其他类就不必定义它。我认为这是更干净的方法。

如果您仍然需要调用构造函数,您可以这样做。

if (is_callable('parent::__construct')) {
    parent::__construct();
}

I got the same error. I have solved it by defining an empty constructor in the parent class. That way other classes don't have to define it. I think it's cleaner approach.

If you still need to call the constructor you can do this.

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