Call private method from inherited class

10,614

Solution 1

Add a dummy before_save function to your Record class, set its accessibly to protected. Now all classes that inherit from Record will have this function, if they don't overwrite it it will do NOTHING. If they overwrite it, it can implement the desired functionality.

class Record {
  public function save() {
    $this->before_save();
    //...Storing record etc.
  }

  protected function before_save() {
     return;
  }
}

class Payment extends Record {
  protected function before_save() {
    $this->payed_at = time();
  }
}

Solution 2

The winning answer doesn't answer the question. The information about "public", "protected", "private", and "final" should be obtainable on any blog or book.

This question asks about how to use a "private" function from an inherit class. The use case here is you're forced to use 3rd party code that is poorly designed, indiscriminately uses private functions, and faced with having to either find a way to use a private function, or fork the entire repo.

Here is the answer to the question.

class foo {

    protected $text = "foo";

    private function bar($text)
    {
        return $text.'->'.$this->text;
    }
}

class fooChild extends foo{

    protected $text = "bar";

    public function barChild()
    {
        $r = new \ReflectionMethod(parent::class, 'bar');
        $r->setAccessible(true);
        //return $r->invokeArgs(new foo(), ['output']); // output->foo
        return $r->invokeArgs($this, ['output']);//output->bar
    }
}

echo (new fooChild())->barChild();

Using the ReflectionMethod class you can call a private method from the inherit class. You can see the difference from using $this and a new instance of the parent. The properties will not be set from the child from a new instance.

Solution 3

Check the error message

Call to private method Payment::before_save() from context 'Record'

This means that you are trying to call a function defined in Payment while you are within Record. Class Record does not have a before_save method because it is further up in the inheritance chain than where the function is defined.

In other words since, the parent-child relation is Record (is parent of) Payment, Payment has access to Records functions (by virtue of inheriting from the parent) but not vice-versa (parent cannot "inherit" child class functions). You can make your function protected which will give it access up and down the inheritance chain, but you might want to rethink the architecture and decide if you want it so.. Ideally you should have the function defined in Record and have it overridden in Payment

Also (and I might be wrong with this), but checking explicitly for method_exists is usually not required unless you are creating a really dynamic system where run time classes can be overlapped and/or generated. If you are defining a class based system from ground-up and you know how you are stitching up the various pieces, usually you would not need to check during run-time if method_exists...just a thought..

Solution 4

change the scope to protected:

http://php.net/manual/en/language.oop5.visibility.php

Solution 5

Visibility and the rules of inheritance in PHP:

Members declared protected can be accessed only within the class itself and by inherited and parent classes

Share:
10,614
berkes
Author by

berkes

Bèr Kessels is entrepeneur and owner of webdevelopment company berk.es. Open Source Webdelopment. Specialised in Ruby, Rails and Sinatra.

Updated on June 15, 2022

Comments

  • berkes
    berkes almost 2 years

    I want to implement a hook-system in my simple ORM, in PHP:

    class Record {
      public function save() {
        if (method_exists($this,"before_save")) {
          $this->before_save();
        }
        //...Storing record etc.
      }
    }
    
    class Payment extends Record {
      private function before_save() {
        $this->payed_at = time();
      }
    }
    
    $payment = new Payment();
    $payment->save();
    

    This results in a Fatal Error:

    Fatal error: Call to private method Payment::before_save() from context 'Record' in

    Makes sense.

    I could change the scope to public, but that seems ugly: no-one but Payment has anything to do with before_save(). It is best left private, IMHO.

    How can I make Record call a private method on the class inheriting from the Record?