PHP Trait

Introduction

In this blog post, we are going to look at traits in PHP. We will start with the definition, look at a simple example and then move on to more interesting issues such as the relationship of traits to interfaces, and abstract classes, multiple inheritance and correct usage of traits.

In a nutshell, “Traits are 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”. Source: http://php.net/manual/en/language.oop5.traits.php

Getting started with traits in PHP

This section is intended to give you an idea of what traits are, and how to use them. If you are already familiar with such then you can skip this section.

The code below gives you a practical example of traits in php

<?php
trait Computations 
{
    public function add($x, $y)
    {
        return $x + $y;
    }

    public function multiply($x, $y)
    {
        return $x * $y;
    }

    public function divide($x, $y)
    {
        return $x / $y;
    }
}

class BasicCalculator
{
    use Computations;
}

class ScientificCalculator
{
    use Computations;
}

$basic_calcultor = new BasicCalculator();
$scientific_calculator = new ScientificCalculator();

echo $basic_calcultor->add(2,2) . '<br>';
echo $scientific_calculator->add(2,3);

HERE,

  • trait Computations {…} the keyword trait is used to declare a trait. Computations is the name of the trait. We have defined and implemented methods that perform arithmetic computations
  • use Computations; makes available all of the methods defined in the trait Computations.

We can now proceed and use our classes Basic and Scientific Calculators as shown below

$basic_calcultor = new BasicCalculator();
$scientific_calculator = new ScientificCalculator();

echo $basic_calcultor->add(2,2) . '<br>';
echo $scientific_calculator->add(2,3);

HERE,

  • The above code will output 4 and 5 respectively.

For more practical examples of traits, refer to the official PHP documentation http://php.net/manual/en/language.oop5.traits.php

We can achieve the above results using inheritance. We define an abstract / concrete class implementing the arithmetic methods and simply have Basic and Scientific Calculator classes extend the parent class.

What really is the big deal with traits?

For us to fully understand and appreciate traits, we will need to look at other similar concepts such as interfaces, abstract and concrete classes, and multiple inheritance.

Interfaces

Interfaces allow you to specify methods and parameters that a given class must implement. Interfaces do not contain any concrete implementations. Just a blue print for the methods that the implementing class must implement religiously. An important fact to note is interfaces do not contain any implementations whatsoever and a concrete class can implement more than one interface.

Abstract class

Abstract class define method signatures and may contain concrete implementations. However, they cannot be instantiated. They must be extended by the concrete class. The concrete class can only extend a single abstract class at any given time.

On interfaces and abstract classes…

As stated above, a class can implement more than one interface. That is great but interfaces do not implement any of the methods. That is left to the concrete class to handle. This can lead to code duplication in the concrete classes.

As stated above, abstract classes can provide more than just method signatures. They can provide implementation too but at any given time, we can only extend a single class.

What happens when we have a situation that requires us to enjoy the best of both worlds (interfaces and abstract class) and PHP only supports single inheritance? This is where traits come in handy.

Traits

Traits are like a combination of interfaces and abstract classes, “like” being the operative word. They are like interfaces in the sense that a concrete class can use more than one trait. They are like abstract classes in the sense that we can have concrete implementation and/or abstracts. The concept of traits sounds very similar to multiple inheritance.

Before we go into more details, let’s briefly look at multiple inheritance, how it’s similar to traits and how traits solve the diamond problem of multiple inheritance.

The diamond problem

PHP is a single inheritance language. A child simply inherits from the parent and can use as is or override methods from the parent. Multiple inheritance should support inheriting from more than one parent. If a child inherits from parents who have completely unique methods and properties, it works just fine. But what happens when a child inherits from parents who have methods with the same names and signature as shown in the diagram below

In such cases, the problem is telling which method that DatabaseConnection will use.

PHP Traits and the Diamond Problem (Conflict Resolution)

A class can implement more than one trait and the above problem can occur.

How do PHP traits handle that?

We will use traits to implement something similar to the above example. We will define MySQL as a trait and SQL Server as a trait too. We will the create a class that uses both traits. Remember, both MySQL and SQL Server traits contain the method connect.

Create a new file and add the following code

<?php

trait MySQL
{
    public function connect()
    {
        echo 'MySQL Connection Successfully Established';
    }
}

trait SQLServer
{
    public function connect()
    {
        echo 'SQL Server Connection Successfully Established';
    }
}

class DatabaseClient 
{
    use MySQL, SQLServer;}

$dc = new DatabaseClient();

$dc->connect();
echo '<br>';
$dc->ss_connect();

You will get the following error message

Fatal error: Trait method connect has not been applied, because there are collisions with other trait methods on DatabaseClient in C:\xampp\htdocs\traits\database.php on line 19

To fix the above error, replace the code

use MySQL, SQLServer;

with

use MySQL, SQLServer {
    MySQL::connect insteadof SQLServer;
    SQLServer::connect as ss_connect;
}

HERE,

  • MySQL::connect insteadof SQLServer; calls the connect method from MySQL instead of the one from SQLServer
  • SQLServer::connect as ssconnect; solves the name collision problem using an alias ssconnect. This means the class that uses both traits will have to call ss_connect for sql server and just connect for mysql

The above approach may or may not be perfect but it works and solves the problem.

When should you use traits?

Traits should be used to implement functionality that is shared across multiple classes. If the function is specific to only a single class, then it is better to implement the functionality directly into that class.

Traits and interfaces are not mutually exclusive. In fact, they kind of complement each other. The idea situation is to use the interface to define the methods and signatures that a class must implement and use traits to implement the functionality.

Following the above rules, let’s look at a practical example.

<?php

interface IDatabase
{
    public function connect();
}

trait MySQL 
{
    public function connect()
    {
        echo 'MySQL Connection Successfully Established <br>';
    }
}

trait SQLServer 
{
    public function connect()
    {
        echo 'SQL Server Connection Successfully Established <br>';
    }
}

trait ExportUtilities
{
    public function toJson()
    {
        echo 'Json results <br>';
    }

    public function toXML()
    {
        echo 'XML results <br>';
    }
}

class MySQLClient implements IDatabase
{
    use MySQL,ExportUtilities;
}

class SQLServerClient implements IDatabase
{
    use SQLServer,ExportUtilities;
}

$ms = new MySQLClient();
$ms->connect();
$ms->toJson();
$ms->toXML();

echo '<br>';

$ss = new SQLServerClient();
$ss->connect();
$ss->toJson();
$ss->toXML();

HERE,

  • interface IDatabase{…} defines an interface IDatabase that specifies the method signatures that the implementing class must implement.
  • trait MySQL{...}, trait SQLServer{...} provides implementation of the method(s) defined in the interface contract
  • class MySQLClient implements IDatabase {use MySQL,ExportUtilities;} defines a class MySQLClient that implements the IDatabase interface and uses MySQL and ExportUtilities traits
  • class SQLServerClient implements IDatabase {use MySQL,ExportUtilities;} defines a class SQLServerClient that implements the IDatabase interface and uses MySQL and ExportUtilities traits.

As you can see from our above example, the database results to Json and XML format is shared across multiple classes i.e. MySQL and SQL Server so it makes sense to use a trait in such a scenario (code re-usability).

MySQLClient class is specific to MySQL database only yet we used a trait and didn’t implement the connect functionality directly. The advantage of this approach is flexibility. You can have different traits that use different connection methodologies such as mysqli_connect and PDO.

Summary

Traits can be easy to inject methods into a class at runtime. This encourages code reusability and to some extent, emulating multiple inheritance without problems such as the diamond problem. They should be used when functionality is shared across multiple classes. Using a combination of interfaces and traits usually yields the best results.

Support Us

Kode Blog Tutorials is dedicated to bring you update to date, high quality free tutorials. You can support us by using the social media buttons to like and share the tutorial and subscribing to our newsletter. Please use the comments section below to give us feedback.

You can sign up for a free account on our site and you will be able to keep track of your progress as you go through the tutorial series.

Post Tags