2011-01-10 17 views
22

Rozszerzam jedną z klas SPL (standardowa biblioteka PHP) i nie mogę wywołać konstruktora nadrzędnego. Tutaj jest błąd otrzymuję:Dlaczego pojawia się błąd krytyczny podczas wywoływania konstruktora nadrzędnego?

Fatal error: Cannot call constructor

Tu jest link do dokumentacji SplQueue „s: http://www.php.net/manual/en/class.splqueue.php

tutaj jest mój kod:

$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; 

Co mogłoby zapobiec mnie z wezwaniem rodzic konstruktora?

+1

Tak z ciekawości, dlaczego rozszerzenie klasy kolejki? Co musisz zrobić, aby dekoracja nie była? – ircmaxell

Odpowiedz

40

SplQueue dziedziczy po SplDoublyLinkedList. Żadna z tych klas nie definiuje własnego konstruktora. Dlatego nie ma wyraźnego konstruktora macierzystego do wywoływania, a otrzymasz taki błąd. Dokumentacja jest trochę myląca dla tego (jak to jest w przypadku wielu klas SPL).

Aby rozwiązać ten problem, nie należy wywoływać konstruktora macierzystego.


Teraz w większości języków obiektowych, będziesz oczekiwać domyślny konstruktornazywać, jeśli nie ma wyraźnego konstruktor zadeklarowana w klasie. Ale tutaj jest haczyk: Klasy PHP nie mają domyślnych konstruktorów! Klasa ma konstruktora wtedy i tylko wtedy, gdy zdefiniowano.

W rzeczywistości, przy użyciu odbicia przeanalizować klasę stdClass widzimy, że nawet brak konstruktora:

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

Próba odzwierciedlają konstruktorów SplQueue i SplDoublyLinkedList zarówno wydajność NULL również.

Domyślam się, że gdy powiesz PHP do wystąpienia klasy, wykonuje całą wewnętrzną alokację pamięci potrzebuje nowego obiektu, a następnie szuka definicji konstruktora i wzywa go tylko jeśli definicja __construct() lub <class name>() jest znalezione. Poszedłem przyjrzeć się kodowi źródłowemu i wydaje się, że PHP po prostu świruje i umiera, gdy nie może znaleźć konstruktora do wywołania, ponieważ wyraźnie powiedziałeś mu w podklasie (zobacz zend_vm_def.h).

+4

Uwielbiam PHP. Będzie tak łatwo przejść przez wszystkie podklasy klasy nadrzędnej, kiedy zdecyduję się dodać do niej konstruktora ... – matt

16

Ten błąd jest generowany zwykle wtedy, gdy klasa parent, do której odwołuje się parent::__construct(), faktycznie nie ma funkcji __construct().

+0

Otrzymuję ten sam błąd, ale mam konstruktora w klasie nadrzędnej, czy możesz mi powiedzieć, co może być możliwym powodem? –

+0

Czy jesteś pewien, że poprawnie zaimplementowałeś funkcję w klasie nadrzędnej? '__construct()' z 2 podkreśleniami – Kristian

+0

Tak, i działało dobrze, ale nie wiem, co się stało nagle, Przestało działać i nadal rzucać krytyczny błąd 'Nie można wywołać konstruktora'. –

2

można włamać to tak:

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

ale jest bezradny.

wystarczy jawnie zadeklarować konstruktora dla każdej klasy. to właściwe zachowanie.

2

Jeśli chcesz zadzwonić do konstruktora najbliższego przodka, możesz przetoczyć się przez przodków z class_parents i sprawdzić z method_exists, jeśli ma konstruktor. Jeśli tak, zadzwoń do konstruktora; jeśli nie, kontynuuj wyszukiwanie z następnym najbliższym przodkiem.Nie tylko zapobiega nadrzędnymi konstruktora rodzica, ale także od innych przodków (w przypadku, gdy rodzic nie posiada konstruktora):

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

} 

Do ponownego użycia kodu, można również napisać ten kod w funkcji, która zwraca kod PHP być 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)); 
    } 
} 
Powiązane problemy