PHP 5.4 is going to introduce some new tricks, particularly multiple inheritance, using an ingenious traits system. Installing an alpha version of PHP is the only way to get a peek (at the time of this writing), and there are a few examples provided on the documentation page: http://us3.php.net/traits.
It’s always exciting to see PHP dip a few more of its toes into the OOP waters, so let’s see what we can do with this new feature!
Setting Up a Sandbox
I started by compiling PHP 5.4.0alpha3 onto a 32-bit Ubuntu Virtual Machine using VirtualBox. I prefer not to undo my primary PHP installation on my day-to-day work station, so a VM provides a convenient way for me to create a USB-stickable, portable sandbox for testing out new stuff. If you’ve never compiled PHP from source before, you may wish to read up on it.
Let’s Write Some Code!
Traits grant us a lot more flexibility for overloading parent methods. Consider the following example.
<?php
class Animal {
public function speak() {
echo 'Animal sounds...';
}
}
trait Dog {
public function speak() {
echo 'woof!';
}
}
class GoldenRetriever extends Animal {
use Dog;
}
$fido = new GoldenRetriever;
$fido->speak();
Of course, when Fido speaks, he’ll be saying ‘woof!’ as instructed by the use keyword in the GoldenRetriever class definition. We can use traits like this to give specific functionality to a set of classes that extend a very generic parent.
For instance, we could have several more animals:
trait Pig {
public function speak() {
echo 'oink!';
}
}
trait Cow {
public function speak() {
echo 'moo!';
}
}
trait Sheep {
public function speak() {
echo 'baaaaa';
}
}
class Piggy extends Animal {
use Pig;
}
class MilkCow extends Animal {
use Cow;
}
class BigHornSheep extends Animal {
use Sheep;
}
$piggy = new Piggy;
$piggy->speak();
$cow = new MilkCow;
$cow->speak();
$sheep = new BigHornSheep;
$sheep->speak();
Pretty cool stuff, right?
One Class, Many Traits
The best part about the traits nomenclature, is that it really makes sense from an empirical standpoint. Things have attributes. We don’t think of real-world objects as deriving from some greater architecture, we see them for what they are: things with characteristics and…traits.
The following set of classes demonstrates our ability to define characteristics for our objects.
<?php
trait Hammerer {
public function driveNails() {
}
}
trait Driller {
public function boreHoles() {
}
}
trait Carpenter {
public function cutBoards() {
}
}
class GeneralContractor {
use Hammerer, Driller, Carpenter;
}
$bob = new GeneralContractor;
$bob->driveNails();
$bob->boreHoles();
$bob->cutBoards();
Grouping Trait Attributes
Constructors can be utilized to implement specific member settings for various traits. When an object uses a given trait, any instances will gain the values established by it. In this way, we can create common scenarios with less setup in the extending classes.
<?php
class Employee {
public $name;
public $payRate;
public $vacationDays;
public $accountability;
}
trait Underling {
public function __construct() {
$this->payRate = 'low';
$this->vacationDays = 'few';
$this->accountability = 'under the bus';
}
public function takeCarpool() {
}
}
trait Supervisor {
public function __construct() {
$this->payRate = 'high';
$this->vacationDays = 'half the year';
$this->accountability = 'drives the bus';
}
public function ridePrivateJet() {
}
}
class Accountant extends Employee {
use Underling;
}
class VicePresident extends Employee {
use Supervisor;
}
$underling = new Accountant;
$underling->name = 'Bob';
$supervisor = new VicePresident;
$supervisor->name = 'Jim';
Realizing Interfaces
The biggest problem with PHP’s previous multiple inheritance scheme, the object interface, was that it merely established a contract which the extending classes were ultimately required to uphold. This allowed for stronger type hinting, but lacked in practicality. Code couldn’t be reused as easily, and the name became a bit of a misnomer when considering what an interface does in the real world (it controls something, like a remote control to a TV, not a contract for the TV to do remote control things).
With traits, we can now enjoy interfaces for what they are good at: establishing rock-solid contracts and conventions. The trait can pick up where the interface drops off by realizing the interface for an extending class in an encapsulated manner. Consider, for example, the following wizard classes:
<?php
// Base.
class Character{
public $attributes = array();
}
// Contracts.
interface Mortal {
public function dies();
}
interface Intelligent {
public function pontificate();
}
interface Magical {
public function learnSpell();
public function castSpell();
}
// Traits.
trait Scholar {
public function learnSpell() {
}
}
trait Sorcerer {
public function castSpell() {
}
}
trait Human {
public function pontificate() {
}
public function dies() {
}
}
// Implemenation.
class Wizard extends Character
implements
Mortal,
Intelligent,
Magical {
use Human, Scholar, Sorcerer;
}
$gandalf = new Wizard;
Notice that the final class does not have to directly implement the abstract methods defined in the interfaces. Since the traits handle this for us, we can keep the object clean and concise. Also, we could easily have a Witch or Warlock class with very similar architecture, and little additional effort.
The Future Rocks!
Traits are going to play a big role moving forward. I’m tickled pink to have multiple inheritance, and I have to say, quite frankly, that this approach just about nails it on the head. We can look forward to less code, stronger conventions, and better design patterns with such a useful component in the toolbox.
Stay tuned for more fun with traits!