PHP Traits

Traits have been available for use in PHP since version 5.4 so nothing I write here is going to be particularly cutting edge or new. In fact, I seem to recall seeing a talk about traits in the PHP UK conference back in 2013 (which you can actually see on YouTube).

The problem is, that I have spent most of my career to date working on PHP 5.3. So the use of traits has been impossible. Now that I am up to date, I have been looking back at some of the introduced features of previous versions that I may have missed. So today, traits.

What is a trait?

According to the PHP manual:

A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

It is a way of organising functions that you want to reuse but don't want to have the constraints of wrapping them in a class.

I actually found it hard to put together a good example that helps explain traits, but with some help from Chris Hoult and Andreas Heigl, I was able to come up with something.

Imagine you had a couple of classes, ParentClassA and ParentClassB:

class ParentClassA {
    public function parentFunctionA()
    {
        return true;
    }
}

class ParentClassB {
    public function parentFunctionB()
    {
        return true;
    }
}

And lets say each one has a child class that inherits from them:

class ClassA extends ParentClassA
{        
    public function classAFunction()
    {
        return true;
    }
}

class ClassB extends ParentClassB
{        
    public function classBFunction()
    {
        return true;
    }
}

So far so good.

Now imagine you had an interface:

interface InterfaceA {
    public function functionA();
    public function functionB();
}

And you want ClassA and ClassB to implement it. You can adjust their code accordingly:

class ClassA extends ParentClassA implements InterfaceA
{
    public function classAFunction()
    {
        return true;
    }
}

class ClassB extends ParentClassB implements InterfaceA
{
    public function classBFunction()
    {
        return true;
    }
}

If you try to run this code now, you would get an error telling you that you have to implement the functions from the interface that you are using.

This is easy, you just add the functions within each class that implements it. But what if you want each class to implement it in the exact same way?

In this example, you could solve this by having a super class that ParentA and ParentB both inherit from that provides the implementation.

This is fine, but it means that this class will always have to be in your class hierarchy even when it doesn't make sense for it to be so.

A trait allows you to organise some reusable functions and pass them around different classes without worrying about inheritance. This is how it looks:

trait interfaceImplementation {
    public function functionA()
    {
        return true;
    }
    public function functionB()
    {
        return true;
    }
}

It is declared and coded exactly like a class would be except it uses the keyword, trait. Now we can import these functions into our classes:

class ClassA extends ParentClassA implements InterfaceA
{
    use interfaceImplementation;

    public function classAFunction()
    {
        return true;
    }
}

class ClassB extends ParentClassB implements InterfaceA
{
    use interfaceImplementation;

    public function classBFunction()
    {
        return true;
    }
}

Both these classes now have the functions that are in the traits. You can use them as though they were part of the class:

$b = new ClassB;
var_dump($b->functionA());

This also satisfies the implementation of the interface. I suppose it can be compared to include and require except instead of it being an entire file being included, it is just the functions of the trait.

As you can see, traits are a very powerful tool to have to help with code reuse. It is not perfect for every situation (certainly not in this example that I have tried to put together), but certainly something to think about if you find that you need to reuses specific functions all over the place without using static functions or abstract classes.

Check out the documentation as it tells you what else you can do with them.


© 2012-2017