Posts Tagged ‘mixins’

Mixins in PHP

August 23rd, 2006 by Ivo

One of the concepts I like in Ruby is the concept of 'mixins'. Mixins are a way of 'mixing in' functionality of other classes.

A kind of 'multiple inheritance' like approach, only without actual inheritance. It is similar to interfaces, but interfaces only tell you that an object must implement certain methods, whereas mixins also provide an implementation.

It's a bit hard to describe in a few sentences, but this article and the pragmatic programmer's guide provide a good explanation. But I'll explain the concept with examples below.

In the past, there have been attempts to apply mixins to PHP, but this, back in 2002, required quite some cumbersome code, due to the limitations that PHP4 had at that time. I was wondering if some of PHP5's new features would make this easier, and I managed to create a mixin solution with only a few lines of code.

I'll start with the example that explains the mixins, and after that, I'll show the code that makes it possible. I'll end this post with a few quirks that I encountered, for which I solicit comments to improve the solution.

Classes to mixin

Let's first create some classes that can be mixed in. I've created two, that each implement some display functionality:

 
  class Alertable
  {
    function alert()
    {
      $str = $this->toString();
      echo '<script language="javascript"> alert(''.$str.''); </script>';
    }
  }
 
  class Blinkable
  {
    function blink()
    {
      echo "<blink>".$this->toString()."</blink>";
    }
  }
 

The classes are plain and simple, they have a single method that contains the display logic. One displays a javascript alert, the other displays a blinking text. The first thing to notice here is the call to $this->toString(). There is no method called toString to be found in the class. This is no problem however, because these classes will never be used directly. They will be mixed in, in another class, and $this will point to the instance of that class after being mixed in. We only require the class that mixes in these methods, to have a toString method.

Applying the mixins

Using the mixins is fairly easy:

 
 
  class Hello extends Object
  {
    var $mixins = array("Alertable", "Blinkable");
 
    function toString()
    {
      return "Hello World";
    }
  }
 
  $o = new Hello();
  $o->alert();
  $o->blink();
 

The class Hello extends 'Object', which is a class I wrote. It makes the mixins possible. I'll get to this class later. But let's first look at the effect of applying the mixin.

In the member variable $mixins, I store an array of classes I want to mix in. Then, I create an instance of the class, and I can call alert() and blink() on this object, as if the methods were part of the Hello class. Notice how this looks quite natural? The user of the class won't notice that these methods are actually defined elsewhere, and the developer of the class has an easy way of reusing functionality.

As with inheritance, the methods are overridable: if I would still define a custom alert() method on the Hello class level, this method would be called instead of the mixin. This makes it possible to override parts of the behaviour of the mixins.

How it works

Let's take a look at how this works. The handling of the mixins is done by the Object class. There are two key elements in this solution. First, I make use of the PHP5 magic method '__call' to intercept calls to the objects, and redirect methods to the mixins when necessary. Second, there is a little known, but for mixins very important, feature in PHP, which is explained in the oop basics of the PHP manual:

"$this is a reference to the calling object (usually the object to which the method belongs, but can be another object, if the method is called statically from the context of a secondary object)."

It is a mystery to me why such a confusing concept is the first thing in the 'basics' page, but it is very useful. What this basically means that $this does not necessarily point to the current object, but to the calling object, as long as the method is called statically. You've noticed the $this reference in the Blinkable class above; What I basically did was make sure that the method in the mixin was called statically and voila, $this points to the object that is using the mixin, and the mixed in class code looks very natural, like it is a part of the class it will be mixed into.

Here is the code for my Object class:

 
  /**
    * Generic base class for all objects that want to make use of
    * mixin functionality.
    */
  class Object
  {
    private $_mixinlookup = array();
 
    // The constructor takes a look at the mixins, and creates a lookup
    // array, so upon a method call, we can quickly determine whether the
    // method was mixed in.
    function Object()
    {
      if (is_array($this->mixins))
      {
        foreach($this->mixins as $mixin)
        {
          $methods = get_class_methods($mixin);
          if (is_array($methods))
          {
            foreach($methods as $method) $this->_mixinlookup[$method] = $mixin;
          }
        }
      }
    }
 
    // The __call magic method intercepts any method that does not exist
    // and falls back to one of the mixins if they define the method that is
    // being called.
    function __call($method, $args)
    {
      if (isset($this->_mixinlookup[$method]))
      {
        $elems = array();
        for ($i=0, $_i=count($args); $i<$_i; $i++) $elems[] = "$args[$i]";
        eval("$result = ".$this->_mixinlookup[$method]."::"
            .$method."(".implode(',',$elems).");");
        return $result;
      }
      trigger_error('Call to undefined function '.$method, E_USER_WARNING);
    }
  }
 

This code won't win any performance prizes unfortunately, I had to use eval to dynamically call a static method. Other, more efficient ways should work, but I'll explain why they didn't in the 'Quirks' section below. For many types of application however, the performance penalty is low compared to the power of mixins that you get.

Practical application

Now that I've explained the concept of mixins, and how to make it work in PHP5, I'll give some examples of when to use it. In the real world, you probably won't be using Blinkables, but there is a lot of functionality you can think of that can be mixed in to make classes powerful. For example, if you have a custom Collection class, and you create methods to access all its elements, you could easily create mixins such as Sortable, Enumerable and Iterator. These would add sort, enumeration and iteration methods to your object, without cluttering your object with the code.

Also, whenever you find yourself in a situation where you think you need multiple inheritance, rethink the issue and see if mixins are suitable. Often, you'll find that mixins accomplish exactly what you hoped to accomplish if you had multiple inheritance.

Quirks

I promised to end with some quirks I ran into. You can safely skip this section if you're not interested in gory details. :-)

There was actually only one problem that I encountered that lead me to use eval() for the mixin functionality. I've talked about the $this pseudo-variable and how when called statically, $this points to the calling object. Well, even though this is documented in the first part of the oop basics in the PHP manual, it only works in the case where a method is called with the classname::methodname construction. What I would've liked to use, was call_user_func_array or even better, Andrei's Reflection API, which is much more powerful. With the reflection API, the __call method would've looked like this:

 
 
  function __call($method, $args)
  {
    if (method_exists($this, $method))
    {
      return call_user_func_array(array($this, $method), $args);
    }
    if (isset($this->_mixinlookup[$method]))
    {
      $method = new ReflectionMethod($this->_mixinlookup[$method], $method);
      return $method->invoke(NULL, $args);
    }
    trigger_error('Call to undefined function '.$method, E_USER_WARNING);
  }  
 

This would've been more clean, and probably is faster. Now why doesn't this work? First, to call the method statically, I have to pass NULL as first parameter to invoke. This leads to the following error however:

Non-object passed to Invoke()

The reason is that invoke refuses to call my static method if it has not been defined with the 'static' keyword. Easy, you would think, just make your method truly static:

 
  static function blink()
  {
    echo "<blink>".$this->toString()."</blink>";
  }
 

This leads to a different error:

Fatal error: Using $this when not in object context

This kind of invalidates the whole '$this refers to the calling object' part of the documentation. I'm not sure if I should file a bug report for this, comments are appreciated. (I guess that even if it would not throw an error, it probably wouldn't work anyway because $this would be pointing to the ReflectionMethod object instead of my object instance).

Conclusion

I've tried to demonstrate how the powerful concept of Mixins can be applied in PHP. Since it only takes a few lines of code, I guess it would not be hard to make this a standard feature of the PHP language, so I would really like to see true mixin support in PHP6. But if that doesn't happen, I hope to have provided a fairly easy way to apply the concept to your PHP classes anyway.

Feel free to use the code samples in this post, a reference to the post in your code would be appreciated but is not required.