Могу ли я расширить класс, используя более 1 класса в PHP?

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

т.е. class a extends b extends c

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

10.12.2008 14:02:56
Используйте агрегацию или интерфейсы. Множественное наследование не существует в PHP.
Franck 10.12.2008 14:19:21
Я смотрю на интерфейсы, поскольку я не большой поклонник больших иерархий классов. Но я не вижу, как интерфейсы на самом деле делают что-нибудь?
atomicharri 10.12.2008 14:32:05
Интерфейсы позволяют вам «наследовать» только API, а не тела функций. Это заставляет класс a реализовывать методы из интерфейса b и c. Это означает, что если вы хотите наследовать поведение, вы должны агрегировать объекты-члены классов b и c в вашем классе a.
Franck 10.12.2008 14:37:37
Подумайте об использовании декораторов sourcemaking.com/design_patterns/decorator или Strategies sourcemaking.com/design_patterns/strategy
Gordon 22.04.2010 10:00:06
Пожалуйста, рассмотрите черты как правильный ответ.
Daniel 26.06.2018 09:58:25
20 ОТВЕТОВ
РЕШЕНИЕ

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

Если вы действительно хотите подделать множественное наследование, вы можете использовать магическую функцию __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"

167
10.12.2008 15:25:04
На самом деле мне очень нравится идея расширения класса. Есть ли какие-либо известные ограничения для этого?
atomicharri 10.12.2008 18:12:49
Насколько я знаю, никаких ограничений нет, PHP - очень разрешающий язык для таких маленьких хаков. :) Как уже отмечали другие, это не самый лучший способ сделать это.
Franck 10.12.2008 18:58:03
Вы не сможете использовать защищенные или частные методы.
wormhit 16.11.2012 14:20:04
@wormhit, однако, я бы не рекомендовал использовать его в производстве, можно использовать ReflectionClass для доступа к закрытым и защищенным методам.
Denis V 18.12.2013 19:21:46
Это не сработает для Implements, которые ожидают, что метод действительно существует, и поведение практически не определено, когда несколько базовых классов имеют метод с одинаковым именем. Это также испортит способность вашего редактора давать вам подсказки.
Erik 14.04.2015 13:51:21

PHP еще не поддерживает множественное наследование классов, однако поддерживает множественное наследование интерфейсов.

См. Http://www.hudzilla.org/php/6_17_0.php для некоторых примеров.

0
10.12.2008 14:07:28
Еще? Я сомневаюсь, что они сделают это. Современные ОО препятствуют множественному наследованию, оно может быть грязным.
PhiLho 10.12.2008 16:53:47

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

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

и так далее.

136
18.09.2010 01:13:26
Можете ли вы объяснить, почему правильно сначала наследовать Медсестру в Матрону, а затем объявить о наследовании HumanEntity в Медсестру?
Qwerty 26.02.2014 18:01:36
@Qwerty Потому что у Матроны есть дополнительные качества медсестры, а у медсестры - все качества человека. Таким образом, Матрона - человек-медсестра и, наконец, обладает способностями Матроны
J-Dizzle 19.09.2014 20:59:51

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

17
10.12.2008 14:48:10
Я не согласен с вашим утверждением, что запрос OP представляет собой «плохой дизайн OO» ; просто взгляните на появление Mixins, чтобы поддержать позицию, согласно которой добавление методов в класс из нескольких источников является хорошей идеей архитектурно. Я, однако, скажу вам, что PHP не предоставляет оптимальный набор языковых функций для достижения оптимального «дизайна», но это не означает, что использование функций, доступных для аппроксимации, обязательно является плохой идеей; просто посмотрите на ответ @ Franck.
MikeSchinkel 29.10.2013 17:09:07

PHP не допускает множественного наследования, но вы можете сделать это с помощью реализации нескольких интерфейсов. Если реализация «тяжелая», предоставьте скелетную реализацию для каждого интерфейса в отдельном классе. Затем вы можете делегировать весь интерфейсный класс этим реализациям скелета с помощью содержания объекта .

0
10.12.2008 15:21:00

Я прочитал несколько статей, не одобряющих наследование в проектах (в отличие от библиотек / фреймворков), и поощряющих программировать интерфейсы 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();
    }
}
6
10.12.2008 17:02:06
Это фактически становится тем же, что показывают Франк и Сэм. Конечно, если вы решите явно использовать композицию, вы должны использовать внедрение зависимостей .
MikeSchinkel 29.10.2013 17:15:10

Я думаю, что в ближайшее время планируется добавить дополнения.

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

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. Однако он не получит доступ к каким-либо закрытым / защищенным методам / переменным, как настоящий расширенный класс.

15
11.12.2008 06:22:08

Не зная, чего именно вы пытаетесь достичь, я бы посоветовал изучить возможность изменения дизайна вашего приложения, чтобы использовать композицию, а не наследование в этом случае.

0
25.01.2009 05:26:45

Всегда хорошая идея сделать родительский класс с функциями ... т.е. добавить всю эту функциональность в родительский.

И «переместить» все классы, которые используют это иерархически вниз. Мне нужно - переписать функции, которые являются конкретными.

0
1.05.2011 11:02:47

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

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

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

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

56
24.06.2011 10:50:44
Постарайтесь не злоупотреблять ими. По сути это статические функции, которые вы применяете к объектам. Вы можете создать беспорядок, злоупотребляя ими.
Nikola Petkanski 10.02.2017 14:20:39
Я не могу поверить, что это не выбранный ответ.
Daniel 26.06.2018 09:57:55

Я только что решил мою проблему «множественного наследования» с:

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

}

Таким образом, у меня есть возможность манипулировать сессией внутри SessionResponse, который расширяет MyServiceResponsetype, по-прежнему способный обрабатывать Session сам по себе.

1
1.02.2012 10:24:45

Если вы хотите проверить, является ли функция общедоступной, см. Эту тему: https://stackoverflow.com/a/4160928/2226755

И используйте метод call_user_func_array (...) для множества или не аргументов.

Нравится :

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");
1
23.05.2017 10:31:38
<?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;
?>
9
11.06.2019 13:57:26
Ваш ответ должен содержать объяснение вашего кода и описание того, как он решает проблему.
AbcAeffchen 7.10.2014 00:36:45
Это почти само за себя, но было бы здорово иметь комментарии в коде.
Heroselohim 11.03.2016 18:26:54

Принятый в настоящее время ответ @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
17.09.2015 08:16:35

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

Вот краткое руководство для вас, http://culttt.com/2014/06/25/php-traits/

1
24.03.2016 13:56:15

Множественное наследование работает на уровне интерфейса. Я сделал тест на 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

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

3
6.02.2017 10:44:13
интересно и неактуально.
Yevgeniy Afanasyev 24.12.2019 03:34:40

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

Однако большую часть времени было бы полезно наследовать от нескольких классов. Например, может быть желательно наследовать методы от пары разных классов, чтобы предотвратить дублирование кода.

Эта проблема может привести к классу, у которого есть длинная семейная история наследования, которая часто не имеет смысла.

В PHP 5.4 была добавлена ​​новая функция языка, известная как Черты. Черта похожа на Mixin в том, что она позволяет вам смешивать классы черт в существующий класс. Это означает, что вы можете уменьшить дублирование кода и получить преимущества, избегая проблем множественного наследования.

Черты

0
1.06.2017 10:13:21

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

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

Увидеть сейчас бизнес-леди логически унаследовал бизнес и человека обоих;

10
15.09.2017 12:51:33
Я застрял с PHP 5.3 нет поддержки черт: D
Nishchal Gautam 20.02.2018 02:24:35
Почему бы не использовать черту BusinessWomanвместо BusinessPerson? Тогда вы можете иметь фактическое множественное наследование.
SOFe 30.01.2019 16:54:20
@ SOFe, потому что BusinessMan также может расширить его. Таким образом, тот, кто когда-либо занимается бизнесом, может расширить бизнес и автоматически получить черты
Alice 6.02.2019 13:09:52
То, как вы это делаете, ничем не отличается от того, что возможно при одиночном наследовании, когда BusinessPerson расширяет абстрактный класс, называемый человеком. Я хочу сказать, что ваш пример не нуждается в множественном наследовании.
SOFe 6.02.2019 13:53:12
Я верю, что до BusinessPerson это требовалось, BusinessWoman мог бы напрямую унаследовать другие черты. Но здесь я попробовал сохранить обе черты в классе-посреднике, а затем использовать его там, где это необходимо. Так что я могу забыть о включении обеих черт, если мне снова понадобится кое-что еще (как я описал BusinessMan). Это все о стилях кода. Если вы хотите включить непосредственно в свой класс. Вы можете пойти дальше с этим - поскольку вы абсолютно правы в этом.
Alice 6.02.2019 14:01:27

класс А расширяет B {}

класс B расширяет C {}

Тогда А расширил и В, и С

-3
14.06.2018 14:31:17
@rostamiani, потому что этот метод также модифицирует класс B. Что мы хотим , чтобы большую часть времени, чтобы иметь два несвязанных классов Bи C(вероятно , из различных библиотек) и получить одновременно наследуется A, таким образом, что Aсодержит все от обоих Bи C, но Bне содержит ничего от Cи наоборот.
SOFe 30.01.2019 16:51:09

Это не настоящий ответ, у нас есть дубликат класса

... но это работает :)

class A {
    //do some things
}

class B {
    //do some things
}

класс copy_B - это копия всего класса B

class copy_B extends A {

    //do some things (copy class B)
}

class A_B extends copy_B{}

сейчас же

class C_A extends A{}
class C_B extends B{}
class C_A_b extends A_B{}  //extends A & B
-1
13.11.2019 18:07:41