我可以在PHP中使用多于一个类扩展一个类吗?

ato*_*rri 141 php oop extends class

如果我有几个类,我需要的功能,但想要为组织单独存储,我可以扩展一个类同时拥有它们吗?

class a extends b extends c

编辑:我知道如何一次扩展一个类,但我正在寻找一种方法来使用多个基类立即扩展一个类--AFAIK你不能在PHP中做到这一点但是应该有办法解决它而不诉诸于class c extends b,class b extends a

Fra*_*nck 163

回答你的编辑:

如果你真的想伪造多重继承,你可以使用魔术函数__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");
Run Code Online (Sandbox Code Playgroud)

打印"abcdef"

  • 您将无法使用受保护或私有方法. (62认同)
  • 就我所知,没有任何限制,对于像这样的小黑客来说,PHP是一种非常宽松的语言.:)正如其他人所指出的那样,尽管如此,这并不是正确的OOP方式. (3认同)
  • 魔术方法造成非常小的性能损失 (2认同)
  • 这对于Implements无效,后者希望该方法实际存在,并且当多个基类具有相同名称的方法时,该行为几乎是未定义的。它还会破坏编辑器给您提示的能力。 (2认同)

ada*_*dam 135

你不能拥有一个扩展两个基类的类.你不可能.

// this is NOT allowed (for all you google speeders)
Matron extends Nurse, HumanEntity
Run Code Online (Sandbox Code Playgroud)

但是,您可以具有以下层次结构......

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable
Run Code Online (Sandbox Code Playgroud)

等等.

  • @Qwerty因为护士具有护士的额外品质,而护士具有人类的所有品质.因此,护士长是一名人类护士,最后拥有护士长的能力 (5认同)

Nik*_*ski 52

您可以使用特征,希望可以从PHP 5.4获得.

Traits是一种在单继承语言(如PHP)中重用代码的机制.Trait旨在通过使开发人员能够在生活在不同类层次结构中的多个独立类中自由地重用方法集来减少单继承的某些限制.Traits和类组合的语义以某种方式定义,这降低了复杂性并避免了与多重继承和Mixins相关的典型问题.

他们因支持更好的组合和重用而具有潜力,因此可以将它们集成到新版本的语言中,如Perl 6,Squeak,Scala,Slate和Fortress.特征也被移植到Java和C#.

更多信息:https://wiki.php.net/rfc/traits

  • 我不敢相信这不是选定的答案。 (2认同)

Mic*_*rdt 17

类并不仅仅是方法的集合.一个类应该代表一个抽象概念,其中状态(字段)和行为(方法)都会改变状态.使用继承只是为了得到一些理想的行为听起来像糟糕的OO设计,以及许多语言不允许多重继承的原因:为了防止"意大利面继承",即扩展3个类,因为每个类都有你需要的方法,并最终得到一个继承100方法和20个字段的类,但只使用其中的5个.


Sam*_*Sam 14

我相信很快就会计划加入混合体.

但在那之前,请接受已接受的答案.您可以抽象出一点来构建一个"可扩展"类:

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();
Run Code Online (Sandbox Code Playgroud)

请注意,在此模型中,"OtherClass"会"了解"$ foo.OtherClass需要有一个名为"setExtendee"的公共函数来建立这种关系.然后,如果它的方法是从$ foo调用的,它可以在内部访问$ foo.但是,它不会像真正的扩展类那样访问任何私有/受保护的方法/变量.


Meh*_*ily 9

<?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;
?>
Run Code Online (Sandbox Code Playgroud)

  • 您的答案应该包含对您的代码的解释以及它如何解决问题的描述. (4认同)

Ali*_*ice 9

使用traits作为基类.然后在父类中使用它们.扩展它.

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();
Run Code Online (Sandbox Code Playgroud)

现在看到商界女性逻辑上继承了商业和人类两者;


jav*_*web 8

@Franck目前接受的答案是可行的,但它实际上并不是多继承,而是在范围之外定义的类的子实例,也有__call()简写 - 考虑$this->childInstance->method(args)在"扩展"类中使用 任何需要ExternalClass类方法的地方.

确切的答案

不,你不能分别,不是真的,因为关键字手册extends说:

扩展类始终依赖于单个基类,即不支持多重继承.

真实答案

但是,正如@adam建议的那样,这并不禁止您使用多个分层继承.

你可以扩展一个类,另一个类和另一个类,等等......

这个非常简单的例子就是:

class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...
Run Code Online (Sandbox Code Playgroud)

重要的提示

您可能已经注意到,如果您可以控制流程中包含的所有类,那么您只能按层次结构执行多个(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();
Run Code Online (Sandbox Code Playgroud)

哪个输出

I am a of A 
I am b of B 
I am c of C 
Run Code Online (Sandbox Code Playgroud)


Phi*_*Lho 6

我已经阅读了几篇文章,这些文章阻止了项目中的继承(而不是库/框架),并鼓励编写agaisnt接口,而不是实现.
他们还通过组合提倡OO:如果您需要类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();
    }
}
Run Code Online (Sandbox Code Playgroud)