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

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

то есть. class a extends b extends c

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

Ответ 1

Отвечая на ваше редактирование:

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

Это уродливо, хотя оно работает с точки зрения пользователя класса 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");

Печать "abcdef"

Ответ 2

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

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

и т.д.

Ответ 3

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

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

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

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

Ответ 4

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

Ответ 5

В ближайшее время, по-моему, планируется добавить микширование.

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

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. Для настройки этого отношения у другогоClass должна быть открытая функция, называемая setExtendee. Затем, если его методы вызывают из $foo, он может получить доступ к $foo изнутри. Однако он не получит доступ к каким-либо частным/защищенным методам/переменным, например, к реальному расширенному классу.

Ответ 6

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

Ответ 7

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

Точный ответ

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

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

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

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

Вы можете расширить один класс, другой, другой с другим и так далее...

Довольно простой пример:

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

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

Как вы могли заметить, вы можете выполнять множественную (2+) intehritance по иерархии, если у вас есть контроль над всеми классами, включенными в процесс. Это означает, что вы не можете применить это решение например со встроенными классами или с классами, которые вы просто не можете редактировать, - если вы хотите это сделать, вы останетесь с решением @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

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

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

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

Ответ 9

Я прочитал несколько статей, обескураживающих наследование в проектах (в отличие от библиотек/фреймворков) и поощряя программировать интерфейсы agaisnt, а не от реализаций.
Они также выступают за ОО по составу: если вам нужны функции в классах a и b, сделайте 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();
    }
}

Ответ 10

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

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

Ответ 11

Я просто решил проблему с множественным наследованием:

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, все еще способный обрабатывать сеанс сам по себе.

Ответ 12

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

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

Ответ 13

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

Для некоторых примеров см. http://www.hudzilla.org/php/6_17_0.php.

Ответ 14

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

Ответ 15

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

Ответ 16

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

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

Ответ 17

Если вы хотите проверить, является ли функция общедоступной, см. эту тему: fooobar.com/questions/56593/...

И используйте метод 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");

Ответ 18

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

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

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

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

Traits

Ответ 19

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

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

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 extends A{}
class C extends B{}
class C extends A_B{}  //extends A & B

Ответ 20

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

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

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