Multiple Inheritance in PHP

Multiple Inheritance in PHP

Alex, most of the times you need multiple inheritance is a signal your object structure is somewhat incorrect. In situation you outlined I see you have class responsibility simply too broad. If Message is part of application business model, it should not take care about rendering output. Instead, you could split responsibility and use MessageDispatcher that sends the Message passed using text or html backend. I don't know your code, but let me simulate it this way:

$m = new Message();
$m->type = 'text/html';
$m->from = 'John Doe <jdoe@yahoo.com>';
$m->to = 'Random Hacker <rh@gmail.com>';
$m->subject = 'Invitation email';
$m->importBody('invitation.html');

$d = new MessageDispatcher();
$d->dispatch($m);

This way you can add some specialisation to Message class:

$htmlIM = new InvitationHTMLMessage(); // html type, subject and body configuration in constructor
$textIM = new InvitationTextMessage(); // text type, subject and body configuration in constructor

$d = new MessageDispatcher();
$d->dispatch($htmlIM);
$d->dispatch($textIM);

Note that MessageDispatcher would make a decision whether to send as HTML or plain text depending on type property in Message object passed.

// in MessageDispatcher class
public function dispatch(Message $m) {
if ($m->type == 'text/plain') {
$this->sendAsText($m);
} elseif ($m->type == 'text/html') {
$this->sendAsHTML($m);
} else {
throw new Exception("MIME type {$m->type} not supported");
}
}

To sum it up, responsibility is split between two classes. Message configuration is done in InvitationHTMLMessage/InvitationTextMessage class, and sending algorithm is delegated to dispatcher. This is called Strategy Pattern, you can read more on it here.

Is Multiple Inheritance allowed at class level in PHP?

Multiple inheritance suffers from the Diamond Problem, which has not been (agreed upon how to be) solved in PHP yet. Thus, there is no multiple inheritance in PHP.

    BaseClass
/\
/ \
ClassA ClassB
\ /
\/
ClassC

If both ClassA and ClassB defined their own method foo(), which one would you call in ClassC?

You are encouraged to either use object composition or interfaces (which do allow multiple inheritance) or - if you are after horizontal reuse - look into the Decorator or Strategy pattern until we have Traits (or Grafts or whatever they will be called then).

Some Reference:

  • [PHP-DEV] Traits,Grafts, horizontal reuse
  • [PHP-DEV] Multiple class inheritance
  • RFC: Traits for PHP
  • Traits-like Functionality in PHP now

Which is the best way of multiple inheritance in php?

As I have written in comment, I would suggest something like this:

abstract class Product
{
// Get/Set title, SKU, properties which are common for *all* products
}

Then some interfaces:

interface HavingWeight
{
// Get/Set weight
}

Traits:

trait WithWeigth
{
// Get/Set weight implementation
}

Then concrete class of Book:

class Book extends Product implements HavingWeight
{
use WithWeight;
}

Using interfaces have the very important advantage, that use can then use code like following:

if($product instanceof HavingWeight)
{
// Show weight
}

Multiple inheritance, 'need' to extend two classes

PHP doesn't support multiple inheritence. You can however rewrite your logic using traits to (sort of) simulate it.

class Field {
protected $name;
protected $value;
}

trait Dropdown {
protected $options = [];
// with functions like setOptions(), addOption(), removeOption() etc.
}

interface IntegrationPositions {
const LAYOUT_POSITION_LEFT = 'left';
const LAYOUT_POSITION_RIGHT = 'right';
}

trait Integration {
protected $layoutPosition = IntegrationPositions::LAYOUT_POSITION_LEFT;
}

class DropdownField extends Field {
use Dropdown;
}

class IntegrationField extends Field {
use Integration;
}

class DropdownIntegrationField extends Field {
use Integration,Dropdown;
}

Update: As @Adambean noted traits cannot have constants. I've therefore updated the example using an enum.

This feels strange to have to declare an enum that's meant to be internal to a trait but PHP does not seem to allow any other mechanism to achieve this as far as I know, I am open to any other ideas.

Is Multiple Inheritance allowed at class level in PHP?

Multiple inheritance suffers from the Diamond Problem, which has not been (agreed upon how to be) solved in PHP yet. Thus, there is no multiple inheritance in PHP.

    BaseClass
/\
/ \
ClassA ClassB
\ /
\/
ClassC

If both ClassA and ClassB defined their own method foo(), which one would you call in ClassC?

You are encouraged to either use object composition or interfaces (which do allow multiple inheritance) or - if you are after horizontal reuse - look into the Decorator or Strategy pattern until we have Traits (or Grafts or whatever they will be called then).

Some Reference:

  • [PHP-DEV] Traits,Grafts, horizontal reuse
  • [PHP-DEV] Multiple class inheritance
  • RFC: Traits for PHP
  • Traits-like Functionality in PHP now

Multiple Inheritance in PHP or is there a better way?

If you use php >= 5.4, you can use traits:

class Loop {
use Single, Query, Page;
}

EDIT: for older versions of php, what you are looking for is called "mixins", which is an emulation of traits. There are several ways to do that, none of which is perfect, but you will surely find something that fits your needs if you search for this keyword.

The use of traits replace the role of multiple inheritance no PHP?


Short answer

Sample Image (c) AbraCadaver

Long answer

Say you have a class Foo that uses traits A and B:

class Foo {
use A, B;
}

Where both traits have a method with a similar name, but different implementation (the implementation doesn't matter, really):

trait A {
public function bar() {
return true;
}
}

trait B {
public function bar() {
return false;
}

Traits work by extending the class horizontally. Simply put - just adding any new contents to the class. And all works fine till there's any doubling in trait methods and properties. Then you have yourself a fatal error if this conflict is not explicitly resolved.

The sweet part is, you can resolve this conflict by specifying which method from which trait to use:

class Foo {
use A, B {
B::bar insteadof A;
}
}

You can also save the other method from oblivion by using alias for it:

class Foo {
use A, B {
B::bar insteadof A;
A::bar as barOfA;
}
}

The manual has traits farely well documented, go check it out.

Multiple inheritance + Multi-Level inheritance in PHP: Is there a pattern or possibility to solve constructor inheritance?

The problem is that traits cannot be instantiated so __construct() is kind of meaningless.

The best approach is to initialize your member variables using the class constructor; that's why constructors exist.

If you want to initialize some members that are declared in a trait, then have a trait function and call it in the appropriate class constructor, example:

trait Q {
protected $a;
public function initQ() { $this->a = "whatever"; }
}

class MyClass {
use Q;

public function __construct() {
$this->initQ();
}
}


Related Topics



Leave a reply



Submit