هل يمكنني تمديد فئة باستخدام أكثر من فئة واحدة في PHP؟

StackOverflow https://stackoverflow.com/questions/356128

  •  21-08-2019
  •  | 
  •  

سؤال

إذا كان لدي عدة فصول لها وظائف أحتاجها ولكن أرغب في تخزينها بشكل منفصل للتنظيم ، فهل يمكنني تمديد فصل إلى حد ما؟

بمعنى آخر class a extends b extends c

تحرير: أعرف كيفية تمديد الفصول الأولى في كل مرة ، لكنني أبحث عن طريقة لتوسيع فصل دراسي على الفور باستخدام فصول أساسية متعددة - AFAIK لا يمكنك القيام بذلك في PHP ولكن يجب أن تكون هناك طرق حوله دون اللجوء إلى ذلك class c extends b, class b extends a

هل كانت مفيدة؟

المحلول

الرد على تحريرك:

إذا كنت ترغب حقًا في زيادة الميراث المتعدد ، فيمكنك استخدام الوظيفة السحرية __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"

نصائح أخرى

لا يمكن أن يكون لديك فصل يمتد فئتين أساسيتين. لا يمكن أن يكون لديك.

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

وهلم جرا.

يمكنك استخدام السمات ، والتي ، نأمل أن تكون متاحة من PHP 5.4.

السمات هي آلية لإعادة استخدام التعليمات البرمجية بلغات ميراث واحدة مثل PHP. تهدف السمة إلى تقليل بعض القيود المفروضة على الميراث الفردي من خلال تمكين المطور من إعادة استخدام مجموعات من الأساليب بحرية في العديد من الطبقات المستقلة التي تعيش في التسلسلات الهرمية الطبقية المختلفة. يتم تعريف دلالات الجمع بين السمات والفئات بطريقة ما ، مما يقلل من التعقيد وتجنب المشكلات النموذجية المرتبطة بالميراث والخلطات المتعددة.

يتم الاعتراف بها لإمكاناتها في دعم تكوين وإعادة استخدام أفضل ، وبالتالي تكاملهم في إصدارات أحدث من اللغات مثل Perl 6 و Squeak و Scala و Slate و Fortress. كما تم نقل السمات إلى Java و C#.

معلومات اكثر: https://wiki.php.net/rfc/traits

لا يُقصد من الفصول أن تكون مجرد مجموعات من الأساليب. من المفترض أن يمثل الفصل مفهومًا مجردًا ، مع كل من الحالة (الحقول) والسلوك (الأساليب) الذي يغير الحالة. إن استخدام الميراث لمجرد الحصول على بعض السلوك المطلوب يبدو وكأنه تصميم سيئ OO ، والسبب بالضبط في العديد من اللغات لا تسمح براالة متعددة: من أجل منع "الميراث السباغيتي" ، أي توسيع 3 فئات لأن كل منها لديه طريقة تحتاجها ، وينتهي بها الأمر الفصل الذي يرث 100 طريقة و 20 حقل ، ولكن لا يستخدم سوى 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. تحتاج OtherClass إلى الحصول على وظيفة عامة تسمى "setextendee" لإعداد هذه العلاقة. ثم ، إذا تم استدعاء الأساليب من $ FOO ، فيمكنه الوصول إلى $ FOO داخليًا. ومع ذلك ، لن تحصل على إمكانية الوصول إلى أي طرق/متغيرات خاصة/محمية مثل فئة ممتدة حقيقية.

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 

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;

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

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

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.

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.

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/

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.

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.

Not knowing exactly what you're trying to achieve, I would suggest looking into the possibility of redesigning you application to use composition rather than inheritance in this case.

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.

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

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

class A extends B {}

class B extends C {}

Then A has extended both B and C

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top