Using traits in PHP

Using traits in PHP

1. Introduction

Traits have been available in PHP since version 5.3. But yet when I ask about traits at the job interview, so many people say: “Yes, I have heard/read about it but never used it in real projects.”
Traits are a powerful tool when you know how to use it. The mission of this article is to introduce them by defining a problem they can solve and to actually solve it.

 

2. The Problem

Let’s assume we are designing a 2D game called “Toy War”. Our current task is to implement all fighting units, which are for now: Tanks, Jet Fighters, Spy Airplanes and Air Defense Towers. Of course we want to create a class for each unit. To have a good architecture we also need to define some abstractions that will help us to structurize our code.

Let’s see what features all the units have. Some of them:
• have their position on the map
• can move
• can turn left and right
• can fly
• have a gun (can fire).

Each feature provides some functionality which turns out basically as methods in our code. For example, ‘having positions’ means we can call getPosition() method and get a 2nd vector, and ‘having gun’ stands for getGunDirection(), turnGun($degrees) and fire().

Let’s build a simple ‘Feature / Unit’ map to see what features each of the units do have:

a map

If you look for common features in all units you’ll see that the only one is ‘Having position’. It means that the only abstraction we can define is a Unit which just has a getPosition() method.

Going further we could define a MovableUnit which will provide ‘moving forward’ and ‘tuning’ features as they are applicable to the same units.

 

At this point we have a pretty straightforward inheritance structure:

an inheritance structure

But we still have two features to implement: ‘flying’ and ‘having gun’. If we forget the movingand arming functionality for a moment and draw a diagram for a flying feature we get:

a diagram for a flying feature

Leaving flying and moving behind the scene and working with arming we get:

a structure

But if we try to merge all these diagrams and build the inheritance structure for all features then we get:

an inheritance structure for all features

This diagram says we need to inherit multiple classes for most of our units. And that is impossible in PHP.

 

3. The Solution

Here is what the Traits come for. The mission of a Trait is to provide some atomic functionality for an object. The Trait defines a single feature (i.e. movable or flyable) and could be applied to any class. The key feature of Traits is that you can apply any number of them to your class. It lets you compose a class from different features it has, and as a result — to build more complicated but elegant architecture, which will be much more adequate to the real world.

With the use of Traits our diagram turns to:

the diagram

The SpyPlane, JetFighter, Tank and Tower are inherited from Unit and are using only the features they have. Which is great because it meets all the requirements and is fully implementable in PHP. Let’s see the code.

4. The Code


<?php
/**
* Abstract Unit class.
* Implement the 'has position' feature.
*/
abstract class Unit
{
 protected $position;
 public function getPosition()
 {
 return $this->position;
 }
}
/**
* Implement the 'moving' feature.
*/
trait Movable
{
 public function move() { /* ... */ }
 public function turn($degrees) { /* ... */ }
}
/**
* Implement the 'having gun' feature
*/
trait Armed
{
 protected $gunDirection;
 public function getGunDirection() {
 return $this->gunDirection;
 }
 public function turnGun($degrees) { /* ... */ }
 public function fire() { /* ... */ }
}
/**
* Implement the 'flying' feature
*/
trait Flyable
{
 public function blastOff() { /* ... */ }
 public function fly() { /* ... */ }
 public function land() { /* ... */ }
}
/**
* Implement the Tank which has a position, can move and definitely has a gun
*/
class Tank extends Unit
{
 use Movable, Armed;
}
/**
* Implement the Jet Fighter which has a position, can move, fly and has a gun
*/
class JetFighter extends Unit
{
 use Movable, Flyable, Armed;
}
/**
* Implement the Spy Plane which has a position, can move and fly but is unarmed.
*/
class SpyPlane extends Unit
{
 use Movable, Flyable;
}
/**
* Implement the Air Defense Tower which has a position and a gun but can't move 
or fly.
*/
class Tower extends Unit
{
 use Armed;
}

 


 

As you can see using traits is as easy as extending the abstract class. And like the methods of the ancestors the traits method could be overwritten by the descendant. But the syntax differs. For example:

 


/**
* Implement the Tank which has a position, can move and definitely has a gun
*/
class Tank extends Unit
{
 use Movable;
 use Armed {
 fire as traitFire;
 };
 public function fire()
 {
 /**
 * Do tank specific fire actions
 */
 $this->traitFire();
 }
}

 


Here we have used as operator to give an alias for fire() method so we can implement our specific fire() using the method from the Trait.

PHP also provides the conflict resolving mechanism, ability to change visibility of methods and other useful functionality which you can learn at http://php.net/traits. Note that the traits are available since PHP 5.4 which is very widespread already. So I bet you can start using it right now.

 

5. Wrap up

Traits let us think of architectural model as of the definition of features represented as Traits and Classes which are composed from Traits in so many ways as do objects in real life. But the well-known hierarchical model is not being completely substituted with the compositional one these days. They complement each other giving us the ability to design graceful architectures for apps we build.

 

About Author

Max Gopey is a PHP Team Leader in Ciklum who currently works on Magento projects.
He has a 6-year experience in developing PHP applications for the web including eCommerce and document management systems.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>