Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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).

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

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