【问题标题】:Why am I getting Fatal error when calling a parent's constructor?为什么在调用父级的构造函数时会出现致命错误?
【发布时间】:2011-06-06 17:42:52
【问题描述】:

我正在扩展 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;

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

【问题讨论】:

  • 只是出于好奇,你为什么要扩展队列类?你需要做什么装饰不会?

标签: php oop constructor spl


【解决方案1】:

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)。

【讨论】:

  • 我喜欢 PHP。当我决定向它添加构造函数时,遍历父类的所有子类将非常容易......
  • @matt,即使有一个可选的默认构造函数,你也必须这样做。因为它是可选的,所以您的开发人员会在某些情况下调用它,而在其他情况下他们不会这样做......或者您是否建议强制使用“默认构造函数”?强制构造函数是一大堆蠕虫......
【解决方案2】:

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

【讨论】:

  • 我遇到了同样的错误,但是我的父类中有构造函数,你能告诉我可能的原因是什么吗?
  • 你确定你在父类中正确实现了函数吗? __construct() 带 2 个下划线
  • 是的,它工作正常,但不知道突然发生了什么,它停止工作并继续抛出致命错误Cannot call constructor
  • 您确定这两个文件仍然可以正常加载吗?作为文件?
  • 是的,我很确定。
【解决方案3】:

你可以这样破解它:

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

但又无可奈何。

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2017-08-29
        • 2016-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-07
        • 1970-01-01
        • 1970-01-01
        • 2020-08-21
        相关资源
        最近更新 更多