169

如果我有几个具有我需要的功能但想单独存储以进行组织的类,我可以扩展一个类以同时具有这两个功能吗?

IEclass a extends b extends c

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

4

20 回答 20

181

如果你真的想在 PHP 5.3 中伪造多重继承,你可以使用神奇的函数 __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”

于 2008-12-10T15:18:14.360 回答
144

你不能有一个扩展两个基类的类。您不能拥有以下内容:

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

等等。

于 2008-12-10T14:12:37.783 回答
66

您可以使用特征,希望从 PHP 5.4 开始提供。

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

它们因其在支持更好的组合和重用方面的潜力而受到认可,因此它们可以集成到新版本的语言中,例如 Perl 6、Squeak、Scala、Slate 和 Fortress。特征也已移植到 Java 和 C#。

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

于 2011-06-24T10:50:44.420 回答
18

类不只是方法的集合。一个类应该代表一个抽象概念,具有状态(字段)和改变状态的行为(方法)。使用继承只是为了获得一些期望的行为听起来像糟糕的 OO 设计,而这正是许多语言不允许多重继承的原因:为了防止“意大利面条式继承”,即扩展 3 个类,因为每个类都有你需要的方法,并以一个继承了 100 个方法和 20 个字段的类,但只使用了其中的 5 个。

于 2008-12-10T14:48:10.143 回答
18

我相信很快就会有添加混音的计划。

但在那之前,请接受公认的答案。您可以将其抽象出来以创建一个“可扩展”类:

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。但是,它不会像真正的扩展类那样访问任何私有/受保护的方法/变量。

于 2008-12-11T06:22:08.637 回答
14

使用特征作为基类。然后在父类中使用它们。扩展它。

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

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

于 2017-09-15T12:51:33.463 回答
13

编辑:2020 PHP 5.4+ 和 7+

从 PHP 5.4.0 开始,有“特性”——你可以在一个类中使用更多特性,所以最终的决定点是你是否想要真正的继承,或者你只需​​要一些“特性”(特性)。Trait 模糊地说,是一个已经实现的接口,意味着只是used。


@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 
于 2015-09-17T08:16:35.140 回答
10
<?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;
?>
于 2014-10-07T00:17:59.640 回答
6

我读过几篇文章,不鼓励在项目中继承(而不是库/框架),并鼓励对接口进行编程,而不是反对实现。
他们还提倡组合式 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();
    }
}
于 2008-12-10T17:02:06.847 回答
3

多重继承似乎在接口级别起作用。我在 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

我还没有玩过它,但我认为分享它可能会很有趣。

于 2017-02-06T10:44:13.353 回答
1

我刚刚解决了我的“多重继承”问题:

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 仍然能够自己处理会话。

于 2012-02-01T10:24:45.180 回答
1

如果要检查函数是否公开,请参阅此主题: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");
于 2013-12-23T10:07:19.687 回答
1

您可以使用自 PHP 5.4 起宣布的 PHP 中的 Traits 来做到这一点

这是给你的快速教程,http://culttt.com/2014/06/25/php-traits/

于 2016-03-24T13:56:15.840 回答
1

PHP 作为一种编程语言的问题之一是您只能具有单一继承这一事实。这意味着一个类只能从另一个类继承。

但是,很多时候从多个类继承是有益的。例如,可能需要从几个不同的类继承方法以防止代码重复。

这个问题可能导致类具有很长的家族继承历史,这通常是没有意义的。

在 PHP 5.4 中添加了该语言的一个新特性,称为 Traits。Trait 有点像 Mixin,因为它允许您将 Trait 类混合到现有类中。这意味着您可以减少代码重复并获得好处,同时避免多重继承问题。

性状

于 2017-05-06T11:13:28.347 回答
0

PHP 还不支持多类继承,但是它支持多接口继承。

有关一些示例,请参见http://www.hudzilla.org/php/6_17_0.php

于 2008-12-10T14:07:28.027 回答
0

PHP 不允许多重继承,但您可以实现多个接口。如果实现很“重”,则为单独的类中的每个接口提供骨架实现。然后,您可以通过对象包含将所有接口类委托给这些骨架实现

于 2008-12-10T15:21:00.890 回答
0

不确切知道您要实现的目标,我建议您考虑重新设计您的应用程序以在这种情况下使用组合而不是继承的可能性。

于 2009-01-25T05:26:45.977 回答
0

总是好主意是制作具有功能的父类......即将所有功能添加到父类。

并“移动”所有使用此分层的类。我需要 - 重写功能,这是特定的。

于 2011-05-01T11:02:47.173 回答
0

这不是一个真正的答案,我们有重复的课程

...但它有效:)

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
于 2019-09-10T10:46:15.920 回答
-3

A 类扩展 B {}

B 类扩展 C {}

然后 A 扩展了 B 和 C

于 2018-06-14T14:31:17.543 回答