web-dev-qa-db-fra.com

Puis-je étendre une classe en utilisant plus d'une classe en PHP?

Si j'ai plusieurs classes avec les fonctions dont j'ai besoin mais que je veux stocker séparément pour l'organisation, puis-je étendre une classe pour avoir les deux?

c'est-à-dire class a extends b extends c

edit: Je sais comment étendre les classes une par une, mais je cherche une méthode pour étendre instantanément une classe en utilisant plusieurs classes de base - Autant que je sache, vous ne pouvez pas le faire dans PHP mais il devrait y en avoir une. voies autour de lui sans recourir à class c extends b, class b extends a

135
atomicharri

Répondre à votre modification:

Si vous voulez vraiment simuler plusieurs héritages, vous pouvez utiliser la fonction magique __call ().

C'est moche, bien que cela fonctionne du point de vue de l'utilisateur de la classe 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");

Estampes "abcdef"

157
Franck

Vous ne pouvez pas avoir une classe qui étend deux classes de base. Tu n'aurais pas pu.

// this is NOT allowed (for all you google speeders)
Matron extends Nurse, HumanEntity

Vous pouvez cependant avoir une hiérarchie comme suit ...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

etc.

127
adam

Vous pouvez utiliser des traits qui, espérons-le, seront disponibles à partir de PHP 5.4.

Traits est un mécanisme de réutilisation de code dans des langages à héritage unique tels que PHP. Un trait est destiné à réduire certaines limitations de l'héritage simple en permettant à un développeur de réutiliser librement des ensembles de méthodes dans plusieurs classes indépendantes vivant dans des hiérarchies de classes différentes. La sémantique de la combinaison de Traits et de classes est définie de manière à réduire la complexité et à éviter les problèmes typiques associés à l'héritage multiple et aux Mixins.

Ils sont reconnus pour leur potentiel à prendre en charge une meilleure composition et une meilleure réutilisation, d’où leur intégration dans des versions plus récentes de langages tels que Perl 6, Squeak, Scala, Slate et Fortress. Les traits ont également été transférés vers Java et C #.

Plus d'informations: https://wiki.php.net/rfc/traits

51
Nikola Petkanski

Les classes ne sont pas destinées à être simplement des collections de méthodes. Une classe est supposée représenter un concept abstrait, avec à la fois un état (champs) et un comportement (méthodes) qui modifient l'état. Utiliser l'héritage simplement pour obtenir le comportement souhaité sonne comme une mauvaise conception OO, et c'est exactement la raison pour laquelle de nombreuses langues interdisent l'héritage multiple: afin d'éviter "l'héritage spaghetti", c'est-à-dire d'étendre trois classes besoin, et se retrouver avec une classe qui hérite de 100 méthodes et 20 champs, mais n'en utilise que 5.

17
Michael Borgwardt

Je crois qu'il est prévu d'ajouter des mix-ins bientôt.

Mais jusque-là, allez avec la réponse acceptée. Vous pouvez résumer cela un peu pour en faire une classe "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();

Notez que dans ce modèle, "OtherClass" apprend à connaître $ foo. OtherClass doit avoir une fonction publique appelée "setExtendee" pour configurer cette relation. Ensuite, si ses méthodes sont invoquées à partir de $ foo, il peut accéder à $ foo en interne. Cependant, il n'aura pas accès à des méthodes/variables privées/protégées comme le ferait une vraie classe étendue.

14
Sam

Utilisez les traits comme classes de base. Ensuite, utilisez-les dans une classe parent. Prolongez-le.

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

Voir maintenant la femme d'affaires a logiquement hérité des affaires et des hommes;

8
Alice
<?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;
?>
8
Mehdi Homeily

La réponse actuellement acceptée par @Franck fonctionnera, mais ce n'est pas en fait un héritage multiple, mais une instance enfant de classe définie hors de portée, il existe également le raccourci __call() - envisagez d'utiliser uniquement $this->childInstance->method(args) partout où vous avez besoin de ExternalClass méthode de classe en classe "étendue".

Réponse exacte

Non, vous ne pouvez pas, respectivement, pas vraiment, car manuel de extends mot-clé dit:

Une classe étendue dépend toujours d'une classe de base unique, c'est-à-dire que l'héritage multiple n'est pas pris en charge.

Vraie réponse

Cependant, comme @adam l'a suggéré correctement, cela ne vous interdit PAS d'utiliser plusieurs héritages hiérarchiques.

Vous POUVEZ prolonger une classe, avec une autre et une autre avec une autre, etc.

Voici un exemple assez simple à ce sujet:

class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...

Note importante

Comme vous l'avez peut-être remarqué, , vous ne pouvez effectuer une hiérarchie multiple (2+) que si vous avez le contrôle de toutes les classes incluses dans le processus - cela signifie , vous ne pouvez pas appliquer cette solution, par exemple avec des classes intégrées ou avec des classes que vous ne pouvez tout simplement pas éditer - si vous voulez le faire, il ne vous reste plus que la solution @Franck - des instances enfants.

... Et enfin exemple avec quelques sorties:

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

Quelles sorties

I am a of A 
I am b of B 
I am c of C 
8
jave.web

J'ai lu plusieurs articles décourageant l'héritage dans les projets (par opposition aux bibliothèques/frameworks), et encourageant la programmation d'interfaces agaisesnt, pas d'implémentations contraires.
Ils préconisent également OO par composition: si vous avez besoin des fonctions des classes a et b, définissez c avec des membres/champs de ce type:

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

L'héritage multiple semble fonctionner au niveau de l'interface. J'ai fait un test sur php 5.6.1.

Voici un code de travail:

<?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

Je ne pensais pas que c'était possible, mais je suis tombé sur le code source de SwiftMailer, dans la classe Swift_Transport_IoBuffer, qui a la définition suivante:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

Je n'ai pas encore joué avec, mais j'ai pensé qu'il pourrait être intéressant de le partager.

3
ling

Je viens de résoudre mon problème "d'héritage multiple" avec:

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 cette façon, j'ai le pouvoir de manipuler une session à l'intérieur d'un SessionResponse qui étend MyServiceResponsetype tout en restant capable de gérer Session par lui-même.

1
blomman9

Vous pouvez le faire en utilisant Traits dans PHP qui a annoncé à partir de PHP 5.4

Voici un tutoriel rapide pour vous, http://culttt.com/2014/06/25/php-traits/

1
spetsnaz

Ne sachant pas exactement ce que vous essayez d'atteindre, je suggérerais d'examiner la possibilité de redéfinir votre application pour utiliser la composition plutôt que l'héritage dans ce cas.

0
Joel

PHP ne supporte pas encore l'héritage de plusieurs classes, il prend cependant en charge l'héritage de plusieurs interfaces.

Voir http://www.hudzilla.org/php/6_17_0.php pour quelques exemples.

0
Tader

Si vous souhaitez vérifier si une fonction est publique, consultez le sujet suivant: https://stackoverflow.com/a/4160928/2226755

Et utilisez la méthode call_user_func_array (...) pour plusieurs arguments ou non.

Comme ça :

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");
0
A-312

PHP n'autorise pas l'héritage multiple, mais vous pouvez le faire en implémentant plusieurs interfaces. Si l'implémentation est "lourde", fournissez implémentation squelettique pour chaque interface dans une classe séparée. Ensuite, vous pouvez déléguer toute la classe d'interface à ces implémentations squelettiques via confinement d'objet.

0
petr k.

Il est toujours judicieux de créer une classe parente, avec des fonctions ...

Et "déplacer" toutes les classes qui utilisent cette hiérarchie vers le bas. J'ai besoin de réécrire des fonctions spécifiques.

0
publikz.com

Un des problèmes de PHP en tant que langage de programmation est le fait que vous ne pouvez avoir qu'un seul héritage. Cela signifie qu'une classe ne peut hériter que d'une autre classe.

Cependant, il serait souvent bénéfique d'hériter de plusieurs classes. Par exemple, il peut être souhaitable d'hériter des méthodes de deux classes différentes afin d'éviter la duplication de code.

Ce problème peut conduire à une classe qui a de longs antécédents familiaux d’héritage qui n’ont souvent aucun sens.

Dans PHP 5.4, une nouvelle fonctionnalité du langage appelée Traits a été ajoutée. Un trait est un peu comme un mixin en ce sens qu'il vous permet de mélanger des classes de traits dans une classe existante. Cela signifie que vous pouvez réduire la duplication de code et en tirer les avantages tout en évitant les problèmes d'héritage multiple.

Traits

0
Osama