About a week ago PHP 5.4 was released as stable. It introduces lots of bugfixes and a few new features, the most important of which is traits. Traits are PHP’s answer to multiple inheritance . Traits are not instantiated like normal classes, but instead work much like mixins, defining common functionality which can be “inserted” into concrete classes.

As an example, let’s assume you have two classes: Person and Business. Both of these entities have an Address associated with them. You don’t want to implement the getAddress() and setAddress() in both of these classes, since this would lead to unnecessary duplicate code. The solution to this problem is object-oriented programming 101: have both classes extend Customer, assuming of course your application has the concept of a customer. This is not always a satisfactory solution though. Suppose there is a third class called Building which also needs an address. In our example a building is not a customer, and therefore cannot extend it.

In PHP 5.4 we can solve this problem using a trait called HasAddress:

  1. class Address {
  2. private $street;
  3. private $town;
  4. private $postcode;
  5. /* etc */
  6. }
  7.  
  8. trait HasAddress {
  9. private $address;
  10. public function getAddress() {
  11. return $this->address;
  12. }
  13. public function setAddress(Address $address) {
  14. $this->address = $address;
  15. }
  16. }
  17.  
  18. class Building {
  19. use HasAddress;
  20. }
  21.  
  22. class Person {
  23. use HasAddress;
  24. }
  25.  
  26. class Business {
  27. use HasAddress;
  28. }
plain

Now all three of our concrete classes have the address getter and setter methods, along with a private member for storing it. One comment on php.net explains the usage of traits quite well, saying it is “language assisted copy and paste”. While I don’t intend to undermine the usefulness of traits, it does leave me wondering whether they are as useful as they appear to be. The reason I mention this is because I haven’t been able to find a way of testing whether a class uses a specific trait. For instance in the code below, all the calls return false.

  1. $business = new Business();
  2. var_dump($business instanceof HasAddress);
  3. var_dump(is_a($business, "HasAddress"));
  4. var_dump(is_subclass_of($business, "HasAddress"));
plain

There is of course a good reason for this, and that is because it’s possible to rename the methods inherited from a trait, as illustrated below:

  1. class Order {
  2. use HasAddress {
  3. HasAddress::getAddress as getShippingAddress;
  4. HasAddress::setAddress as setShippingAddress;
  5. }
  6. }
plain

Now the order specifically has a shipping address. However, something interesting happens when looking at what methods are in fact defined on this class:
  1. $order = new Order();
plain

which will output:
  1. Array
  2. (
  3. [0] => getShippingAddress
  4. [1] => getAddress
  5. [2] => setShippingAddress
  6. [3] => setAddress
  7. )
plain

As we can see both the original and aliased methods are present on the class, and they do exactly what you’d expect, as the code below returns true:
  1. $order = new Order();
  2. $address = new Address();
  3. $order->setAddress($address);
  4. var_dump($order->getAddress() === $order->getShippingAddress());
plain

I’m not entirely sure whether this is intended behaviour or not, but I suspect it might not be. The keen observer may see where I was going with the shipping address, but unfortunately the code below doesn’t work, since member variables become private to the class and not the trait, and cannot be renamed:
  1. class Order {
  2. use HasAddress {
  3. HasAddress::getAddress as getShippingAddress;
  4. HasAddress::setAddress as setShippingAddress;
  5. }
  6. use HasAddress {
  7. HasAddress::getAddress as getBillingAddress;
  8. HasAddress::setAddress as setBillingAddress;
  9. }
  10. }
plain

In conclusion, I see traits as a welcome edition to PHP along with 5.3′s namespaces, which I was personally very happy with. Originally coming from a strict Java background, I’m also glad to see the PHP developers did not carelessly throw in a whole heap of multiple inheritance functionality, but instead seem to be taking things slow. I suspect we will see more of this in the future, probably refined a bit down the line.