2011-01-10 24 views
22

Estoy extendiendo una de las clases SPL (Biblioteca PHP Estándar) y no puedo llamar al constructor principal. Aquí está el error que estoy recibiendo:¿Por qué recibo un error fatal al llamar al constructor de un padre?

Fatal error: Cannot call constructor

Aquí hay un enlace a la documentación del SplQueue 's: 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 impedir que me llamando al el constructor de los padres?

+1

Solo por curiosidad, ¿por qué estás ampliando la clase de cola? ¿Qué necesitas hacer para decorar? – ircmaxell

Respuesta

40

SplQueue hereda de SplDoublyLinkedList. Ninguna de estas clases define un constructor propio. Por lo tanto, no hay un constructor padre explícito para llamar, y obtienes ese error. 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 padre.


Ahora, en la mayoría de los lenguajes orientados a objetos, que se puede esperar el constructor por defecto a ser llamado 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 se define.

De hecho, utilizando la reflexión para analizar la clase stdClass, vemos incluso que carece de un constructor:

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

El intento de reflejar los constructores de SplQueue y SplDoublyLinkedList tanto el rendimiento NULL también.

Mi conjetura es que cuando le dices a PHP para crear instancias de una clase, se realiza toda la asignación de memoria interna que necesita para el nuevo objeto, a continuación, busca una definición constructor y lo llama sólo si una definición de __construct() o <class name>() es encontrado. Fui a echar un vistazo al código fuente, y parece que PHP se asusta y muere cuando no puede encontrar un constructor para llamar porque se lo dijo explícitamente en una subclase (ver zend_vm_def.h).

+4

Me encanta PHP. Será tan fácil pasar por todas las subclases de la clase principal cuando decida agregarle constructor ... – matt

16

Este error se produce, generalmente, cuando la clase parent a la que se hace referencia en parent::__construct() en realidad no tiene la función __construct().

+0

Recibo el mismo error, pero tengo un constructor en mi clase principal, ¿puede decirme qué puede hacer? ser la razón posible? –

+0

¿Estás seguro de haber implementado correctamente la función en la clase principal? '__construct()' con 2 caracteres de subrayado – Kristian

+0

Sí, y funcionó bien, pero no sé lo que sucedió de repente, dejó de funcionar y sigue arrojando el error fatal 'No se puede llamar al constructor'. –

2

Es posible cortar de esta manera:

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

pero es impotente.

acaba de declarar constructor explícitamente para cada clase. es el comportamiento correcto.

2

Si desea llamar al constructor del antecesor más cercano, puede recorrer los antepasados ​​con class_parents y consultar con method_exists si tiene un constructor. Si es así, llame al constructor; si no, continúa tu búsqueda con el antecesor más cercano.No sólo se puede prevenir anulando el constructor de los padres, sino también la de otros antepasados ​​(en caso de que el padre no tiene 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 la reutilización de código, también se puede escribir este código como una función que devuelve el código PHP para ser eval ed:

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