views:

7242

answers:

13

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

+4  A: 

No.

You should get b and c as object inside the class a if you need function of both. If you need to have multiple inheritance by inheritance you should try to seperate the concept with Interface.

Precision: class a can inherit of class b. Class b can inherit of class c. This way a will be able to access from a function of c but you must really be sure that this as a logic between all these class. Usually, it's better to try avoiding inheritance for no good reason.

Daok
I disagree, he can do what he asks.
George Jempty
class a extends b extends c ? show me that.
Daok
a class cannot extends 2 class. He has to do what I said or what you said in your post. But not what he said.
Daok
George, I think you misunderstood what E3 wants to do.
Franck
I don't think I misunderstood, he posted pseudo code, below is how you do. I'm not saying its optimum, but it does show how to do precisely what was asked.class c {}class b extends c {}class a extends b {}
George Jempty
I think he wants a to inherit from b and c, which is not possible. That's the way I understand it anyway. :)
Franck
I understand the samething as sork.
Daok
his edit shows that he is asking for multiple inheritance which is not possible in PHP
tharkun
yep i'm absolutely confused as to how the interface thing is a solution. i can understand the hierarchical extends but interface i am at a loss
E3
A: 

Yes absolutely, just try it, in the way you describe: class b extending from c, and class a extending from b.

But what you cannot do, is have class a extend both b & c

George Jempty
A: 

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.

Tader
Yet? I doubt they will do it. Modern OO discourage multiple inheritance, it can be messy.
PhiLho
+2  A: 

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

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

adam
+4  A: 

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.

Michael Borgwardt
A: 

you are asking if multiple inheritance is possible in PHP.

the answer is: NO

Is PHP likely to implement multiple inheritance?

someone else answered this qustion on PHPBuilder.com with:

When there's peace in Jerusalem.

which I would interpret as : it's possible if many factors change but not very likely in the near future.

tharkun
+10  A: 

Answering your edit :

If you really want to fake multiple inheritance, 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"

Franck
I actually quite like the idea of extending the classAre there any known limitations of doing it this way?
E3
No limitations as far as I know, PHP is a very permissive language for little hacks like this. :) As others have pointed out, it's not the proper OOP way of doing it though.
Franck
A: 

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.

petr k.
+2  A: 

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();
    }
}
PhiLho
+4  A: 

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.

Sam
A: 

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.

Joel
A: 

everyone here who is saying to use "multiple interfaces to create multi-inheritance" are idiots. plain and simple.

if you have a function that exists inside of another class and you don't have acesss to it unless you do something stupid and very costly to performance, you should realize that, hey, my class isn't designed properly! Not, "what can I make php do to work around my shit design".

Doing "hacks" to make code work just because you're too lazy to write it successfully only leads to disaster and a complete misunderstanding of the language. This is true for ANY language.

I would recommend reviewing the code, and coming up with a NEW design plan, and rewriting it based on one that is more scalable. Otherwise, you're always going to have this issue.

Jon
A: 

Jon,

What if we don't have control over what was originally written? Example, I'm creating a wordpress widget which their class is structured in a why where it has to extend the WP_Widget class. See below:

You can see my comment in there where I want to put my code which is in the widget function...

class productWidget extends WP_Widget {
/** constructor */
function productWidget() {
    parent::WP_Widget(false, $name = 'Products', array('description'=>'Use this widget to display products'));  
}



/** @see WP_Widget::widget */
function widget($args, $instance) { 
    extract( $args );
    $title = apply_filters('widget_title', $instance['title']);

    print $before_widget;
    if ($title)
    {
        print $before_title . $title . $after_title;
    }

    //**************************************************************
    //**************************************************************

    // RIGHT HERE IS WHERE I WANT TO USE FUNCTIONS FROM ANOTHER CLASS
    // THAT I CREATED. WHICH THOSE FUNCTIONS CONNECT TO AN EXTERNAL API, ETC.

    // Example, I would like to do something like this:
    $this->getProducts();


    //**************************************************************
    //**************************************************************
    print $after_widget;
}

/** @see WP_Widget::update */
function update($new_instance, $old_instance) {             
    $instance = $old_instance;
    $instance['title'] = strip_tags($new_instance['title']);
    $instance['display_type'] = strip_tags($new_instance['display_type']);
    $instance['product_limit'] = strip_tags($new_instance['product_limit']);
    return $instance;
}

/** @see WP_Widget::form */
function form($instance) {              
    $title = (!empty($instance['title'])) ? (esc_attr($instance['title'])) : ('');
    $display_type = (!empty($instance['display_type'])) ? (esc_attr($instance['display_type'])) : ('');
    $product_limit = (!empty($instance['product_limit'])) ? (esc_attr($instance['product_limit'])) : ('');

    // Title
    $input = gen_content('input', '', array('class'=>'widefat', 'id'=>$this->get_field_id('title'), 'name'=>$this->get_field_name('title'), 'type'=>'text', 'value'=>$title));
    print gen_content('label', _e('Title:') . $input, array('for'=>$this->get_field_id('title')));

    print "<br/>";
    print "<br/>";

    // Display Type
    $options = '';
    $options .= ($display_type == 'newest') ? (gen_content('option', 'Newest Products', array('value'=>'newest', 'selected'=>'selected'))) : (gen_content('option', 'Newest Products', array('value'=>'newest')));
    $options .= ($display_type == 'popular') ? (gen_content('option', 'Most Popular', array('value'=>'popular', 'selected'=>'selected'))) : (gen_content('option', 'Most Popular', array('value'=>'popular')));
    $input = gen_content('select', $options, array('class'=>'widefat', 'id'=>$this->get_field_id('display_type'), 'name'=>$this->get_field_name('display_type')));
    print gen_content('label', _e('Display Type:') . $input, array('for'=>$this->get_field_id('display_type')));

    print "<br/>";
    print "<br/>";

    // Limit
    $options = '';
    for ($i=1; $i<=10; $i++)
    {
        if ($i == $product_limit) { $options .= gen_content('option', $i, array('value'=>$i, 'selected'=>'selected')); }
        else { $options .= gen_content('option', $i, array('value'=>$i)); }
    }
    $input = gen_content('select', $options, array('class'=>'widefat', 'id'=>$this->get_field_id('product_limit'), 'name'=>$this->get_field_name('product_limit')));
    print gen_content('label', _e('Limit:') . $input, array('for'=>$this->get_field_id('product_limit')));
}}

Thoughts? I know I could always add my functions to this class, but I'm using them for other things, and besides that, I do not like to have duplicate functions, that just gets sloppy.

Thanks,

Luke

Luke
This is not a message board. Create a new question for this.
dbemerlin