How do I get a PHP class constructor to call its parent's parent's constructor?

291,711

Solution 1

The ugly workaround would be to pass a boolean param to Papa indicating that you do not wish to parse the code contained in it's constructor. i.e:

// main class that everything inherits
class Grandpa 
{
    public function __construct()
    {

    }

}

class Papa extends Grandpa
{
    public function __construct($bypass = false)
    {
        // only perform actions inside if not bypassing
        if (!$bypass) {

        }
        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        $bypassPapa = true;
        parent::__construct($bypassPapa);
    }
}

Solution 2

You must use Grandpa::__construct(), there's no other shortcut for it. Also, this ruins the encapsulation of the Papa class - when reading or working on Papa, it should be safe to assume that the __construct() method will be called during construction, but the Kiddo class does not do this.

Solution 3

class Grandpa 
{
    public function __construct()
    {}
}

class Papa extends Grandpa
{
    public function __construct()
    {
        //call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        //this is not a bug, it works that way in php
        Grandpa::__construct();
    }
}

Solution 4

Beautiful solution using Reflection.

<?php
class Grandpa 
{
    public function __construct()
    {
        echo "Grandpa's constructor called\n";
    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        echo "Papa's constructor called\n";

        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        echo "Kiddo's constructor called\n";

        $reflectionMethod = new ReflectionMethod(get_parent_class(get_parent_class($this)), '__construct');
        $reflectionMethod->invoke($this);
    }
}

$kiddo = new Kiddo();
$papa = new Papa();

Solution 5

I ended up coming up with an alternative solution that solved the problem.

  • I created an intermediate class that extended Grandpa.
  • Then both Papa and Kiddo extended that class.
  • Kiddo required some intermediate functionality of Papa but didn't like it's constructor so the class has that additional functionality and both extend it.

I've upvoted the other two answers that provided valid yet ugly solutions for an uglier question:)

Share:
291,711

Related videos on Youtube

Paulo
Author by

Paulo

I am a LAMP-stack developer with 15 years in the industry. I am one of the two owners of Luscious Orange, a design and development firm based out of Winnipeg Canada.

Updated on September 25, 2020

Comments

  • Paulo
    Paulo over 3 years

    I need to have a class constructor in PHP call its parent's parent's (grandparent?) constructor without calling the parent constructor.

    // main class that everything inherits
    class Grandpa 
    {
        public function __construct()
        {
    
        }
    
    }
    
    class Papa extends Grandpa
    {
        public function __construct()
        {
            // call Grandpa's constructor
            parent::__construct();
        }
    }
    
    class Kiddo extends Papa
    {
        public function __construct()
        {
            // THIS IS WHERE I NEED TO CALL GRANDPA'S
            // CONSTRUCTOR AND NOT PAPA'S
        }
    }
    

    I know this is a bizarre thing to do and I'm attempting to find a means that doesn't smell bad but nonetheless, I'm curious if it's possible.

    • Mark
      Mark over 14 years
      Is there a reason you can't just instantiate Grandpa inside of Kiddo's construct?
    • Paulo
      Paulo over 14 years
      The Grampa constructor sets properties for itself that are inherited by its children. Papa does some stuff in it's constructor that will mess up Kiddo. So I need the call to Grandpa constructor to set properties for Kiddo during construction.
    • MitMaro
      MitMaro over 14 years
      Then shouldn't Kiddo extend from Grandpa and not Papa.
    • Paulo
      Paulo over 14 years
      @MitMaro. I agree and I actually solved my actual problem by creating an intermediate class that extended Grandpa. Then both Papa and Kiddo extended that class. Kiddo required some intermediate functionality of Papa but didn't like it's constructor so the class has that additional functionality and both extend it.
    • MitMaro
      MitMaro over 14 years
      A perfect solution. You should post it as an answer.
    • Paulo
      Paulo over 11 years
      Changed the accepted answer to the one that truly resolves the question being asked.
    • bpoiss
      bpoiss about 6 years
      what if the grandparent class is abstract?
  • Mark
    Mark over 14 years
    Can't understand how. Declaring __construct as static results in the following error for me "Fatal error: Constructor Grandpa::__construct() cannot be static" under PHP5.3
  • Paulo
    Paulo over 14 years
    When I tried it, I didn't declare it as static. I created the class structure normally but instead of calling parent::__construct(), I called Grandpa::__construct() and it worked. I doesn't seem right to me either but this whole question got kinda weird.
  • xximjasonxx
    xximjasonxx almost 14 years
    I think the better idea here is to break the functionality you are trying to use out of the constructed and into a protected method. Then you can call that method from a constructor selectively
  • JCC
    JCC over 12 years
    Agreed. I use this all the time - you can call a class by it's name, not just via the magic names of parent or self. I have been known to go three or four classes deep. In fact, I've started referring to my base class by it's name rather than using parent, that way I'm sure I'm getting the right object, always.
  • Iain Collins
    Iain Collins about 12 years
    Personally I wouldn't choose to do this as it means Papa's contractor won't get called at all. I'd go with something like cballou's approach (i.e. of passing an argument to Papa's constructor and having it invoke it's parents constructor or not based on that).
  • nalply
    nalply over 11 years
    This does not answer the exact question you've phrased. This happens if the real world muddies up something which should be clear and confined. It's a pity for this question.
  • Kostanos
    Kostanos almost 11 years
    good workaround, but it is not acceptable if the parent class comes from some external library wish you with to extend. I like the too much php answer below.
  • mimarcel
    mimarcel almost 11 years
    The situation we are in here is in such a way that we need to skip the parent's logic and in most of the cases we can't change the grandparent or the parent classes. I believe this is the best way to do it as there are changes made only in the child. The only issue is that it might throw an E_STRICT notice link, it didn't for me though when I tested it.
  • llange
    llange over 8 years
    Just remember that parent is only a shortcut to whatever first parent that implemented the method… Thus from a descendant calling AscendantName::method works and from an ascendant calling static::method will always call the latest generation that implemented the method. And you may check not to call yourself using (get_class($this)==__CLASS__?'Healthy people dont\'t call themself…':'Calling latest') if you'd like parents to call kid methods (seems strange but might be usefull combined with private function and __call magic function )…
  • nicksona
    nicksona over 7 years
    EvilChookie`s solusion is the best. Should be the approved answer. Than you Chookie!
  • SparK
    SparK over 7 years
    In php when you call a function using static operator :: from an object context it will be called using the object context which is completely fkd-up. At least in JS (about the this madness) it uses the callee context.
  • v010dya
    v010dya over 6 years
    This is an interesting solution, however, if you really don't need the logic of the parent's constructor, are you sure that you are really making a subsclass of a parent?
  • Travis Weston
    Travis Weston over 6 years
    @Wouter0100 lala977 did say "From PHP 7" which your link is using 5.6
  • Wouter0100
    Wouter0100 over 6 years
    @TravisWeston please recheck my link. it tests that peace of code against all versions. 5.6 up to 7.2 reproduce the same error, while other runtimes return some different errors.
  • Halfstop
    Halfstop about 6 years
    This is the correct answer. While it may seem silly to inherit Papa but you want to call the GrandPa constructor without Papa, I've found it useful to be do. I want to keep the hierarchy, but I need to do a clean Kiddo constructor that doesn't have anything to do with Papa, but still want the benefits of using what's going on in GrandPa's constructor. Maybe Papa is doing a bunch of junk in the constructor that isn't needed or wanted by Kiddo, but it still has useful components.
  • Pedro
    Pedro over 4 years
    That's actually quite clever. In my implementation I do if($bypass) return; & can position it so some important stuff gets done before the bypass.
  • Tranzium
    Tranzium over 3 years
    @SparK If you are using PHP 5.3.0 or later, Late Static Bindings would most likely solve your use case.
  • iKev61
    iKev61 over 2 years
    Just called grandpa like I'm the boss