Могу ли я расширить класс, используя более 1 класса в PHP? - PullRequest
130 голосов
/ 10 декабря 2008

Если у меня есть несколько классов с функциями, которые мне нужны, но я хочу сохранить их отдельно для организации, могу ли я расширить класс, чтобы иметь оба?

т.е. class a extends b extends c

edit: я знаю, как расширять классы по одному, но я ищу способ мгновенного расширения класса, используя несколько базовых классов - AFAIK, вы не можете сделать это в PHP, но должны быть способы обойти это без обращения к class c extends b, class b extends a

Ответы [ 19 ]

154 голосов
/ 10 декабря 2008

Отвечая на ваши изменения:

Если вы действительно хотите подделать множественное наследование, вы можете использовать магическую функцию __call ().

Это уродливо, хотя работает с точки зрения пользователя класса А:

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

Отпечатки "abcdef"

124 голосов
/ 10 декабря 2008

У вас не может быть класса, расширяющего два базовых класса. Вы не могли бы иметь.

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

Однако вы можете иметь следующую иерархию ...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

и т. Д.

50 голосов
/ 24 июня 2011

Вы можете использовать черты, которые, будем надеяться, будут доступны в PHP 5.4.

Черты - это механизм повторного использования кода в языках с одним наследованием, таких как PHP. Черта предназначена для уменьшения некоторых ограничений одиночного наследования, позволяя разработчику свободно повторно использовать наборы методов в нескольких независимых классах, живущих в разных иерархиях классов. Семантика сочетания признаков и классов определяется таким образом, чтобы уменьшить сложность и избежать типичных проблем, связанных с множественным наследованием и миксинами.

Они признаны за их потенциал в поддержке лучшей композиции и повторного использования, следовательно, их интеграция в более новые версии языков, таких как Perl 6, Squeak, Scala, Slate и Fortress. Черты также были портированы на Java и C #.

Дополнительная информация: https://wiki.php.net/rfc/traits

17 голосов
/ 10 декабря 2008

Классы не должны быть просто коллекциями методов. Предполагается, что класс представляет абстрактную концепцию с состоянием (полями) и поведением (методами), которое изменяет состояние. Использование наследования просто для получения желаемого поведения звучит как плохой дизайн ОО, и именно поэтому многие языки запрещают множественное наследование: чтобы предотвратить «наследование спагетти», то есть расширение 3 классов, потому что у каждого есть свой метод, и в конечном итоге класс, который наследует 100 методов и 20 полей, но использует только 5 из них.

14 голосов
/ 11 декабря 2008

В ближайшее время, я полагаю, есть планы по добавлению дополнений.

Но до тех пор идите с принятым ответом. Вы можете немного абстрагироваться, чтобы создать «расширяемый» класс:

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

Обратите внимание, что в этой модели "OtherClass" узнает о $ foo. OtherClass должен иметь открытую функцию setExtendee, чтобы установить это отношение. Затем, если его методы вызываются из $ foo, он может получить внутренний доступ к $ foo. Однако он не получит доступ к каким-либо закрытым / защищенным методам / переменным, как настоящий расширенный класс.

8 голосов
/ 17 сентября 2015

Принятый в настоящее время ответ @Franck будет работать, но на самом деле это не множественное наследование, а дочерний экземпляр класса, определенный вне области, также существует сокращение __call() - рассмотрите возможность использования только $this->childInstance->method(args) везде, где вам нужен класс ExternalClass метод в «расширенном» классе.

Точный ответ

Нет, вы не можете, соответственно, не совсем, поскольку руководство по extends ключевому слову говорит:

Расширенный класс всегда зависит от одного базового класса, то есть множественное наследование не поддерживается.

Реальный ответ

Однако, как правильно сказал @adam, это НЕ запрещает вам использовать множественное иерархическое наследование.

Вы МОЖЕТЕ продлить один класс, другой и другой с другим и так далее ...

Итак, довольно простой пример:

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

Важное примечание

Как вы могли заметить, вы можете делать множественное (2+) сцепление только по иерархии, если у вас есть контроль над всеми классами, включенными в процесс - это означает, что вы не можете применить это решение, например. со встроенными классами или с классами, которые вы просто не можете редактировать - если вы хотите это сделать, у вас остается решение @Franck - дочерние экземпляры.

... И, наконец, пример с некоторым выводом:

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

Какие выходы

I am a of A 
I am b of B 
I am c of C 
8 голосов
/ 07 октября 2014
<?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 голосов
/ 15 сентября 2017

Используйте черты в качестве базовых классов. Затем используйте их в родительском классе. Расширьте его.

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

Смотрите, теперь деловая женщина логически унаследовала бизнес и человека;

6 голосов
/ 10 декабря 2008

Я прочитал несколько статей, не одобряющих наследование в проектах (в отличие от библиотек / фреймворков), и поощряющих программировать интерфейсы agaisnt, но не против реализаций. Они также защищают ОО по составу: если вам нужны функции в классах А и В, сделайте, чтобы в c были элементы / поля этого типа:

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();
    }
}
3 голосов
/ 06 февраля 2017

Множественное наследование работает на уровне интерфейса. Я сделал тест на php 5.6.1.

Вот рабочий код:

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

Я не думал, что это было возможно, но я наткнулся на исходный код SwiftMailer в классе Swift_Transport_IoBuffer, который имеет следующее определение:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

Я еще не играл с ним, но подумал, что было бы интересно поделиться.

...