When to use traits in PHP

Traits were introduced in PHP 5.4 on March 1st, 2012. Traits provide support for horizontal code reuse, but what is meant by horizontal reuse? Should we be using traits? This article explores what traits are and when we should use them as well as when we should not.

What is a trait?

Anthony Ferrara said it best when he wrote in a StackOverflow answer that:

[Traits are] basically just automated copy and paste*.

Unlike manual copy and paste, traits ensure all copies of the code remain consistent—changes to a trait are reflected in all classes using that trait, which is desirable when each copy represents the same idea. Since everything inside a trait is copied directly into a class, even private members of a trait are accessible to the class composing that trait.

Traits are different from includes because include statements can only contain complete classes whereas traits can only represent class fragments. Even if our trait could conceptually function by itself, it is unable to do so because we cannot create instances of traits – the trait must be composed by a class.

Traits were added to PHP to provide horizontal reuse. Horizontal reuse is the corollary of vertical reuse, better known as inheritance. When we create a class hierarchy by extending classes it can be said we are extending vertically. When we compose traits in our classes it can be said we are extending horizontally. Although a class can only extend one other class we can use as many traits as desired.

As Ferrara points out, traits in PHP are actually mixins, which means we can not only declare behaviour but also state in a PHP trait. While the formal definition of a trait permits only method declarations, in PHP we can also define member variables, which means they're actually mixins!

Mixins are found in many modern scripting languages including Ruby, Python, JavaScript and, of course, PHP; however they seldom make an appearance in compiled languages like Java and C variants. Even amongst languages that support them, we can usually express solutions to our problems without using traits at all. So do we even need traits? When should we use a trait?

* Note that traits are not strictly the same as copy and paste. A class can compose two traits: one which specifies an abstract method and another which specifies a concrete implementation of the same method. Normally, if we specify the same method name twice in our class we would get an error stating we cannot redeclare the method, but when this ocurrs with traits PHP will resolve the conflict so long as the method signatures are compatible.

When should we use traits?

Whenever we have code duplicated in our application we should find a way to eliminate the duplication and reduce our technical debt. Code duplicated n times must be modified in n places when we need to make a change; therefore we would like to ensure that n = 1 such that any change is only made in one place. By reusing code we eliminate duplication.

Traits are just one option for code reuse; we should also consider inheritance and composition. To help decide which solution to use we can apply the following rule.

Traits should be used when we want to reuse code but inheritance and composition are both unsuitable.

Following this rule we can decide how to implement code reuse. The decision tree for this rule looks like the following.

Using this model we can explain, for all cases, why traits are an appropriate or inappropriate solution for code reuse. Let's apply this model to a real example.

Reusing entity fields

Notice that, according to our decision tree, a trait is always the last option we consider. We always consider inheritance and composition first; a trait is a last resort when no other option is available to us - and this is good - because traits are difficult to test in silo so we should use them sparingly.

Why not traits

  • You don't need a trait! Why not just a static class.
  • They're hard to test.
  • They hide functionality from the reader.

Summary

We can summarise usage of traits by applying the following rule: do not use traits unless absolutely necessary. Some problems that can only be solved with traits and these are the only problems traits should be used to solve. It is therefore our responsibility to first determine whether a problem can be solved without traits, and if it can, prefer those solutions.