Categories


Archives


Recent Posts


Categories


PHP Traits

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

It’s another quick primer this time. Today we’re going to talk about a PHP 5.4+ feature called traits. While Laravel doesn’t make heavy use of traits they are sprinkled around the core codebase, so you’ll want to get familiar with them.

The Problem

Sometimes when you’re working in a class based object oriented system, you’ll find yourself in a situation where you want functionality from another class in your own class. If your class doesn’t have a parent class, you can accomplish this by extending the class whose behavior your want. Pretend you’re creating a messenger class.

class Messenger
{
}

While you’re working on the messenger class, other members of your team have written a Hello class.

class Hello
{
    public function hello()
    {
        echo "Hello","\n"
    }
}

If you want Hello‘s functionality, you can extend it.

class Messenger extends Hello
{
}

$o = new Messenger;
$o->hello();

Pretty basic object oriented stuff. However, lets say someone on your team has also written a class Goodbye.

class Goodbye
{
    public function goodbye()
    {
        echo "Goodbye","\n";
    }
}

As it stands right now, there’s no way for you to incorporate Goodbye‘s functionality into your Messenger class. That is, in PHP, a class can only have one parent class. Some languages have experimented with allowing a class to have many parents (with a feature called multiple inheritance), but multiple inheritance creates a slew of ambiguities and has a bad reputation as it complicates simpler single class inheritance.

When PHP’s core team considered this programming problem for PHP 5.4, instead of multiple inheritance they developed a language feature called “traits”.

What’s a Trait

Traits are PHP’s take on the multiple inheritance problem. Think of a trait as something in-between a class and an interface. You can define a trait in PHP with the following

trait Hello
{
}

Similar to interfaces, you can define abstract methods on traits

trait Hello
{
    abstract public function sendMessage();
}

However, different from interfaces, you can also define concrete methods on traits

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }

}    

You may not directly instantiate traits. The following program

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }
}    

$o = new Hello;

will halt with the error.

Fatal error: Cannot instantiate trait Hello

So far, traits look a lot like abstract classes. However, a class cannot extend a trait. The following program

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }
}    

class Messenger extends Hello
{
}

$o = new Messenger

will halt with the error

Fatal error: Class Messenger cannot extend from trait

Instead, a class uses traits. Consider the following program.

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }
}    

class Messenger
{
    use Hello;
    public function sendMessage()
    {
        echo 'I am going to say hello',"\n";
        echo $this->hello(),"\n";
    }
}

$o = new Messenger;
$o->sendMessage();

This program will run with the following output

I am going to say hello
Hello World

Notice that our class definition contains a

use Hello;

This tells PHP we want our class Messenger to use the trait Hello. The use keyword here has nothing to do with PHP namespaces. When use is inside a class block, PHP knows it means use a trait — when use is outside a class block, PHP knows it means alias or import a namespace.

When a classes uses a trait, it has access to all the trait’s methods, just as though it had extended from it. Notice we’re calling the hello method

echo $this->hello(),"\n";

There’s no hello defined on Messenger. Instead, hello is defined on the trait.

Multiple Magic

So far the functionality we’ve described can all be accomplished without traits. That is, other than the use operator, this all looks like plain old class inheritance. That’s about to change. Consider the following program

trait Goodbye
{
    public function goodbye()
    {
        echo "Goodbye","\n";
    }    
}

trait Hello
{
    public function hello()
    {
        echo "Hello World","\n";
    }
}    


class Messenger
{
    use Hello, Goodbye;
    public function sendMessage()
    {
        echo 'You say goodbye',"\n";
        echo $this->goodbye();

        echo 'and I say hello',"\n";
        echo $this->hello(),"\n";
    }
}

$o = new Messenger;
$o->sendMessage();

Here we’ve defined two traits. One named Goodbye, the other named Hello. Then, in our Messenger class, we’ve used both

use Hello, Goodbye;

Run the above program, and you’ll get the following output

You say goodbye
Goodbye
and I say hello
Hello World

This is the point of traits. A client programmer can pull in any trait they want into their class to adopt the trait’s functionality.

While not as powerful as multiple inheritance (i.e. you need to write your code as traits in the first place), traits cordon off the ambiguities that arise from multiple inheritance into a separate system. This allows that separate system to develop advanced features for dealing with those ambiguities without complicating existing class syntax. If you’re interested in how these ambiguities are resolved, the PHP manual has a more in depth discussion of all the features of PHP’s traits.

With PHP 5.3 finally deprecated, you’ll start seeing traits used more often. In particular, there are parts of the Laravel framework that rely on traits for their functionality. Next time we’ll have a quick primer on Laravel’s MacroableTrait.

Originally published October 19, 2014
Series Navigation<< Laravel Facade TroubleshootingLaravel’s MacroableTrait >>