2011-01-10 2 views
22

Я расширяю один из классов SPL (стандартная библиотека PHP), и я не могу вызвать конструктор родителя. Здесь ошибка я получаю:Почему я получаю Неустранимая ошибка при вызове конструктора родителя?

Fatal error: Cannot call constructor

Вот ссылка на SplQueue «s документации: http://www.php.net/manual/en/class.splqueue.php

Вот мой код:

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

Что может помешать мне называя родительский конструктор?

+1

Просто из любопытства, почему вы расширяете класс очереди? Что вам нужно сделать, чтобы украсить не будет? – ircmaxell

ответ

40

SplQueueследует от SplDoublyLinkedList. Ни один из этих классов не определяет собственный конструктор. Поэтому нет явного родительского конструктора для вызова, и вы получаете такую ​​ошибку. Документация немного вводит в заблуждение на этом (как и для многих классов SPL).

Чтобы решить эту ошибку, не вызывайте родительский конструктор.


Теперь, в большинстве объектно-ориентированных языков программирования, вы будете ожидать конструктор по умолчанию называться, если не явный конструктор, объявленный в классе. Но вот улов: PHP-классы не имеют конструкторов по умолчанию! Класс имеет конструктор тогда и только тогда, когда он определен.

В самом деле, с помощью отражения для анализа stdClass класса, мы видим, даже то, что не хватает конструктора:

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

Попытка отразить конструкторы SplQueue и SplDoublyLinkedList оба дают NULL, а также.

Я думаю, что когда вы говорите PHP, чтобы создать экземпляр класса, он выполняет все внутренние выделения памяти, необходимые для нового объекта, а затем ищет определение конструктора и называет его только если определения __construct() или <class name>() найден. Я пошел взглянуть на исходный код, и кажется, что PHP просто волнуется и умирает, когда он не может найти конструктор для вызова, потому что вы явно сказали его в подклассе (см. zend_vm_def.h).

+4

Я люблю PHP. Это будет так легко пройти через все подклассы родительского класса, когда я решит добавить к нему конструктор ... – matt

16

Эта ошибка возникает, как правило, когда класс parent ссылается на parent::__construct(), фактически не имеет функции __construct().

+0

Я получаю ту же ошибку, но у меня есть конструктор в моем родительском классе, можете ли вы сказать мне, что может быть возможной причиной? –

+0

Вы уверены, что правильно реализовали функцию в родительском классе? '__construct()' с 2 символами подчеркивания – Kristian

+0

Да, и он работал нормально, но не знаю, что произошло внезапно. Он перестает работать и продолжает бросать фатальную ошибку 'Can not call constructor'. –

2

Вы можете взломать его, как это:

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

но беспомощен.

просто объявить конструктор явно для каждого класса. это правильное поведение.

2

Если вы хотите вызвать конструктор ближайшего предка, вы можете прокрутить предков с помощью 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'; 
    } 

} 

Для повторного использования кода, можно было бы написать этот код в качестве функции, возвращает код PHP, который должен быть 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)); 
    } 
} 
Смежные вопросы