Trait-like Functionality for PHP

Eliminating repetition and encouraging code reuse are central tenets of software development.

PHP’s lack of multiple inheritance means sometimes there are tough design choices to be made when similar functionality is required in separate branches of the class heirarchy.

As proposed in the Horizontal Reuse for PHP RFC, traits offer a means of duplicating functionality in independent classes.

In this post, I’ll be detailing an approach that offers trait-like functionality to your objects in an intuitive and straightforward way.

Why Traits?

So why do we need traits, anyway?

PHP is a single inheritance language, meaning that each class can only extend one other. This allows us to build logical class hierarchies which extend functionality in a “vertical” way.

For example, the Lion class might extend a Mammal class, which in turn extends Animal. Or Eagle could extend Bird which also extends Animal. Or maybe Locust -> Insect -> Animal. Pretty logical stuff.

The problem arises when an overlap in functionality occurs in different branches of the class hierarchy. Say, for instance, we are interested how the different species of animals get around. Both our locust and our eagle are flyers, but they’re in separate branches of the class hierarchy. So where do we put our getAverageFlyingSpeed() and getMaxAltitude() methods?

At this point we have two choices. The first option is to move the methods into a class higher up the tree, like the Animal class. The drawback here is that we end up with a superclass containing scores of methods that are redundant for all but a handful of concrete classes. Not good.

The second option is to simply duplicate the code in the classes where it is required. This, however, flies in the face of the DRY principle and can make the code harder to maintain.

What we need is a means of re-using code other than simple inheritance. That’s where traits come in.

Traits are classes containing a collection of methods, usually relating to a particular behaviour. Since they can be applied at will, they are independent of the inheritance hierarchy. Conceptually, traits are very much like interfaces with actual methods.

Using our example, we could create a Flight trait which could be applied to both the eagle and locust classes, giving the desired functionality without the drawbacks associated with single class inheritance.

I Want Them Now!

Whoa there! One step at a time :)

Hopefully the benefits of traits for a language like PHP should be pretty clear.

Sadly, they’re unlikely to included in any PHP release in the near future (although you can download Stefan Marr’s patched PHP versions).

In the meantime, I’ve put together an approach that offers trait-like functionality and is reasonably easy to implement. There’s too much code to include it all in this post, so you can download the source code here.

The Code

In this setup, traits are classes that extend My_Trait_Abstract. To illustrate this, we’ll create a trait, My_Trait_Json, that adds a toJson method to any class it is added to.

<?php
 
require_once 'My/Trait/Abstract.php';
 
class My_Trait_Json extends My_Trait_Abstract
{
    /**
     * @param My_Object $object
     * @return string
     */
    public function toJson( My_Object $object )
    {
	$json = json_encode( get_object_vars( $object ) );
 
	return $json;
    }
}

Every public method in a trait class will be available to the classes to which they’re added. Also notice that the first parameter of each method must be an instance of the object which it is working on… that’ll make more sense later.

The abstract trait class uses reflection to examine each trait to find public methods – only the constructor and getMethods are excluded. _retrieveMethods is lazy-loaded and only carried out on a call to getMethods.

    /**
     * @return array
     */
    public function getMethods()
    {
	if ( NULL === $this->_methods ) {
	    $this->_methods = array();
	    $this->_retrieveMethods();
	}
 
        return $this->_methods;
    }
 
    /**
     * @return My_Trait_Abstract
     */
    protected function _retrieveMethods()
    {
	// Exclude the constructor and public getMethods method
	$exclude = array(
	    '__construct',
	    'getMethods'
	);
	$refObject = new ReflectionObject( $this );
 
	foreach ( $refObject->getMethods() as $method ) {
	    if ( $method->isPublic() ) {
		$name = $method->getName();
		if ( !in_array( $name, $exclude ) ) {
		    $this->_addMethod( $name );
		}
	    }
	}
 
        return $this;
    }
 
    /**
     * @param string $method Name of method
     * @return My_Trait_Abstract
     */
    protected function _addMethod( $method )
    {
	if ( !method_exists( $this, $method ) ) {
	    throw new Exception( "Method '$method' does not exist" );
	}
	if ( array_search( $method, $this->_methods ) ) {
	    throw new Exception( "Duplicate method '$method'" );
	}
	$this->_methods[] = $method;
 
	return $this;
    }

The Glue

With our trait classes in order, we now need a way to access their functionality.

To do this, we’ll use a __call magic method in our object superclass and a “trait broker” – a class which maintains trait instances and their methods. (For Zend Framework users, the TraitBroker works in a very similar way to ZF’s PluginBroker.)

Here’s our object superclass:

<?php
 
abstract class My_Object
{
    /**
     * @var My_TraitBroker
     */
    static private $_traitBroker;
 
    /**
     * @param   string $method
     * @param   array $args
     * @return  mixed
     */
    public function __call( $method, $args )
    {
	// Check trait broker for extended functionality
	$class = get_class( $this );
	$traitBroker = $this->getTraitBroker();
	// Check whether class traits have been initialised
	if ( !$traitBroker->isClassRegistered( $class ) ) {
	    $this->_initTraits();
	    $traitBroker->registerClass( $class );
	}
	if ( $traitBroker->hasMethod( $method, $class ) ) {
	     return $traitBroker->callMethod( $method, $this, $args );
	}
        throw new Exception(
	    "Invalid method ". get_class( $this ) ."::". $method ."(". print_r( $args, 1 ) .")"
	);
    }
 
    //////////////////////////
    // Trait Functionality //
    //////////////////////////
 
    /**
     * @return My_TraitBroker
     */
    public function getTraitBroker()
    {
	if ( !isset( self::$_traitBroker ) ) {
	    require_once 'My/TraitBroker.php';
	    $broker = new My_TraitBroker();
	    self::$_traitBroker = $broker;
	}
 
	return self::$_traitBroker;
    }
 
    /**
     * @param My_TraitBroker
     * @return void
     */
    protected function _initTraits()
    {
	// Do nothing - child classes add traits here
    }
 
    /**
     * @param My_Trait_Abstract
     * @return My_Object
     */
    protected function _registerTrait( My_Trait_Abstract $trait )
    {
	$this->getTraitBroker()->registerTrait( $trait, get_class( $this ) );
 
	return $this;
    }
}

The __call method will trap calls to non-existent methods, at which point the TraitBroker is instantiated and any traits for the class are initialised. All that our child classes need to do is extend _initTraits and specify which traits it uses.

Internally, the TraitBroker takes care of getting the right trait instance and calling the requested method.

    /**
     * Call the trait method
     *
     * @param string $method
     * @param string $objectClass
     * @param My_Object $object
     * @param array $args
     * @return mixed
     */
    public function callMethod( $method, My_Object $object, array $args = array() )
    {
	$objectClass = get_class( $object );
        if ( !$this->hasMethod( $method, $objectClass ) ) {
            throw new My_Exception( "Cannot call method '$method' - not registered." );
        }
        $trait = $this->_methods[ $objectClass ][ $method ];
        $args = array_merge( array( $object ), $args );
 
        $result = call_user_func_array(
            array( $trait, $method ),
            $args
        );
 
        return $result;
    }

As you can see, the trait class is passed the calling object and any arguments trapped by the __call method. This does mean that traits can only manipulate an object’s public methods and properties – how much of a limitation this is will depend on your exact implementation. In practice, I’ve found that this is usually easy to work around.

Now all we need is a class extending My_Object that registers the Json trait.

<?php
 
require_once 'My/Object.php';
require_once 'My/Trait/Json.php';
 
class My_Model extends My_Object
{
    /**
     * @var string
     */
    public $foo = 'bar';
 
    /**
     * @var string
     */
    public $bat = 'baz';
 
    /**
     * @return void
     */
    protected function _initTraits()
    {
	// Register traits
	$this->_registerTrait( new My_Trait_Json() );
    }
}

The protected _registerTrait method takes care of registering the the trait with the broker.

The client code and output is exactly the same as if the toJson method were part of our model.

<?php
 
require_once 'My/Model.php';
 
$object = new My_Model();
 
var_dump( $object->toJson() );
 
// outputs
// string(25) "{"foo":"bar","bat":"baz"}"

A few notes:

  • Since the _initTraits method is inherited by child classes like any other, traits will also be inherited. You can override this by putting a custom (or empty) _initTraits method in the child class.
  • My_Trait_Abstract allows for an associative array of options to be passed to its constructor. For these to have any effect, you’ll need to add a protected _setOptionName method in your trait class.
  • Although I haven’t benchmarked it, there is sure to be a small performance hit using traits against hard coded methods due to the use of __call and reflection. Chances are it’ll be pretty insignificant in a real world application though.

Although the JSON trait is a very simple example, I hope that you can see the potential value in this kind of setup. I’ve only been tinkering with it for a couple of weeks, but I’ve already used traits for things like addresses and HTML metadata (for a content management system). In fact, I’ve started putting functionality into traits that I’d normally hard code into classes, purely because the potential for re-use is so much greater.

Final Thoughts

PHP is at an exciting stage in its development and it will be interesting to see how the language changes over the next year or so, especially towards the release of PHP 6. But until new features like traits and grafts are available within the language itself, it’s up to us to come up with other ways around the limitations we face.

This approach to implementing traits is one way in which you can help to reduce repetition and promote code reuse within your applications.

Don’t forget that you download the complete source code.

Please let me know what you think by leaving a comment :)

Social Bookmarks
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay

Tags: , , ,

5 Responses to “Trait-like Functionality for PHP”

  1. Aaron McGowan 06. Apr, 2010 at 8:23 am #

    Very cool and an interesting thought Steve.

    Aaron

  2. Ralf 12. Apr, 2010 at 3:51 pm #

    Hi Steve!

    Interesting indeed. It seems like a generic object plug-in system. Sure, this fits OOPLs other then PHP as well.

    I recently sketched out a solution witch uses code generation for mixing-in other classes functionality. While this approach does not make massive use of PHP’s magic method mechanics, it enables the developer to have code assist from an IDE. But this is only kind of a side effect. The post is really about generating type-checking property accessors.

    Have a look: http://blog.uebercode.org/2010/03/static-annotation-driven-php-code.html

    I would appreciate some comments on this ;)

    Regards!

  3. Steve 14. Apr, 2010 at 4:12 pm #

    Thanks Ralf!

    I like your reflection/code generation approach and the “compatibility” with code assist is a big plus. Sadly, one downside of complex OO approaches is that most IDEs just can’t handle them!

    I haven’t played around with Zend_CodeGenerator yet but it looks like an interesting component.

    Thanks again for the comment.

  4. Harald Stowasser 18. May, 2010 at 9:50 am #

    A ‘Trait’ is what we call a Mixin.

    But I dislike the usage of __call for the trait-methods because a Reflection cant list them. This prevent “code completion” and “class browsers”)

    I also would say it is not necessary to make the access to $_traitBroker private. Because you provide access to the whole Object via getTraitBroker.

    So you can make a cleaner Interface with lesser code and no ‘magic’

    • Steve 18. May, 2010 at 10:28 am #

      Hey Harald, thanks for the comment.

      >> A ‘Trait’ is what we call a Mixin.
      Who/what is we? :)

      As I understand it, Mixins are more complex than traits and carry some of the same problems as mulitple inheritance. At least as far as PHP goes, the terminology is all a bit vague until something actually gets implemented.

      >> But I dislike the usage of __call for the trait-methods because a Reflection cant list them. This prevent “code completion” and “class browsers”)
      Agreed – this approach does have some drawbacks. That said, so do the alternatives.

      If you use a code generation technique as suggested by Ralf, you’re still duplicating code and need to re-run the code generation whenever one of the traits/plugins/mixins is modified. The only way we’re likely to see a completely “clean” implementation is if/when some form of horizontal code re-use becomes a feature of PHP.

      >> I also would say it is not necessary to make the access to $_traitBroker private. Because you provide access to the whole Object via getTraitBroker.
      A tiny implementation detail, but yes, you’re right.

      >> So you can make a cleaner Interface with lesser code and no ‘magic’
      How? Perhaps I’m missing something, but I don’t see what your suggestion is?

Leave a Reply