Using Traits in PHP 5.4

Share this article

Minimizing code duplication through better organization and code reuse is an important goal of Object Oriented Programming. But in PHP it can sometimes be difficult because of the limitations of the single inheritance model it uses; you might have some methods that you would like to use in multiple classes but they may not fit well into the inheritance hierarchy. Languages like C++ and Python allow us to inherit from multiple classes which solves this problem to some extent, and mixins in Ruby allows us to mix the functionality of one or more classes without using inheritance. But multiple inheritance has issues such as the Diamond Problem problem, and mixins can be a complex mechanism to work with. In this article I will discuss traits, a new feature introduced in PHP 5.4 to overcome such issues. The concept of traits itself is nothing new to programming and is used in other languages like Scala and Perl. They allows us to horizontally reuse code across independent classes in different class hierarchies.

What a Trait Looks Like

A trait is similar to an abstract class which cannot be instantiated on its own (though more often it’s compared to an interface). The PHP documentation defines traits as follows:
Traits is a mechanism for code reuse in single inheritance languages such as PHP. 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.
Let’s consider this example:
<?php
class DbReader extends Mysqli
{
}

class FileReader extends SplFileObject
{
}
It’d be a problem if both classes needed some common functionality, for example making both of them singletons. Since PHP doesn’t support multiple inheritance, either each class will have to implement the necessary code to support the Singleton pattern or there will be an inheritance hierarchy that doesn’t make sense. Traits offer a solution to exactly this type of problem.
<?php
trait Singleton
{
    private static $instance;

    public static function getInstance() {
        if (!(self::$instance instanceof self)) {
            self::$instance = new self;
        }
        return self::$instance;
    }
}

class DbReader extends ArrayObject
{
    use Singleton;
}

class  FileReader
{
    use Singleton;
}
The trait Singleton has a straight forward implementation of the Singleton pattern with a static method getInstance() which creates an object of the class using this trait (if it’s not already created) and returns it. Let’s try to create the objects of these classes using the method getInstance().
<?php
$a = DbReader::getInstance();
$b = FileReader::getInstance();
var_dump($a);  //object(DbReader)
var_dump($b);  //object(FileReader)
We can see that $a is an object of DbReader and $b is an object of FileReader, but both are now behaving as singletons. The method from Singleton has been horizontally injected to the classes using it. Traits do not impose any additional semantics on the class. In a way, you can think of it as a compiler-assisted copy and paste mechanism where the methods of the trait is copied into the composing class. If we were simply subclassing DbReader from a parent with a private $instance property, the property wouldn’t be shown in the dump of ReflectionClass::export(). And yet with traits, there it is!
Class [  class FileReader ] {
  @@ /home/shameer/workplace/php54/index.php 19-22

  - Constants [0] {
  }
  - Static properties [1] {
    Property [ private static $_instance ]
  }
  - Static methods [1] {
    Method [  static public method instance ] {
      @@ /home/shameer/workplace/php54/index.php 6 - 11
    }
  }
  - Properties [0] {
  }
  - Methods [0] {
  }
}

Multiple Traits

So far we have used only one trait with a class, but in some cases we may need to incorporate the functionality of more than one trait.
<?php
trait Hello
{
    function sayHello() {
        echo "Hello";
    }
}

trait World
{
    function sayWorld() {
        echo "World";
    }
}

class MyWorld
{
    use Hello, World;
}

$world = new MyWorld();
echo $world->sayHello() . " " . $world->sayWorld(); //Hello World
Here we have two traits, Hello and World. Trait Hello is only able to say “Hello” and trait World can say “World”. In the MyWorld
class we have applied Hello and World so that the MyWorld object will have methods from both traits and be able to say “Hello World”.

Traits Composed of Traits

As the application grows, it’s quite possible that we will have a set of traits which are used across different classes. PHP 5.4 allows us to have traits composed of other traits so that we can include only one instead of a number of traits in all these classes. This lets us rewrite the previous example as follows:
<?php
trait HelloWorld
{
    use Hello, World;
}

class MyWorld
{
    use HelloWorld;
}

$world = new MyWorld();
echo $world->sayHello() . " " . $world->sayWorld(); //Hello World
Here we have created the trait HelloWorld, using traits Hello and World, and included it in MyWorld. Since the HelloWorld trait has methods from the other two traits, it’s just the same as if we had including the two traits in the class ourselves.

Precedence Order

As I’ve already mentioned, traits work as if their methods have been copied and pasted into the classes using them and they are totally flattened into the classes’ definition. There may be methods with the same name in different traits or in the class itself. You might wonder which one will be available in the object of child class. The precedence order is:
  1. the methods of a trait override inherited methods from the parent class
  2. the methods defined in the current class override methods from a trait
This is made clear in the following example:
<?php
trait Hello
{
    function sayHello() {
        return "Hello";
    }

    function sayWorld() {
        return "Trait World";
    }

    function sayHelloWorld() {
        echo $this->sayHello() . " " . $this->sayWorld();
    }

    function sayBaseWorld() {
        echo $this->sayHello() . " " . parent::sayWorld();
    }
}

class Base
{
    function sayWorld(){
        return "Base World";
    }
}

class HelloWorld extends Base
{
    use Hello;
    function sayWorld() {
        return "World";
    }
}

$h =  new HelloWorld();
$h->sayHelloWorld(); // Hello World
$h->sayBaseWorld(); // Hello Base World
We have a HelloWorld class derived from Base, and both classes have a method named sayWorld() but with different implementations. Also, we have included the trait Hello in the HelloWorld class. We have two methods, sayHelloWorld() and sayBaseWorld(), the former of which calls sayWorld() which exists in both classes as well as in the trait. But in the output, we can see the one from the child class was invoked. If we need to reference the method from the parent class, we can do so by using the parent keyword as shown in the sayBaseWorld() method.

Conflict Resolution and Aliasing

When using multiple traits there may be a situation where different traits use the same method names. For example, PHP will give a fatal error if you try to run the following code because of conflicting method names:
<?php
trait Game
{
    function play() {
        echo "Playing a game";
    }
}

trait Music
{
    function play() {
        echo "Playing music";
    }
}

class Player
{
    use Game, Music;
}

$player = new Player();
$player->play();
Such trait conflicts aren’t resolved automatically for you. Instead, you must choose which method should be used inside the composing class using the keyword insteadof.
<?php
class Player
{
    use Game, Music {
        Music::play insteadof Game;
    }
}

$player = new Player();
$player->play(); //Playing music
Here we have chosen to use the play() method of the Music trait inside the composing class so the class Player will play music, not a game. In the above example, one method has been chosen over the other from two traits. In some cases you may want to keep both of them, but still avoiding conflicts. It’s possible to introduce a new name for a method in a trait as an alias. An alias doesn’t rename the method, but offers an alternate name by which it can be invoked. Aliases are created using the keyword as.
<?php
class Player
{
    use Game, Music {
        Game::play as gamePlay;
        Music::play insteadof Game;
    }
}

$player = new Player();
$player->play(); //Playing music
$player->gamePlay(); //Playing a game
Now any object of class Player will have a method gamePlay(), which is the same as Game::play(). Here it should be noted that we have resolved any conflicts explicitly, even after aliasing.

Reflection

The Reflection API is one of the powerful features of PHP to analyze the internal structure of interfaces, classes, and methods and reverse engineer them. And since we’re talking about traits, you might be interested to know about the Reflection API’s support for traits. In PHP 5.4, four methods have been added to ReflectionClass to get information about traits in a class. We can use ReflectionClass::getTraits() to get an array of all traits used in a class. The ReflectionClass::getTraitNames() method will simply return an array of trait names in that class. ReflectionClass::isTrait() is helpful to check if something is a trait or not. In the previous section we discussed having aliases for traits to avoid collisions due to traits with the same name. ReflectionClass::getTraitAliases() will return an array of trait aliases mapped to its original name.

Other Features

Apart from the above mentioned, there are other features that makes traits more interesting. We know that in classical inheritance the private properties of a class can’t be accessed by child classes. Traits can access the private properties or methods of the composing classes, and vice versa! Here is an example:
<?php
trait Message
{
    function alert() {
        echo $this->message;
    }
}

class Messenger
{
    use Message;
    private $message = "This is a message";
}

$messenger = new Messenger;
$messenger->alert(); //This is a message
As traits are completely flattened into the class composed of them, any property or method of the trait will become a part of that class and we access them just like any other class properties or methods. We can even have abstract methods in a trait to enforce the composing class to implement these methods. For example:
<?php
trait Message
{
    private $message;

    function alert() {
        $this->define();
        echo $this->message;
    }
    abstract function define();
}

class Messenger
{
    use Message;
    function define() {
        $this->message = "Custom Message";
    }
}

$messenger = new Messenger;
$messenger->alert(); //Custom Message
Here we have a trait Message with an abstract method define(). It requires all classes which use this trait to implement the method. Otherwise, PHP will give an error saying there is an abstract method which has not been implemented. Unlike traits in Scala, traits in PHP can have a constructor but it must be declared public (an error will be thrown if is private or protected). Anyway, be cautious when using constructors in traits, though, because it may lead to unintended collisions in the composing classes.

Summary

Traits are one of the most powerful features introduced in PHP 5.4, and I’ve discussed almost all their features in this article. They let programmers reuse code fragments horizontally across multiple classes which do not have to be within the same inheritance hierarchy. Instead of having complex semantics, they provide us with a light weight mechanism for code reuse. Though there are some drawbacks with traits, they certainly can help improve the design of your application removing code duplication and making it more DRY. Image via Vlue / Shutterstock

Frequently Asked Questions (FAQs) about Using Traits in PHP 5.4

What are the benefits of using traits in PHP 5.4?

Traits in PHP 5.4 provide a powerful tool for code reusability. They allow developers to reuse methods in multiple classes. Traits can be included within different classes to access the same set of methods, reducing code duplication. They also help in resolving the single inheritance problem of PHP by allowing a class to inherit methods from multiple traits.

How do I use a trait in a class?

To use a trait in a class, you need to use the ‘use’ keyword inside the class. Here’s an example:
trait MyTrait {
public function myMethod() {
// method code
}
}

class MyClass {
use MyTrait;
}
In this example, MyClass can now use the myMethod() function defined in MyTrait.

Can a trait override a method in a class?

Yes, a trait can override a method in a class. If a trait and a class have methods with the same name, the method in the trait will override the one in the class. However, if the class uses multiple traits with methods having the same name, a fatal error will occur due to ambiguity.

How can I resolve conflicts between traits?

PHP provides two ways to resolve conflicts between traits: the ‘insteadof’ operator and method aliasing. The ‘insteadof’ operator is used to specify which method to use when two traits have methods with the same name. Method aliasing allows you to give a method in a trait a different name in the class that uses it.

Can a trait use another trait?

Yes, a trait can use another trait. This is done using the ‘use’ keyword inside the trait. This allows you to reuse methods from one trait in another trait.

Can traits have properties?

Yes, traits can have properties. These properties can be accessed and manipulated in the same way as class properties.

Can traits have constructors?

Traits cannot have constructors. If you need to initialize some properties in a trait, you can define an init() method or similar and call it from the class that uses the trait.

Can traits be abstract?

No, traits cannot be abstract. However, they can have abstract methods. These methods must be implemented in the class that uses the trait.

Can traits implement interfaces?

No, traits cannot implement interfaces. However, a class that uses a trait can implement an interface.

Can traits be static?

No, traits cannot be static. However, they can have static methods and properties. These can be accessed in the same way as static methods and properties in a class.

Shameer CShameer C
View Author

Shameer is a passionate programmer and open-source enthusiast from Kerala, India. He has experience in web development using Scala, PHP, Ruby, MySQL, and JavaScript. While not working, Shameer spends his time coding personal projects, learning, watching screen casts, blogging, etc. His specific areas of interest include cloud computing, and system and database administration.

Advanced
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week