Why am I getting Fatal error when calling a parent's constructor?

29,907

Solution 1

SplQueue inherits from SplDoublyLinkedList. Neither of these classes defines a constructor of its own. Therefore there's no explicit parent constructor to call, and you get such an error. The documentation is a little misleading on this one (as it is for many SPL classes).

To solve the error, don't call the parent constructor.


Now, in most object-oriented languages, you'll expect the default constructor to be called if there isn't an explicit constructor declared in a class. But here's the catch: PHP classes don't have default constructors! A class has a constructor if and only if one is defined.

In fact, using reflection to analyze the stdClass class, we see even that lacks a constructor:

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

Attempting to reflect the constructors of SplQueue and SplDoublyLinkedList both yield NULL as well.

My guess is that when you tell PHP to instantiate a class, it performs all the internal memory allocation it needs for the new object, then looks for a constructor definition and calls it only if a definition of __construct() or <class name>() is found. I went to take a look at the source code, and it seems that PHP just freaks out and dies when it can't find a constructor to call because you told it explicitly to in a subclass (see zend_vm_def.h).

Solution 2

This error gets thrown, usually, when the parent class being referenced in parent::__construct() actually has no __construct() function.

Solution 3

You may hack it like this:

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

but it's helpless.

just declare constructor explicitly for every class. it's the right behavior.

Solution 4

If you want to call the constructor of the nearest ancestor, you can loop through the ancestors with class_parents and check with method_exists if it has a constructor. If so, call the constructor; if not, continue your search with the next nearest ancestor. Not only do you prevent overriding the parent's constructor, but also that of other ancestors (in case the parent doesn't have a constructor):

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

}

For code reuse, you could also write this code as a function that returns the PHP code to be evaled:

// 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));
  }
}

Solution 5

I got the same error. I have solved it by defining an empty constructor in the parent class. That way other classes don't have to define it. I think it's cleaner approach.

If you still need to call the constructor you can do this.

if (is_callable('parent::__construct')) {
    parent::__construct();
}
Share:
29,907
Admin
Author by

Admin

Updated on July 31, 2020

Comments

  • Admin
    Admin almost 4 years

    I am extending one of the SPL (Standard PHP Library) classes and I am unable to call the parent's constructor. Here is the error I am getting:

    Fatal error: Cannot call constructor

    Here is a link to the SplQueue's documentation: http://www.php.net/manual/en/class.splqueue.php

    Here is my 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;
    

    What could prevent me from calling the parent's constructor?