My Love / Hate Relationship with PHP Traits

Love and Hate PHP TraitsAnyone who knows me knows that I am a bit of a stickler when it comes to traditional software design principles. I do believe they lead to outstanding software products when they are followed correctly. This isn’t to say I am not flexible when it comes to new ideas to improve traditional rules. When I saw the introduction of PHP traits in 5.4.0 I was eager to learn all about them and how they worked. Any new trick to simplify my work is always a plus. You may call it “syntax sugar” but as long as it doesn’t break some founding design principle in a willy nilly fashion, I am all over it.

PHP traits, in my opinion, are handy and very flexible. I guess that is the “love” part of my relationship with them. When I am in a rush, it is tricks like this that make things easier for me to get to the point of rolling out the next version of a blockbuster app. However, I feel that traits also meddle with a bit of the inheritance rules that have been proven time and time again. Is it possible to love as well as hate something at the same time?

The Love

Before we go into talking about what I don’t like about traits, let’s take a minute to cover what traits are and some of the things we might like about them. As stated on the PHP website: “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.”

In layman’s terms this means that we can define a group of functions on their own and then have several different classes borrow them for their own purposes. These classes might be completely unrelated to one another but all have a need for the same functionality provided by the methods. Perhaps we have several functions related to an engine… like reading its temperature. We have two classes, a plane and a car. Both of these objects act very different but need to “borrow” the temperature reading functions to help monitor their engine’s temperature.

Below we have a simple example of two different classes using a trait called “EngineTools”. This trait has a temperature conversion function. This gives the different classes a way to convert their engine temperatures from a scale of Fahrenheit to Celsius. If we want to add more functionality we can simply add more to the trait definition later thus expanding it. This will give our classes extra abilities without having to mess with the inheritance chain those classes may have. We don’t have to define another class, we don’t have to deal with abstracts or implementing interfaces or inheriting anything. Here is our basic demonstration example…

// Set of traits that we can share with classes.
trait EngineTools {
	// Convert engine temperature to Celsius
	function currentTempC() {
		return ($this->engineTemp - 32) * (5/9);
	}
}

// Our Plane uses the tools for temperature conversion
class Plane {
	use EngineTools;
	
	private $engineTemp;
	
	function __construct() {
		$this->startPlane();
	}
	
	private function startPlane() {
		$this->engineTemp = 210;
	}
	
	private function fly() {
		// Fly
	}
}

// Car uses same engine tools for temperature conversion
class Car {
	use EngineTools;
	
	private $engineTemp;
	
	function __construct() {
		$this->startCar();
	}
	
	private function startCar() {
		$this->engineTemp = 230;
	}
	
	private function drive() {
		// Drive
	}
}

$plane = new Plane();
$car = new Car();

echo $plane->currentTempC() . "<br/>";
echo $car->currentTempC() . "<br/>";

What I love about this concept is that we can write several generic style functions, a library if you will, and have classes use them when they need them. Think of it as “bolt on” technology. Have a plane or a car? Drop in the EngineTools trait and suddenly these classes can do more than before… and we didn’t even really alter the class definition (beyond adding the “use” line). I can see traits as a great way for measurement, calibration, or logging functions to be added to existing classes.

The Hate

Besides traits being a nice mechanism for bolting on functionality to existing structures, they are also going to be a nightmare in the wrong hands. What traits essentially do is offload functionality to yet another, what I will call, entity. These dangers could be some of the following:

  • An entire class’ abilities are NOT in one location visually.
  • You alter the trait thinking it will alter one type of class using it, but may introduce problems in another class also using it. Meaning you will have to know all classes using the trait.
  • Introduces the idea of multiple inheritance ideas which bring with it the need to worry about conflicts in naming. (Yes you can solve it using insteadof, but now you have to worry about that and take precautions like specific naming related to the trait or to use this keyword). This is also related to the deadly diamond of death problem.

The Consensus

I haven’t seen too many people argue one way or the other for/against this feature. In addition, I have not yet seen large scale adoption of it in day to day work. So I guess that means the jury is still out. But my feelings on this feature are mixed and I would recommend using it only in situations where you know the advantages greatly exceed the disadvantages. That is, time and ease of use outweigh unintended naming conflicts or any unforeseen inheritance style side effects.

I do think we should continue to strive for a clear single inheritance design in PHP on new code and use traits as a tool to help grow out functionality for existing complex systems. I would hate to see a feature like this blur the line of inheritance and lead to entities that alter other entities and then scattered throughout a system. Then find that these altering entities are hidden away in some other file which is being included. Hopefully they don’t become a detriment to the future of PHP. What are your thoughts? Do you use them or not? Let us know in the comments below.

Thanks for reading!

About The Author

Martyr2 is the founder of the Coders Lexicon and author of the new ebook "The Programmers Idea Book". He has been a programmer for over 16 years. He works for a hot application development company in Vancouver Canada which service some of the biggest telecoms in the world. He has won numerous awards for his mentoring in software development and contributes regularly to several communities around the web. He is an expert in numerous languages including .NET, PHP, C/C++, Java and more.
  • http://carolyn-ann.blogspot.com Carolyn Ann

    They’re like Ruby’s mixins, which have a very similar role. It does lead to some difficult code reading as you hunt for whichever file has that method. I’ve developed the opinion that if you need Google to find where the method is, something has gone wrong.

    As far as I can tell, I’m the only one who thinks that, however! :-)

    • http://www.coderslexicon.com Martyr2

      I am sure you are not the only one who thinks that. I have seen it happen in many projects where you have to go hunting down some mixin or trait that is in a completely different file. We can only hope that developers do the right thing and keep the parts of classes in the relative general area where they are used. We just have to be cautious on that front. :)

  • http://blog.ircmaxell.com Anthony

    Basically, I agree with you. I wrote a post a while ago equating traits to a generic anti-pattern that can have clean uses, but where most uses are abuses: http://blog.ircmaxell.com/2011/07/are-traits-new-eval.html

    As far as “Ruby Mixins”, the big difference is that traits are a purely compile-time construct. You can’t re-bind them at runtime (or instantiation time). If you could (like you can with scala: $obj = new Class with Trait, Trait2), then all bets would be off and it would be incredible. But as it stands, I feel it’s just automated copy-paste which should be treated the same as manual copy-paste (frowned upon)…

    • http://www.coderslexicon.com Martyr2

      Good points. I have also heard other bloggers talking about the feeling of “copy-paste” when using traits. :)

  • Gerard

    1:1 -> trait:file? What will the average number of included files be for the typical php application built around a framework?

    • http://www.coderslexicon.com Martyr2

      Hard to say really but I have seen some different applications use as many as 20-30 different include files, some even more. When you factor in everything from config files, modules, constants, libraries, templates and more you can grow quite quickly. Hopefully I understood your question correctly.

  • Angelo

    Traits are really good, cannot see anything wrong with multiple inheritance or have a library for easy to reuse.

    Traits are for small easy objects with a specific use, meaning DO NOT write 1.000.000 lines function and think it will be reusable (in most of the cases it won’t).

    If ppl cannot take advantage of traits to work more efficient then they are better off staying at home watching TV all day and claim benefits.

  • http://systemsarchitect.net/ Lukasz Kujawa

    First of congratulations for a good article and starting important discussion. I wouldn’t call Traits a syntax sugar. They are very useful when it’s difficult (or impossible) to pass functionality by inheritance. Saying that this is the only case when I would go for a Trait. If it’s possible to share logic by inheritance that would be my first weapon of choice. To be in a save position I’m trying to use Traits across one namespace but from time to time I break this rule.

    • http://www.coderslexicon.com Martyr2

      Hey thanks for the reply. It sounds like you are taking a reasonable approach. There will certainly be instances where the hierarchy structure won’t accomodate additions/changes and traits are a perfect solution for this.

      I tried to get the point across when I mentioned the idea of “bolt on” technology where if things are solidified to the point of no chance of really changing the design to handle the specs, traits give you the perfect chance to add on to it.

      As long as you apply them equally and make them easy to find, traits can be awesome. It is part of what I love about them. :)

  • http://foaa.de/ Ulrich Kautz

    I use quite often languages with true or limited multiple inheritance (Perl, Go, Python, ..) and embrace traits in PHP. The major argument against them, I would subscribe to, is the difficulty of testing (which applies to a degree to abstract classes as well).

    To your hate points: Given regular inheritance as “A extends B”, without traits, the first two points apply as well:

    * Not all class capabilities are visible in one location (A can what B provides)
    * Altering in B effects A (and any other class inheriting from one of those)

    The collisions (methods, properties) could have been better resolved (closer to multiple inheritance).. but at least they trow a fatal error, so will probably be quite obvious during testing.

    Altogether, I would love to see true multi inheritance in PHP, but settle for traits for now. Sure, this can increase complexity – but if we’d throw any tool out of the box which does that, we’d probably still be writing assembler.

    • http://www.coderslexicon.com Martyr2

      I can’t say I am a huge fan of the idea of multiple inheritance for PHP, just because I know much about the dangers that entails, but you make some good points. Perhaps they will do a better job with handling conflicts. If we all adapt the idea of qualifying the names of our traits we could avoid that problem too.

      So for instance if we create a trait where we prefix its methods/properties with a unique name, then the chances of it conflicting in the system might be reduced. Much like you would do with plugin development.

      I still think there needs to be work on this feature and I am glad you brought up some of those points. Shows there is work to be done still. :)