¿Puedo extender una clase usando más de 1 clase en PHP?
Pregunta
Si tengo varias clases con funciones que necesito pero quiero almacenar por separado para la organización, ¿puedo extender una clase para tener ambas cosas?
es decir class a extends b extends c
EDITAR: Sé cómo extender las clases uno a la vez, pero estoy buscando un método para extender instantáneamente una clase usando múltiples clases base: Afaik no puede hacerlo en PHP, pero debería haber formas de evitarlo sin recurrir class c extends b
, class b extends a
Solución
Respondiendo a su edición:
Si realmente desea fingir herencia múltiple, puede usar la función mágica __call ().
Esto es feo, aunque funciona desde el punto de vista del usuario de Clase A:
class B {
public function method_from_b($s) {
echo $s;
}
}
class C {
public function method_from_c($s) {
echo $s;
}
}
class A extends B
{
private $c;
public function __construct()
{
$this->c = new C;
}
// fake "extends C" using magic function
public function __call($method, $args)
{
$this->c->$method($args[0]);
}
}
$a = new A;
$a->method_from_b("abc");
$a->method_from_c("def");
Imprime "ABCDEF"
Otros consejos
No puede tener una clase que extienda dos clases base. No podrías tener.
// this is NOT allowed (for all you google speeders)
Matron extends Nurse, HumanEntity
Sin embargo, podrías tener una jerarquía de la siguiente manera ...
Matron extends Nurse
Consultant extends Doctor
Nurse extends HumanEntity
Doctor extends HumanEntity
HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable
y así.
Puede usar rasgos, que, con suerte, estarán disponibles en PHP 5.4.
Los rasgos es un mecanismo para la reutilización de código en lenguajes de herencia única, como PHP. Un rasgo está destinado a reducir algunas limitaciones de herencia única al permitir que un desarrollador reutilice conjuntos de métodos libremente en varias clases independientes que viven en diferentes jerarquías de clase. La semántica de la combinación de rasgos y clases se define de una manera, lo que reduce la complejidad y evita los problemas típicos asociados con la herencia y las mezclas múltiples.
Son reconocidos por su potencial para apoyar una mejor composición y reutilización, de ahí su integración en versiones más nuevas de idiomas como Perl 6, Squeak, Scala, Slate y Fortress. Los rasgos también se han portado a Java y C#.
Más información: https://wiki.php.net/rfc/traits
Las clases no están destinadas a ser solo colecciones de métodos. Se supone que una clase representa un concepto abstracto, con el estado (campos) y el comportamiento (métodos) que cambia el estado. El uso de la herencia solo para obtener un comportamiento deseado suena como un mal diseño de OO, y exactamente la razón por la cual muchos idiomas rechazan la herencia múltiple: para evitar la "herencia de espagueti", es decir, extender 3 clases porque cada uno tiene un método que necesita y terminar con Una clase que hereda 100 métodos y 20 campos, pero solo usa 5 de ellos.
Creo que hay planes para agregar mezclas pronto.
Pero hasta entonces, ve con la respuesta aceptada. Puede abstraer un poco para hacer una clase "extensible":
class Extendable{
private $extender=array();
public function addExtender(Extender $obj){
$this->extenders[] = $obj;
$obj->setExtendee($this);
}
public function __call($name, $params){
foreach($this->extenders as $extender){
//do reflection to see if extender has this method with this argument count
if (method_exists($extender, $name)){
return call_user_func_array(array($extender, $name), $params);
}
}
}
}
$foo = new Extendable();
$foo->addExtender(new OtherClass());
$foo->other_class_method();
Tenga en cuenta que en este modelo "OtherClass" llega a 'saber' sobre $ Foo. OtherClass necesita tener una función pública llamada "SetExtendee" para configurar esta relación. Luego, si sus métodos se invocan desde $ foo, puede acceder a $ foo internamente. Sin embargo, no obtendrá acceso a ningún método/variables privado/protegido como lo haría una clase extendida real.
La respuesta actualmente aceptada de @Franck funcionará, pero de hecho no es una herencia múltiple, sino una instancia infantil de clase definida fuera de alcance, también existe el __call()
taquigrafía: considere usar solo $this->childInstance->method(args)
en cualquier lugar donde necesite el método de clase de clases externos en la clase "extendida".
Respuesta exacta
No, no puedes, respectivamente, en realidad no, como manual de extends
palabra clave dice:
Una clase extendida siempre depende de una sola clase base, es decir, la herencia múltiple no es compatible.
Respuesta real
Sin embargo, como @adam sugirió correctamente, esto no le prohíbe usar múltiples herencias jerárquicas.
Puedes extender una clase, con otra y otra con otra y así sucesivamente ...
Tan un ejemplo bastante simple en esto sería:
class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...
Nota IMPORTANTE
Como habrás notado, Solo puede hacer múltiples (2+) entrada por jerarquía si tiene control sobre todas las clases incluidas en el proceso - Eso significa que no puede aplicar esta solución, por ejemplo, con clases incorporadas o con clases que simplemente no puede editar; si desea hacerlo, se queda con la solución @Franck: instancias infantiles.
... y finalmente ejemplo con alguna salida:
class A{
function a_hi(){
echo "I am a of A".PHP_EOL."<br>".PHP_EOL;
}
}
class B extends A{
function b_hi(){
echo "I am b of B".PHP_EOL."<br>".PHP_EOL;
}
}
class C extends B{
function c_hi(){
echo "I am c of C".PHP_EOL."<br>".PHP_EOL;
}
}
$myTestInstance = new C();
$myTestInstance->a_hi();
$myTestInstance->b_hi();
$myTestInstance->c_hi();
Que sale
I am a of A
I am b of B
I am c of C
Use rasgos como clases base. Luego úsalos en una clase principal. Extiéndelo .
trait business{
function sell(){
}
function buy(){
}
function collectMoney(){
}
}
trait human{
function think(){
}
function speak(){
}
}
class BusinessPerson{
use business;
use human;
// If you have more traits bring more
}
class BusinessWoman extends BusinessPerson{
function getPregnant(){
}
}
$bw = new BusinessWoman();
$bw ->speak();
$bw->getPregnant();
Vea ahora Mujer de Negocios Heredados Lógicamente Empresarios y Humanos Ambos;
<?php
// what if we want to extend more than one class?
abstract class ExtensionBridge
{
// array containing all the extended classes
private $_exts = array();
public $_this;
function __construct() {$_this = $this;}
public function addExt($object)
{
$this->_exts[]=$object;
}
public function __get($varname)
{
foreach($this->_exts as $ext)
{
if(property_exists($ext,$varname))
return $ext->$varname;
}
}
public function __call($method,$args)
{
foreach($this->_exts as $ext)
{
if(method_exists($ext,$method))
return call_user_method_array($method,$ext,$args);
}
throw new Exception("This Method {$method} doesn't exists");
}
}
class Ext1
{
private $name="";
private $id="";
public function setID($id){$this->id = $id;}
public function setName($name){$this->name = $name;}
public function getID(){return $this->id;}
public function getName(){return $this->name;}
}
class Ext2
{
private $address="";
private $country="";
public function setAddress($address){$this->address = $address;}
public function setCountry($country){$this->country = $country;}
public function getAddress(){return $this->address;}
public function getCountry(){return $this->country;}
}
class Extender extends ExtensionBridge
{
function __construct()
{
parent::addExt(new Ext1());
parent::addExt(new Ext2());
}
public function __toString()
{
return $this->getName().', from: '.$this->getCountry();
}
}
$o = new Extender();
$o->setName("Mahdi");
$o->setCountry("Al-Ahwaz");
echo $o;
?>
He leído varios artículos que desalentan la herencia en proyectos (a diferencia de las bibliotecas/marcos) y alentaban a programar interfaces de Agaisnt, no contra las implementaciones.
También abogan por composición: si necesita las funciones en la clase A y B, haga que C tenga miembros/campos de este tipo:
class C
{
private $a, $b;
public function __construct($x, $y)
{
$this->a = new A(42, $x);
$this->b = new B($y);
}
protected function DoSomething()
{
$this->a->Act();
$this->b->Do();
}
}
La herencia múltiple parece funcionar a nivel de interfaz. Hice una prueba en PHP 5.6.1.
Aquí hay un código de trabajo:
<?php
interface Animal
{
public function sayHello();
}
interface HairyThing
{
public function plush();
}
interface Dog extends Animal, HairyThing
{
public function bark();
}
class Puppy implements Dog
{
public function bark()
{
echo "ouaf";
}
public function sayHello()
{
echo "hello";
}
public function plush()
{
echo "plush";
}
}
echo PHP_VERSION; // 5.6.1
$o = new Puppy();
$o->bark();
$o->plush();
$o->sayHello(); // displays: 5.6.16ouafplushhello
No pensé que eso fuera posible, pero me topé con el código fuente de Swiftmailer, en la clase Swift_Transport_ioBuffer, que tiene la siguiente definición:
interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream
Todavía no jugué con eso, pero pensé que sería interesante compartir.
Acabo de resolver mi problema de "herencia múltiple" con:
class Session {
public $username;
}
class MyServiceResponsetype {
protected $only_avaliable_in_response;
}
class SessionResponse extends MyServiceResponsetype {
/** has shared $only_avaliable_in_response */
public $session;
public function __construct(Session $session) {
$this->session = $session;
}
}
De esta manera, tengo el poder de manipular la sesión dentro de una respuesta de sesión que extiende myServiceReSponseSype que aún puede manejar la sesión por sí misma.
Puede hacerlo usando rasgos en PHP que se anunciaron a partir de PHP 5.4
Aquí hay un tutorial rápido para ti, http://culttt.com/2014/06/25/php-traits/
PHP aún no admite la herencia de clase múltiple, sin embargo, admite la herencia de interfaz múltiple.
Ver http://www.hudzilla.org/php/6_17_0.php para algunos ejemplos.
PHP no permite la herencia múltiple, pero puede hacerlo implementando múltiples interfaces. Si la implementación es "pesada", proporcione implementación esquelética para cada interfaz en una clase separada. Entonces tú puedes Delegar toda la clase de interfaz a estas implementaciones esqueléticas a través de contención de objetos.
Sin saber exactamente lo que está tratando de lograr, sugeriría investigar la posibilidad de rediseñar su aplicación para usar la composición en lugar de la herencia en este caso.
Siempre la buena idea es hacer la clase de los padres, con funciones ... es decir, agregar toda esta funcionalidad a los padres.
Y "mover" todas las clases que usan esto jerárquicamente hacia abajo. Necesito: reescribir funciones, que son específicas.
Si desea verificar si una función es pública, consulte este tema: https://stackoverflow.com/a/4160928/2226755
Y use el método Call_user_func_array (...) para muchos argumentos o no.
Como esto :
class B {
public function method_from_b($s) {
echo $s;
}
}
class C {
public function method_from_c($l, $l1, $l2) {
echo $l.$l1.$l2;
}
}
class A extends B {
private $c;
public function __construct() {
$this->c = new C;
}
public function __call($method, $args) {
if (method_exists($this->c, $method)) {
$reflection = new ReflectionMethod($this->c, $method);
if (!$reflection->isPublic()) {
throw new RuntimeException("Call to not public method ".get_class($this)."::$method()");
}
return call_user_func_array(array($this->c, $method), $args);
} else {
throw new RuntimeException("Call to undefined method ".get_class($this)."::$method()");
}
}
}
$a = new A;
$a->method_from_b("abc");
$a->method_from_c("d", "e", "f");
Uno de los problemas de PHP como lenguaje de programación es el hecho de que solo puede tener una herencia única. Esto significa que una clase solo puede heredar de otra clase.
Sin embargo, muchas veces sería beneficiosa heredar de múltiples clases. Por ejemplo, podría ser deseable heredar métodos de un par de clases diferentes para evitar la duplicación de código.
Este problema puede conducir a una clase que tiene un largo historial familiar de herencia que a menudo no tiene sentido.
En PHP 5.4, se agregó una nueva característica del lenguaje conocida como rasgos. Un rasgo es como una mezcla en la que le permite mezclar clases de rasgos en una clase existente. Esto significa que puede reducir la duplicación de código y obtener los beneficios mientras evita los problemas de herencia múltiple.
La clase A se extiende B {}
La clase B se extiende C {}
Entonces A ha extendido tanto B como C