PHP: Cómo llamar a la función de una clase hija de la clase principal

¿Cómo puedo llamar a una función de una clase infantil de la clase principal? Considera esto:

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

Para eso son las clases abstractas . Una clase abstracta básicamente dice: Quien hereda de mí, debe tener esta función (o estas funciones).

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

De acuerdo, esta respuesta es MUY tarde, pero ¿por qué nadie pensó en esto?

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

Y el método se define en la clase extendida:

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

Entonces con el siguiente código:

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

El resultado será:

 I am a child method! 

Lo uso para llamar a los métodos de enlace que se pueden definir por una clase secundaria pero no tienen que serlo.

Técnicamente, no se puede llamar a una instancia de pez (hija) de una instancia de ballena (principal), pero dado que se trata de herencia, myFunc () estará disponible en su instancia de pez de todos modos, para que pueda llamar $yourFishInstance->myFunc() directamente.

Si está refiriéndose al patrón de método de la plantilla , simplemente escriba $this->test() como el cuerpo del método. Llamar a myFunc() desde una instancia de pez delegará la llamada a test() en la instancia de pez. Pero nuevamente, no hay llamadas desde una instancia de ballena a una instancia de pez.

En una nota al margen, una ballena es un mamífero y no un pez;)

Ok, bueno, hay tantas cosas mal con esta pregunta que realmente no sé por dónde empezar.

En primer lugar, los peces no son ballenas y las ballenas no son peces. Las ballenas son mamíferos.

En segundo lugar, si desea llamar a una función en una clase secundaria de una clase principal que no existe en su clase principal, entonces su abstracción es gravemente defectuosa y debe reconsiderarla desde cero.

En tercer lugar, en PHP puedes hacer:

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

En una instancia de whale causará un error. En una instancia de fish debería funcionar.

Desde PHP 5.3, puede usar la palabra clave static para llamar a un método de la clase llamada. es decir:

  

El ejemplo anterior dará como resultado: B

fuente: PHP.net / enlaces estáticos tardíos

Yo iría con la clase abstracta …
pero en PHP no tienes que usarlos para que funcione. Incluso la invocación del constructor de la clase padre es una llamada al método “normal” y el objeto es completamente “operativo” en este momento, es decir, $ este “sabe” sobre todos los miembros, heredados o no.

 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; 

huellas dactilares

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

es decir, aunque la clase Foo no sabe nada sobre una función init () puede llamar al método, ya que la búsqueda se basa en a qué $ es una referencia.
Ese es el lado técnico. Pero realmente debe hacer cumplir la implementación de ese método ya sea haciéndolo abstracto (forzando a los descendientes a implementarlo) o proporcionando una implementación predeterminada que se puede sobrescribir.

Sé que es probable que sea un poco tarde para ti, pero también tuve que solucionar este problema. Para ayudar a otros a entender por qué esto a veces es un requisito, aquí está mi ejemplo:

Estoy construyendo un marco MVC para una aplicación, tengo una clase de controlador base, que se amplía para cada clase de controlador individual. Cada controlador tendrá diferentes métodos, dependiendo de lo que el controlador necesite hacer. Por ejemplo, mysite.com/event cargaría el controlador de eventos. mysite.com/event/create cargará el controlador de eventos y llamará al método ‘crear’. Para estandarizar la llamada de la función de creación, necesitamos la clase de controlador base para acceder a los métodos de la clase hija, que será diferente para cada controlador. Entonces, en cuanto al código, tenemos la clase padre:

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

Entonces la clase del niño:

 class wordcontroller extends controller { public function add() { echo "Inside Add"; } public function edit() { echo "Inside Edit"; } public function delete() { echo "Inside Delete"; } } 

Entonces, la solución en mi caso era pasar la instancia hija a la clase principal como parámetro.

La única forma en que podrías hacer esto sería a través de la reflexión . Sin embargo, la reflexión es costosa y solo debe usarse cuando sea necesario.

El verdadero problema aquí es que una clase de padres nunca debe confiar en la existencia de un método de clase infantil. Este es un principio rector de OOD e indica que hay un defecto grave en su diseño.

Si su clase de padres depende de un niño específico, entonces no puede ser utilizada por ninguna otra clase de niños que pueda extenderla también. La relación padre-hijo va de la abstracción a la especificidad, y no al revés. Sería mucho, mucho mejor que poner la función requerida en la clase principal en su lugar, y anularla en las clases secundarias si es necesario. Algo como esto:

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

¿Qué pasa si la ballena no se extiende? ¿Cómo sería la llamada de esa función? Desafortunadamente no hay forma de hacerlo.

Ah, y un pez extiende una ballena? Un pez es un pez, una ballena es un mamífero.

Es muy sencillo. Puedes hacer esto sin clase abstracta.

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

Incluso si esta es una vieja pregunta, esta es mi solución usando 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); } } 

El beneficio de utilizar la clase ReflectionMethod es que puede pasar una matriz de argumentos y verificar cuál se necesita en el método que está llamando:

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