32

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

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

这是SplQueue's 文档的链接: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;

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

4

5 回答 5

52

SplQueue继承自SplDoublyLinkedList。这些类都没有定义自己的构造函数。因此没有显式的父构造函数可以调用,你会得到这样的错误。该文档在这方面有点误导(因为它适用于许多 SPL 类)。

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


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

实际上,使用反射来分析stdClass类,我们甚至可以看到缺少构造函数:

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

试图反映构造函数SplQueueSplDoublyLinkedList两者的构造函数NULL

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

于 2011-01-10T19:26:20.310 回答
24

通常,当parent被引用的类parent::__construct()实际上没有__construct()功能时,就会抛出这个错误。

于 2012-07-26T20:42:33.107 回答
2

你可以像这样破解它:

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

但它是无助的。

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

于 2015-09-11T02:10:23.237 回答
2

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

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

if (is_callable('parent::__construct')) {
    parent::__construct();
}
于 2019-07-31T06:49:11.040 回答
2

如果要调用最近祖先的构造函数,可以使用 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';
  }

}

对于代码重用,您还可以将此代码编写为返回要eval编辑的 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));
  }
}
于 2015-10-30T00:16:10.893 回答