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

时间:2011-01-10 19:17:34

标签: php oop constructor spl

我正在扩展其中一个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;

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

5 个答案:

答案 0 :(得分:43)

SplQueue继承自SplDoublyLinkedList。这些类都没有定义自己的构造函数。因此,没有明确的父构造函数可以调用,而且会出现这样的错误。文档在这个文档上有点误导(就像许多SPL类一样)。

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


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

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

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

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

我的猜测是,当你告诉PHP实例化一个类时,它会执行新对象所需的所有内部内存分配,然后查找构造函数定义并仅在定义时调用已找到__construct()<class name>()。我去看看源代码,看起来PHP只是因为你在子类中明确地告诉它而无法找到要调用的构造函数而变得怪异而死(参见zend_vm_def.h)。 / p>

答案 1 :(得分:21)

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

答案 2 :(得分:2)

你可能会这样破解:

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

但是无助。

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

答案 3 :(得分: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';
  }

}

对于代码重用,您还可以将此代码编写为返回PHP代码为eval的函数:

// 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));
  }
}

答案 4 :(得分:0)

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

如果仍然需要调用构造函数,则可以执行此操作。

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