PHP: How to call function of a child class from parent class
Asked Answered
S

15

76

How do i call a function of a child class from parent class? Consider this:

class whale
{
  function __construct()
  {
    // some code here
  }

  function myfunc()
  {
  // how do i call the "test" function of fish class here??
  }
}

class fish extends whale
{
  function __construct()
  {
    parent::construct();
  }

  function test()
  {
    echo "So you managed to call me !!";
  }

}
Swordsman answered 22/12, 2009 at 8:3 Comment(4)
I know i have put in place wrong class names but this is just an example, not something that i will implement it anywhere.Swordsman
in a logical view, fish should be the parent class and whale the child one ;)Windburn
@DaNieL Maybe fish should be the child class as it resides inside the whale after it's been eaten.Rodent
If you wonder, whales are mammals, not fish.Strontia
R
135

That's what abstract classes are for. An abstract class basically says: Whoever is inheriting from me, must have this function (or these functions).

abstract class whale
{

  function __construct()
  {
    // some code here
  }

  function myfunc()
  {
    $this->test();
  }

  abstract function test();
}


class fish extends whale
{
  function __construct()
  {
    parent::__construct();
  }

  function test()
  {
    echo "So you managed to call me !!";
  }

}


$fish = new fish();
$fish->test();
$fish->myfunc();
Repository answered 22/12, 2009 at 8:28 Comment(5)
While correct, I think he wants to do $whale = new Whale; $fish = new Fish; and then $whale->myfunc(); is supposed to call $fish->test(); without knowing $fish exists.Thuja
+1, abstract methods are generally the answer to your question of having parent classes refer to methods in child classes. If Gordon is correct and you're really trying to do something different/specific like that, you should clarify. Otherwise, this should be accepted.Bangup
I searched also this, but your answer does not sovel what I want:whale = new whale(); $whale->test(); :( So one thing which I am thinking about - when called whale controller, then just redirect to fish controller and call. But somehow it feels that redirects are lagging.Irena
@Repository I have question in this we are not able to create object of whale class because abstract class object creating is not possible. But in my case some other function inside parent class how can I manage easily please suggest if any way.Rocambole
Is it possible to do this staticly? when the method is staticMoriah
C
36

Okay, this answer is VERY late, but why didn't anybody think of this?

Class A{
    function call_child_method(){
        if(method_exists($this, 'child_method')){
            $this->child_method();
        }
    }
}

And the method is defined in the extending class:

Class B extends A{
    function child_method(){
        echo 'I am the child method!';
    }
}

So with the following code:

$test = new B();
$test->call_child_method();

The output will be:

I am a child method!

I use this to call hook methods which can be defined by a child class but don't have to be.

Contrarious answered 26/1, 2013 at 21:43 Comment(5)
If all child classes have the same methods you can also consider implementing an interface to the child classes. Thereby you're able to exclude the method_exists() in the parent class.Vassalage
@Christian Engel may be you are wrong the questioner try to say something like creating object of class A and using this object they want to try call method of class B. May Be my understanding wrong.Rocambole
You in fact are calling a method of class B to call another method of class B :|Dorset
Yes, absolutely @rain! The reason why this pattern is interesting is, that you can ignore if a child class implements a given method. The "guard" or hook method will always be there, tests if the child class implements the "real" method and calls it.Contrarious
@ChristianEngel Besides that this is a violation of single responsibility, there’s no guarantee that someone won’t just call $test->child_method(). I think an interface contract will be best if the only thing that matters is if a certain class has a certain method. And then check if that class implemented the intended interface.Dorset
T
18

Technically, you cannot call a fish instance (child) from a whale instance (parent), but since you are dealing with inheritance, myFunc() will be available in your fish instance anyway, so you can call $yourFishInstance->myFunc() directly.

If you are refering to the template method pattern, then just write $this->test() as the method body. Calling myFunc() from a fish instance will delegate the call to test() in the fish instance. But again, no calling from a whale instance to a fish instance.

On a sidenote, a whale is a mammal and not a fish ;)

Thuja answered 22/12, 2009 at 8:16 Comment(1)
this is just an example dude, i am not going to implement those names anywhere :)Swordsman
B
14

Ok, well there are so many things wrong with this question I don't really know where to start.

Firstly, fish aren't whales and whales aren't fish. Whales are mammals.

Secondly, if you want to call a function in a child class from a parent class that doesn't exist in your parent class then your abstraction is seriously flawed and you should rethink it from scratch.

Third, in PHP you could just do:

function myfunc() {
  $this->test();
}

In an instance of whale it will cause an error. In an instance of fish it should work.

Blayze answered 22/12, 2009 at 8:10 Comment(3)
+1 for- Firstly, fish aren't whales and whales aren't fish. Whales are mammals.Value
Excellent answer @cletus, that means that It is not good for a parent class to have such intimate knowledge of its children, right? +1 Thanks in advanceTitleholder
i.e. a parent should not depend on the child, but a child class may depend on a parent class @BlayzeTitleholder
A
14

Since PHP 5.3 you can use the static keyword to call a method from the called class. i.e.:

<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // Here comes Late Static Bindings
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test();
?>

The above example will output: B

source: PHP.net / Late Static Bindings

Acroterion answered 4/1, 2013 at 21:37 Comment(2)
great, I can't thank you enough. I was stuck with my static classes where I don't have an instance of them to call, this was the only way I solved it.Rem
@TomSawyer can you please provide any proof or any benchmark results?Noddy
F
3

The only way you could do this would be through reflection. However, reflection is expensive and should only be used when necessary.

The true problem here is that a parent class should never rely on the existence of a child class method. This is a guiding principle of OOD, and indicates that there is a serious flaw in your design.

If your parent class is dependent on a specific child, then it cannot be used by any other child classes that might extend it as well. The parent-child relationship goes from abstraction to specificity, not the other way around. You would be much, much better off to put the required function in the parent class instead, and override it in the child classes if necessary. Something like this:

class whale
{
  function myfunc()
  {
      echo "I am a ".get_class($this);
  }
}

class fish extends whale
{
  function myfunc()
  {
     echo "I am always a fish.";
  }
}
Fashion answered 22/12, 2009 at 8:15 Comment(1)
I doubt it can be done with Reflection. Whale would have to have Fish hardcoded somewhere.Thuja
K
3

I'd go with the abstract class....
but in PHP you don't have to use them to make it work. Even the invocation of the parent class' constructor is a "normal" method call and the object is fully "operational" at this point, i.e. $this "knows" about all the members, inherited or not.

class Foo
{
  public function __construct() {
    echo "Foo::__construct()\n";
    $this->init();
  }
}

class Bar extends Foo
{
  public function __construct() {
    echo "Bar::__construct()\n";
    parent::__construct();
  }

  public function init() {
    echo "Bar::init()\n";
  }
}

$b = new Bar;

prints

Bar::__construct()
Foo::__construct()
Bar::init()

i.e. even though class Foo doesn't know anything about a function init() it can call the method since the lookup is based on what $this is a reference to.
That's the technical side. But you really should enforce the implementation of that method by either making it abstract (forcing descendants to implement it) or by providing a default implementation that can be overwritten.

Koheleth answered 22/12, 2009 at 10:43 Comment(0)
H
3

I know this is probably a bit late for you, but I had to get around this problem as well. To help others understand why this is sometimes a requirement, here's my example:

I'm building an MVC framework for an application, I have a base controller class, which is extended by each individual controller class. Each controller will have different methods, depending on what the controller needs to do. Eg, mysite.com/event would load the event controller. mysite.com/event/create will load the event controller and call the 'create' method. In order to standardise the calling of the create function, we need the base controller class to access the methods of the child class, which will be different for every controller. So code-wise, we have the parent class:

class controller {

    protected $aRequestBits;

    public function __construct($urlSegments) {
        array_shift($urlSegments);
        $this->urlSegments = $urlSegments;      
    }

    public function RunAction($child) {
        $FunctionToRun = $this->urlSegments[0];
        if(method_exists($child,$FunctionToRun)) {
            $child->$FunctionToRun();
        }
    }   
}

Then the child class:

class wordcontroller extends controller {

    public function add() {
        echo "Inside Add";
    }

    public function edit() {
        echo "Inside Edit";
    }

    public function delete() {
        echo "Inside Delete";
    }
}

So the solution in my case was to pass the child instance itself back to the parent class as a parameter.

Hum answered 20/2, 2012 at 0:14 Comment(1)
You don't need to pass the $child reference. using if(method_exists($this,$FunctionToRun)) should be enough. Keep in mind that wordcontroller inherits all the functions in controller, and so wordControlObj->runAction() will check for existing functions in wordcontroller and controller classes. If wordController overrides any method in controller, then wordcontroller version will be called instead. If you happen to have a debugger, an easy way of tracking the run flow would be override in wordcontroller the method just by using: function RunAction() { parent::RunAction() }Laurence
D
1

It's very simple. You can do this without abstract class.

class whale
{
  function __construct()
  {
    // some code here
  }

  /*
  Child overridden this function, so child function will get called by parent. 
  I'm using this kind of techniques and working perfectly.  
  */
  function test(){
     return "";
  }

  function myfunc()
  {
    $this->test();
  }
}

class fish extends whale
{
  function __construct()
  {
    parent::construct();
  }

  function test()
  {
    echo "So you managed to call me !!";
  }

}
Durware answered 16/9, 2017 at 11:47 Comment(0)
R
1

Even if this is an old question, this is my solution using ReflectionMethod:

class whale
{
  function __construct()
  {
    // some code here
  }

  function myfunc()
  {
    //Get the class name
    $name = get_called_class();

    //Create a ReflectionMethod using the class and method name
    $reflection = new \ReflectionMethod($class, 'test');

    //Call the method
    $reflection->invoke($this);
  }
}

The benefit of using the ReflectionMethod class is that you could pass an array of arguments and check which one is needed in the method you are calling:

    //Pass a list of arguments as an associative array
    function myfunc($arguments){
      //Get the class name
      $name = get_called_class();

      //Create a ReflectionMethod using the class and method name
      $reflection = new \ReflectionMethod($class, 'test');

      //Get a list of parameters
      $parameters = $reflection->getParameters()

      //Prepare argument list
      $list = array();
      foreach($parameters as $param){

          //Get the argument name
          $name = $param->getName();
          if(!array_key_exists($name, $arguments) && !$param->isOptional())
            throw new \BadMethodCallException(sprintf('Missing parameter %s in method %s::%s!', $name, $class, $method));

          //Set parameter
          $list[$name] = $arguments[$name];
        }

      //Call the method
      $reflection->invokeArgs($this, $list);
    }
Retinol answered 11/12, 2017 at 12:42 Comment(0)
T
1

From whale instance you can't call this function. but from fish instance you can do

function myfunc()
{
    static::test();
}
Termite answered 8/11, 2019 at 19:1 Comment(0)
G
0

If exists a method in the child class, method will be called from the parent class (as an optional callback if exists)

<?php

    class controller
    {

        public function saveChanges($data)
        {
            //save changes code
            // Insert, update ... after ... check if exists callback
            if (method_exists($this, 'saveChangesCallback')) {
                $arguments = array('data' => $data);
                call_user_func_array(array($this, 'saveChangesCallback'), $arguments);
            }
        }
    }

    class mycontroller extends controller
    {

        public function setData($data)
        {
            // Call parent::saveChanges
            $this->saveChanges($data);
        }

        public function saveChangesCallback($data)
        {
            //after parent::saveChanges call, this function will be called if exists on this child
            // This will show data and all methods called by chronological order:
            var_dump($data);
            echo "<br><br><b>Steps:</b><pre>";
            print_r(array_reverse(debug_backtrace()));
            echo "</pre>";
        }
    }

$mycontroller = new mycontroller();
$mycontroller->setData(array('code' => 1, 'description' => 'Example'));
Gimble answered 22/5, 2019 at 9:37 Comment(0)
D
0

That's a little tricky if you talk about OOP concepts that's not possible

but if you use your brain then it can be :)

OOP say's you cannot call child class function from parent class and that's correct because inheritance is made of inheriting parent functions in child

but

you can achieve this with Static class

class Parent
{
  static function test()
  {
     HelperThread::$tempClass::useMe();
  }
}

class child extends parent 
{
  // you need to call this. functon everytime you want to use 
  static function init()
  {
     HelperThread::$tempClass = self::class;
  }

  static function useMe()
  {
     echo "Ahh. thank God you manage a way to use me";
  } 

}

class HelperThread
{
  public static  $tempClass;
}

that's just a solution to my problem. i hope it helps with your problem

Happy Coding :)

Donahoe answered 5/4, 2022 at 16:0 Comment(0)
R
0

Why not like this!


    class whale
    {
      function __construct()
      {
          // some code here
      }
    
      function myfunc()
      {
          (new fish())->test();
      }
    }
    
    class fish extends whale
    {
      function __construct()
      {
        parent::__construct();
      }
    
      function test()
      {
        echo "So you managed to call me !!";
      }
    
    }

Rochette answered 12/7, 2023 at 8:1 Comment(0)
H
-1

what if whale isn't extended? what would that function call result in? Unfortunately there is no way to do it.

Oh, and does a fish extend a whale? A fish is a fish, a whale is a mammal.

Havildar answered 22/12, 2009 at 8:5 Comment(3)
anyways, i could not understand your answer, can u suggest an alternative to it?Swordsman
This is unlogic. If that whale got no child class, it isn't extended - hence no method to call from it.Jungle
@Havildar "A fish is a fish, a whale is a mammal." --- What the hell does this have to do with anything? It's a class name. Does the code work? That's the real question here. Because your answer could have just been a comment (it doesn't try solve the issue in any way), and because you are very condescending, I will downvote this answer.Homology

© 2022 - 2024 — McMap. All rights reserved.