2011-01-10 3 views
22

Ich erweitere eine der SPL (Standard PHP Library) -Klassen und ich kann den Elternkonstruktor nicht aufrufen. Hier ist der Fehler Ich erhalte:Warum erhalte ich einen schwerwiegenden Fehler beim Aufruf des Konstruktors eines Elternteils?

Fatal error: Cannot call constructor

Hier ist ein Link auf die SplQueue ‚s Dokumentation: http://www.php.net/manual/en/class.splqueue.php

Hier ist mein 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; 

Was mich ruft die verhindern könnten Elternkonstruktor?

+1

Nur aus Neugier, warum erweitern Sie die Warteschlange Klasse? Was brauchst du, um dieses Dekorieren nicht zu machen? – ircmaxell

Antwort

40

SplQueue erbt von SplDoublyLinkedList. Keine dieser Klassen definiert einen eigenen Konstruktor. Daher gibt es keinen expliziten übergeordneten Konstruktor, der aufgerufen wird, und Sie erhalten einen solchen Fehler. Die Dokumentation ist ein wenig irreführend (wie es für viele SPL-Klassen ist).

Um den Fehler zu beheben, rufen Sie nicht den übergeordneten Konstruktor auf.


, nun in den meisten objektorientierten Sprachen, werden Sie die Standardkonstruktors erwarten genannt zu werden, wenn es nicht explizit Konstruktor in einer Klasse deklariert ist. Aber hier ist der Haken: PHP-Klassen haben keine Standardkonstruktoren! Eine Klasse hat einen Konstruktor wenn und nur wenn man definiert ist.

In der Tat, Reflexion mit der stdClass Klasse zu analysieren, sehen wir auch, dass ein Konstruktor fehlt:

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

Versuch, die Konstrukteure von SplQueue und SplDoublyLinkedList beide ergeben NULL als auch zu reflektieren.

Meine Vermutung ist, dass, wenn Sie PHP sagen, eine Klasse zu instanziieren, es die ganze interne Speicherzuweisung führt er für das neue Objekt braucht, sucht dann nach einem Konstruktor Definition und nennt es nur, wenn eine Definition von __construct() oder <class name>() gefunden. Ich habe mir den Quellcode angesehen und es scheint, dass PHP einfach ausrastet und stirbt, wenn es keinen Konstruktor zum Aufruf findet, weil Sie es explizit in einer Unterklasse angegeben haben (siehe zend_vm_def.h).

+4

Ich liebe PHP. Es wird so einfach sein, durch alle Unterklassen der Elternklasse zu gehen, wenn ich beschließe, ihm einen Konstruktor hinzuzufügen ... – matt

16

Dieser Fehler wird normalerweise ausgelöst, wenn die parent-Klasse, auf die in parent::__construct() verwiesen wird, tatsächlich keine __construct()-Funktion hat.

+0

Ich bekomme den gleichen Fehler, aber ich habe Konstruktor in meiner Elternklasse, können Sie mir sagen, was kann der mögliche Grund sein? –

+0

Sind Sie sicher, dass Sie die Funktion in der Elternklasse korrekt implementiert haben? '__construct()' mit 2 Unterstrichen – Kristian

+0

Ja, und es funktionierte gut, aber ich weiß nicht, was plötzlich passiert ist. Es hörte auf zu arbeiten und warf den fatalen Fehler 'Kann Konstruktor nicht aufrufen 'fort. –

2

Sie können es so hacken:

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

aber es ist hilflos.

deklarieren Sie Konstruktor explizit für jede Klasse. es ist das richtige Verhalten.

2

Wenn Sie den Konstruktor des nächsten Vorfahren aufrufen möchten, können Sie die Vorfahren mit class_parents durchlaufen und mit method_exists überprüfen, ob es einen Konstruktor hat. Wenn ja, rufen Sie den Konstruktor auf; Wenn nicht, setzen Sie Ihre Suche mit dem nächsten Vorfahren fort.Nicht nur verhindern, dass Sie der Eltern Konstruktor überschreiben, sondern auch die der anderen Vorfahren (falls die Eltern keinen Konstruktor hat):

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'; 
    } 

} 

Für die Wiederverwendung von Code, könnten Sie auch diesen Code als eine Funktion schreiben, die gibt den PHP-Code zurück 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)); 
    } 
} 
Verwandte Themen