web-dev-qa-db-fra.com

PHP: Comment appeler une fonction d'une classe enfant à partir de la classe parent

Comment appeler une fonction d'une classe enfant à partir de la classe parent? Considérons ceci:

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 !!";
  }

}
52
Sarfraz

C'est ce que classes abstraites sont pour. Une classe abstraite dit essentiellement: Quiconque hérite de moi doit avoir cette fonction (ou ces fonctions).

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();
93
FlorianH

D'accord, cette réponse est très tardive, mais pourquoi personne n'y a-t-il pensé?

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

Et la méthode est définie dans la classe extensible:

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

Donc avec le code suivant:

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

La sortie sera:

I am a child method!

J'utilise cela pour appeler des méthodes hook qui peuvent être définies par une classe enfant mais ne doivent pas l'être.

27
Christian Engel

Techniquement, vous ne pouvez pas appeler une instance de poisson (enfant) depuis une instance de baleine (parent), mais puisque vous avez affaire à un héritage, myFunc () sera de toute façon disponible dans votre instance de poisson. Vous pouvez donc appeler directement $yourFishInstance->myFunc().

Si vous vous référez au modèle de méthode modèle , écrivez simplement $this->test() en tant que corps de la méthode. L'appel de myFunc() à partir d'une instance fish déléguera l'appel à test() dans l'instance fish. Mais encore une fois, aucun appel d'une instance de baleine à une instance de poisson.

Sur une note, une baleine est un mammifère et non un poisson;)

15
Gordon

Ok, eh bien, il y a tellement de problèmes avec cette question que je ne sais pas par où commencer.

Premièrement, les poissons ne sont pas des baleines et les baleines ne sont pas des poissons. Les baleines sont des mammifères.

Deuxièmement, si vous voulez appeler une fonction dans une classe enfant à partir d’une classe parent qui n’existe pas dans votre classe parent, votre abstraction est sérieusement défectueuse et vous devez la repenser à partir de zéro.

Troisièmement, dans PHP, vous pouvez simplement faire:

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

Dans une instance de whale, cela provoquera une erreur. Dans une instance de fish, cela devrait fonctionner.

9
cletus

Depuis PHP 5.3, vous pouvez utiliser le mot clé static pour appeler une méthode à partir de la classe appelée. c'est à dire.:

<?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();
?>

L'exemple ci-dessus affichera: B

source: PHP.net/Liaisons statiques tardives

6
Agustin

J'irais avec la classe abstraite ....
mais dans PHP vous n'avez pas à les utiliser pour que cela fonctionne. Même l'appel du constructeur de la classe mère est un appel de méthode "normal" et l'objet est pleinement "opérationnel" à ce stade, c'est-à-dire que $ this "connaît" tous les membres, qu'ils soient hérités ou non.

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;

empreintes

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

c'est-à-dire que même si class Foo ne sait rien d'un function init (), il peut appeler la méthode car la recherche est basée sur la référence de $ this.
C'est le côté technique. Mais vous devriez vraiment imposer l'implémentation de cette méthode en la rendant abstraite (en forçant les descendants à l'implémenter) ou en fournissant une implémentation par défaut pouvant être écrasée. 

3
VolkerK

Je sais que c'est probablement un peu tard pour vous, mais je devais aussi contourner ce problème. Pour aider les autres à comprendre pourquoi c'est parfois une exigence, voici mon exemple:

Je construis un framework MVC pour une application, j'ai une classe de contrôleur de base, qui est étendue à chaque classe de contrôleur. Chaque contrôleur aura différentes méthodes, en fonction de ce que le contrôleur doit faire. Par exemple, mysite.com/event chargerait le contrôleur d'événements. mysite.com/event/create chargera le contrôleur d'événements et appellera la méthode 'create'. Afin de normaliser l'appel de la fonction create, nous avons besoin de la classe de contrôleur de base pour accéder aux méthodes de la classe enfant, qui seront différentes pour chaque contrôleur. En code, nous avons la classe parente:

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

Puis la classe enfant:

class wordcontroller extends controller {

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

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

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

La solution dans mon cas était donc de renvoyer l'instance enfant elle-même à la classe parent en tant que paramètre. 

2
rca86

Même s'il s'agit d'une vieille question, voici ma solution à l'aide de 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);
  }
}

L'avantage d'utiliser la classe ReflectionMethod est que vous pouvez passer un tableau d'arguments et vérifier lequel est nécessaire dans la méthode que vous appelez:

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

Le seul moyen de le faire serait par réflexion . Cependant, la réflexion coûte cher et ne devrait être utilisée que lorsque cela est nécessaire.

Le vrai problème ici est qu'une classe parent ne doit jamais s'appuyer sur l'existence d'une méthode de classe enfant. Ceci est un principe directeur de OOD, et indique qu'il existe un grave défaut dans votre conception.

Si votre classe parent dépend d'un enfant spécifique, elle ne peut pas être utilisée par aucune autre classe enfant susceptible de l'étendre également. La relation parent-enfant va de l'abstraction à la spécificité, et non l'inverse. Vous feriez bien mieux de placer la fonction requise dans la classe parente et de la remplacer si nécessaire dans les classes enfant. Quelque chose comme ça:

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

class fish extends whale
{
  function myfunc()
  {
     echo "I am always a fish.";
  }
}
1
zombat

S'il existe une méthode dans la classe enfant, la méthode sera appelée à partir de la classe parent (en tant que rappel facultatif s'il existe)

<?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'));
0
XPerez

C'est très simple. Vous pouvez le faire sans classe abstraite.

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 !!";
  }

}
0
rishad2m8