Почему я получаю фатальную ошибку при вызове конструктора родителя? - PullRequest
27 голосов
/ 10 января 2011

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

Неустранимая ошибка: не удается вызвать конструктор

Вот ссылка на документацию SplQueue: 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;

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

Ответы [ 4 ]

44 голосов
/ 10 января 2011

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

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


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

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

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

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

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

21 голосов
/ 27 июля 2012

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

2 голосов
/ 30 октября 2015

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

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

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

, но это беспомощно.

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

...