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

发布于 2024-07-09 17:48:13 字数 261 浏览 6 评论 0原文

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

class a extends b extends c

编辑:我知道如何一次扩展一个类,但我正在寻找一种方法来立即使用多个基类扩展一个类 - AFAIK 你不能在 PHP 中执行此操作,但应该有办法解决它,而无需求助于 class c extends bclass b extends a

If I have several classes with functions that I need but want to store separately for organisation, can I extend a class to have both?

i.e. class a extends b extends c

edit: I know how to extend classes one at a time, but I'm looking for a method to instantly extend a class using multiple base classes - AFAIK you can't do this in PHP but there should be ways around it without resorting to class c extends b, class b extends a

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

帝王念 2024-07-16 17:48:13

如果你真的想在 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”

If you really want to fake multiple inheritance in PHP 5.3, you can use the magic function __call().

This is ugly though it works from class A user's point of view :

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

Prints "abcdef"

不弃不离 2024-07-16 17:48:13

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

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

等等。

You cannot have a class that extends two base classes. You could not have the following:

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

You could however have a hierarchy as follows...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

and so on.

请持续率性 2024-07-16 17:48:13

您可以使用 Traits,希望 PHP 5.4 中可以使用 Traits。

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

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

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

You could use traits, which, hopefully, will be available from PHP 5.4.

Traits is a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies. The semantics of the combination of Traits and classes is defined in a way, which reduces complexity and avoids the typical problems associated with multiple inheritance and Mixins.

They are recognized for their potential in supporting better composition and reuse, hence their integration in newer versions of languages such as Perl 6, Squeak, Scala, Slate and Fortress. Traits have also been ported to Java and C#.

More information: https://wiki.php.net/rfc/traits

无尽的现实 2024-07-16 17:48:13

编辑:2020 PHP 5.4+ 和 7+

自 PHP 5.4.0 起,有“Traits” - 您可以在一个类中使用更多特征,因此最终的决定点是您是否想要真正的继承还是只是需要一些“特征”(特质)。 含糊地说,Trait 是一个已经实现的接口,仅供使用使用。


Currently accepted answer by @Franck will work but it is not in fact multiple inheritance but a child instance of class defined out of scope, also there is the `__call()` shorthand - consider using just `$this->childInstance->method(args)` anywhere you need ExternalClass class method in "extended" class.

确切的答案

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

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

真正的答案

但是,正如 @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 

EDIT: 2020 PHP 5.4+ and 7+

As of PHP 5.4.0 there are "Traits" - you can use more traits in one class, so the final deciding point would be whether you want really an inheritance or you just need some "feature"(trait). Trait is, vaguely said, an already implemented interface that is meant to be just used.


Currently accepted answer by @Franck will work but it is not in fact multiple inheritance but a child instance of class defined out of scope, also there is the `__call()` shorthand - consider using just `$this->childInstance->method(args)` anywhere you need ExternalClass class method in "extended" class.

Exact answer

No you can't, respectively, not really, as manual of extends keyword says:

An extended class is always dependent on a single base class, that is,
multiple inheritance is not supported.

Real answer

However as @adam suggested correctly this does NOT forbids you to use multiple hierarchal inheritance.

You CAN extend one class, with another and another with another and so on...

So pretty simple example on this would be:

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

Important note

As you might have noticed, you can only do multiple(2+) intehritance by hierarchy if you have control over all classes included in the process - that means, you can't apply this solution e.g. with built-in classes or with classes you simply can't edit - if you want to do that, you are left with the @Franck solution - child instances.

...And finally example with some output:

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

Which outputs

I am a of A 
I am b of B 
I am c of C 
心病无药医 2024-07-16 17:48:13

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

Classes are not meant to be just collections of methods. A class is supposed to represent an abstract concept, with both state (fields) and behaviour (methods) which changes the state. Using inheritance just to get some desired behaviour sounds like bad OO design, and exactly the reason why many languages disallow multiple inheritance: in order to prevent "spaghetti inheritance", i.e. extending 3 classes because each has a method you need, and ending up with a class that inherits 100 method and 20 fields, yet only ever uses 5 of them.

驱逐舰岛风号 2024-07-16 17:48:13

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

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

看看现在的女商人顺理成章地继承了商业和人类两者;

Use traits as base classes. Then use them in a parent class. Extend it .

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

See now business woman logically inherited business and human both;

梦罢 2024-07-16 17:48:13

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

但在那之前,请接受已接受的答案。 您可以对其进行一些抽象以创建一个“可扩展”类:

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

There are plans for adding mix-ins soon, I believe.

But until then, go with the accepted answer. You can abstract that out a bit to make an "extendable" class:

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

Note that in this model "OtherClass" gets to 'know' about $foo. OtherClass needs to have a public function called "setExtendee" to set up this relationship. Then, if it's methods are invoked from $foo, it can access $foo internally. It will not, however, get access to any private/protected methods/variables like a real extended class would.

勿忘初心 2024-07-16 17:48:13
<?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;
?>
<?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;
?>
手长情犹 2024-07-16 17:48:13

我读过几篇文章,反对在项目中继承(而不是库/框架),并鼓励针对接口进行编程,而不是针对实现。
他们还提倡通过组合实现面向对象:如果您需要类 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();
    }
}

I have read several articles discouraging inheritance in projects (as opposed to libraries/frameworks), and encouraging to program agaisnt interfaces, no against implementations.
They also advocate OO by composition: if you need the functions in class a and b, make c having members/fields of this type:

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();
    }
}
一个人的旅程 2024-07-16 17:48:13

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

我还没有使用它,但我认为它可能分享起来很有趣。

Multiple inheritance seems to work at the interface level.
I made a test on php 5.6.1.

Here is a working code:

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

I didn't think that was possible, but I stumbled upon in the SwiftMailer source code, in the Swift_Transport_IoBuffer class, which has the following definition:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

I didn't play with it yet, but I thought it might be interesting to share.

眼眸印温柔 2024-07-16 17:48:13

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

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 中操作会话,该 SessionResponse 扩展了 MyServiceResponsetype ,仍然能够自行处理 Session。

I just solved my "multiple inheritance" problem with:

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

}

This way I have the power to manipulate session inside a SessionResponse which extends MyServiceResponsetype still being able to handle Session by itself.

晒暮凉 2024-07-16 17:48:13

如果您想检查函数是否是公共的,请参阅此主题: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");

If you want to check if a function is public see this topic : https://stackoverflow.com/a/4160928/2226755

And use call_user_func_array(...) method for many or not arguments.

Like this :

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");
清浅ˋ旧时光 2024-07-16 17:48:13

您可以使用从 PHP 5.4 开始发布的 PHP 中的 Traits 来做到这一点,

这里有一个快速教程,http://culttt.com/2014/06/25/php-traits/

You are able to do that using Traits in PHP which announced as of PHP 5.4

Here is a quick tutorial for you, http://culttt.com/2014/06/25/php-traits/

帅冕 2024-07-16 17:48:13

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

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

这个问题可能会导致具有悠久家族继承历史的类通常没有意义。

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

特征

One of the problems of PHP as a programming language is the fact that you can only have single inheritance. This means a class can only inherit from one other class.

However, a lot of the time it would be beneficial to inherit from multiple classes. For example, it might be desirable to inherit methods from a couple of different classes in order to prevent code duplication.

This problem can lead to class that has a long family history of inheritance which often does not make sense.

In PHP 5.4 a new feature of the language was added known as Traits. A Trait is kind of like a Mixin in that it allows you to mix Trait classes into an existing class. This means you can reduce code duplication and get the benefits whilst avoiding the problems of multiple inheritance.

Traits

小草泠泠 2024-07-16 17:48:13

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

请参阅 http://www.hudzilla.org/php/6_17_0.php 了解一些信息例子。

PHP does not yet support multiple class inheritance, it does however support multiple interface inheritance.

See http://www.hudzilla.org/php/6_17_0.php for some examples.

绻影浮沉 2024-07-16 17:48:13

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

PHP does not allow multiple inheritance, but you can do with implementing multiple interfaces. If the implementation is "heavy", provide skeletal implementation for each interface in a seperate class. Then, you can delegate all interface class to these skeletal implementations via object containment.

聚集的泪 2024-07-16 17:48:13

总是好的主意是创建带有函数的父类......即将所有功能添加到父类中。

并将所有使用它的类按层次结构向下“移动”。 我需要 - 重写函数,这是特定的。

Always good idea is to make parent class, with functions ... i.e. add this all functionality to parent.

And "move" all classes that use this hierarchically down. I need - rewrite functions, which are specific.

演出会有结束 2024-07-16 17:48:13

A 类扩展了 B {}

B 类扩展了 C {}

那么 A 扩展了 B 和 C

class A extends B {}

class B extends C {}

Then A has extended both B and C

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文