php - overload - public function__construct()
¿Por qué recibo un error grave al llamar al constructor de un padre? (4)
Este error se produce, generalmente, cuando la clase parent
la que se hace referencia en parent::__construct()
realidad no tiene la función __construct()
.
Estoy extendiendo una de las clases de SPL (Biblioteca PHP estándar) y no puedo llamar al constructor del padre. Aquí está el error que estoy recibiendo:
Error fatal: no se puede llamar al constructor
Aquí hay un enlace a la documentación de SplQueue
: http://www.php.net/manual/en/class.splqueue.php
Aquí está mi código:
$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;
¿Qué podría evitar que llame al constructor de los padres?
Puedes hackearlo así:
if (in_array(''__construct'', get_class_methods(get_parent_class($this)))) {
parent::__construct();
}
pero es impotente.
simplemente declare constructor explícitamente para cada clase. Es el comportamiento correcto.
Si desea llamar al constructor del antepasado más cercano, puede recorrer los ancestros con class_parents y verificar con method_exists si tiene un constructor. Si es así, llame al constructor; Si no, continúe su búsqueda con el siguiente antepasado más cercano. No solo impides anular el constructor del padre, sino también el de otros ancestros (en caso de que el padre no tenga un 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'';
}
}
Para reutilizar el código, también puede escribir este código como una función que devuelve el código PHP para ser 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));
}
}
SplQueue
hereda de SplDoublyLinkedList
. Ninguna de estas clases define un constructor propio. Por lo tanto, no hay un constructor padre explícito al que llamar, y se produce un error de este tipo. La documentación es un poco engañosa en este caso (como lo es para muchas clases de SPL).
Para resolver el error, no llame al constructor principal.
Ahora, en la mayoría de los lenguajes orientados a objetos, esperará que se llame al constructor predeterminado si no hay un constructor explícito declarado en una clase. Pero aquí está el truco: ¡las clases de PHP no tienen constructores por defecto! Una clase tiene un constructor si y solo si uno está definido .
De hecho, al utilizar la reflexión para analizar la clase stdClass
, vemos incluso que carece de un constructor:
$c = new ReflectionClass(''stdClass'');
var_dump($c->getConstructor()); // NULL
Intentando reflejar los constructores de SplQueue
y SplDoublyLinkedList
ambos producen NULL
también.
Supongo que cuando le dices a PHP que cree una instancia de una clase, realiza toda la asignación de memoria interna que necesita para el nuevo objeto, luego busca una definición de constructor y la llama solo si la definición de __construct()
o <class name>()
se encuentra. Fui a echar un vistazo al código fuente, y parece que PHP simplemente se asusta y muere cuando no puede encontrar un constructor al que llamar porque lo dijiste explícitamente en una subclase (consulta zend_vm_def.h
).