简体   繁体   English

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

[英]Why am I getting Fatal error when calling a parent's constructor?

I am extending one of the SPL (Standard PHP Library) classes and I am unable to call the parent's constructor. 我正在扩展其中一个SPL(标准PHP库)类,我无法调用父的构造函数。 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 以下是SplQueue文档的链接: httpSplQueue

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? 什么可以阻止我调用父的构造函数?

SplQueue inherits from SplDoublyLinkedList . SplQueue继承自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). 文档在这个文档上有点误导(就像许多SPL类一样)。

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! 但这里有一个问题: PHP类没有默认的构造函数! 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: 实际上,使用反射来分析stdClass类,我们甚至看到缺少构造函数:

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

Attempting to reflect the constructors of SplQueue and SplDoublyLinkedList both yield NULL as well. 试图反映SplQueueSplDoublyLinkedList的构造函数SplDoublyLinkedList产生NULL

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

通常,当在parent::__construct()引用的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. 这是正确的行为。

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. 如果要调用最近祖先的构造函数,可以使用class_parents循环遍历祖先,并使用method_exists检查它是否具有构造函数。 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 eval ed: 对于代码重用,您还可以将此代码编写为返回要eval的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));
  }
}

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM