php 为什么在调用父级的构造函数时会出现致命错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4650542/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why am I getting Fatal error when calling a parent's constructor?
提问by Tonto McGee
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:
我正在扩展 SPL(标准 PHP 库)类之一,但无法调用父级的构造函数。这是我得到的错误:
Fatal error: Cannot call constructor
致命错误:无法调用构造函数
Here is a link to the SplQueue
's documentation: http://www.php.net/manual/en/class.splqueue.php
这是SplQueue
's 文档的链接: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?
什么可以阻止我调用父的构造函数?
回答by BoltClock
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).
SplQueue
继承自SplDoublyLinkedList
. 这些类都没有定义自己的构造函数。因此,没有要调用的显式父构造函数,您会收到这样的错误。文档对此有点误导(就像许多 SPL 类一样)。
To solve the error, don't call the parent constructor.
要解决错误,请不要调用父构造函数。
Now, in most object-oriented languages, you'll expect the default constructorto 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.
现在,在大多数面向对象的语言中,如果没有在类中声明显式构造函数,您将期望调用默认构造函数。但这里有一个问题:PHP 类没有默认构造函数!一个类有一个构造函数,当且仅当定义了一个。
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.
试图反映的构造函数SplQueue
和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 ifa 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
).
我的猜测是,当您告诉 PHP 实例化一个类时,它会执行新对象所需的所有内部内存分配,然后查找构造函数定义并仅在找到__construct()
或的定义时才调用它<class name>()
。我去看了看源代码,当 PHP 找不到要调用的构造函数时,它似乎吓坏了,死掉了,因为您在子类中明确告诉它要调用(请参阅 参考资料zend_vm_def.h
)。
回答by Kristian
This error gets thrown, usually, when the parent
class being referenced in parent::__construct()
actually has no __construct()
function.
通常,当parent
被引用的类parent::__construct()
实际上没有__construct()
功能时,会抛出此错误。
回答by simonkuang
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.
只需为每个类显式声明构造函数。这是正确的行为。
回答by Alexander Jank
If you want to call the constructor of the nearest ancestor, you can loop through the ancestors with class_parentsand check with method_existsif 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_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';
}
}
For code reuse, you could also write this code as a function that returns the PHP code to be eval
ed:
为了代码重用,您还可以将此代码编写为返回要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));
}
}
回答by Svetoslav Marinov
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();
}