Exploring PHP Traits

Exploring PHP Traits

PHP Traits are very helpful in PHP Programming, Trait means a class can only inherit from one other class.

However, a lot of the time it would be beneficial to inherit from multiple classes.

For example, it might be desirable to inherit methods from a couple of different classes in order to prevent code duplication.

This problem can lead to a class that has a long family history of inheritance which often does not make sense.

A Trait is a kind of like a Mixin in that it allows you to mix Trait classes into an existing class. This means you can reduce code duplication and get the benefits whilst avoiding the problems of multiple inheritances.

In this article, I’m going to be exploring Traits to show how you can use them in your projects.

What is PHP Traits?

A Trait is simply a group of methods that you want to include within another class. A Trait, like an abstract class, cannot be instantiated on its own.

An example of a Trait could be:

trait Sharable {

  public function share($item)
  {
    return 'share this item';
  }

}

You could then include this Trait within other classes like this:

class Post {

  use Sharable;

}

class Comment {

  use Sharable;

}

Now if you were to create new objects out of these classes you would find that they both have the share() method available:

$post = new Post;
echo $post->share(''); // 'share this item'

$comment = new Comment;
echo $comment->share(''); // 'share this item'

How do PHP Traits work?

As you can see from the example above, both the Post and the Comment objects have the share() method available despite not having that method defined.

A Trait is basically just a way to copy and pastes code during runtime.

This means the Trait is copied into the Post and Comment classes so when you instantiate a new instance, the share() method code will be available.

How are PHP Traits different from Abstract classes?

A Trait is different from an Abstract class because they do not rely on inheritance.

Imagine if the Post and the Comment class had to inherit from a AbstractSocial class.

We are most likely going to want to do more than just share posts and comments on social media websites, so we’ll probably end up with a complicated inheritance tree like this:

class AbstractValidate extends AbstractCache {}
class AbstractSocial extends AbstractValidate {}
class Post extends AbstractSocial {}

This complicated inheritance is pretty nasty, but it also adds complication when a simpler object does not have a similar inherence structure.

For example, if we had a Message object that shouldn’t allow social sharing, then this object would require a slightly different inheritance structure.

How is PHP Traits different to Interfaces?

Traits kind of look a lot like Interfaces. Both Traits and interfaces are usually simple, concise and not much use without an actually implemented class. However, the difference between the two is important.

An interface is a contract that says “this object is able to do this thing”, whereas a Trait is giving the object the ability to do the thing.

// Interface
interface Sociable {

  public function like();
  public function share();

}

// Trait
trait Sharable {

  public function share($item)
  {
    // share this item
  }

}

// Class
class Post implements Sociable {

  use Sharable;

  public function like()
  {
    //
  }

}

In this example, we have a Sociable interface that states that the Post object is able to like() and share().

The Sharable Trait implements the share() method and the like() method is implemented in the Post class.

So as you can see we could type hint the Post object to see if it is sociable (it implements the Sociable interface), whilst the Trait defines a reusable method that we can then mix into other similar classes:

$post = new Post;

if($post instanceOf Sociable)
{
  $post->share('hello world');
}

What are the benefits of PHP Traits?

The benefit of using Traits is that you reduce code duplication whilst preventing complicated class inheritance that might not make sense within the context of your application.

This allows you to define simple Traits that are clear and concise and then mix in that functionality where appropriate.

What are the drawbacks of PHP Traits?

However, with that being said, there are possible drawbacks when using Traits too.

Traits make it very easy to write bloated classes that have too much responsibility.

A Trait is essentially a way to copy and paste code between classes. By having a way to very simply add another group of methods to a class, it’s very easy to diverge from the single responsibility principle.

Other drawbacks to using Traits are not being able to see all the methods of a class when looking at the source code as well as method conflicts or duplication of logic.

I think PHP Traits, when used correctly, is a fantastic tool to have at our disposal. However, Traits can also be a crutch for lazy programming.

It’s very easy to just add a Trait to solve your immediate problem. Often composition is the better approach over inheritance or using a Trait.

What are the typical situations for using PHP Traits?

So what would be a typical situation when using a Trait would be a good idea?

Well, I think Traits are an excellent way to reuse a chunk of code between a set of similar classes that should not inherit from the same abstract class.

Using the social application from earlier, imagine we had objects for Post, Photo, Note, Message and Link. For the most part, these objects are fairly interchangeable within our system as they are typically created and interacted with between users.

However, Post, Photo, Note and Link are all objects that are publicly shareable between users, whereas Message objects are private messages that are not made public.

The Post, Photo, Note and Link objects all implement a Shareable interface:

interface Shareable {

  public function share();

}

Does it make sense to duplicate the share() method in every class that implements the Shareable interface?

No.

Does it make sense to have an AbstractShare class that objects who implement the Shareable interface extend?

No.

Does it make sense to have the share() method implemented as part of an AbstractEntity class, but then blocked out for the Message object?

No.

Does it make sense to implement a ShareableTrait that fulfills the interface contract and can, therefore, be easily added to only objects that require it?

Yes!

Conclusion

So the big question is, should you use PHP Traits? I think you should definitely consider using Traits within your projects.

Traits offer a really nice solution to the nasty mess of inheritance that can arise in single inheritance languages such as PHP.

Traits allow you to add functionality to your classes horizontally without over-complicating or duplicating code.

However, it is very easy to use Traits as a crutch. Traits are not the answer to all of your problems.

Using a Trait in the wrong situation is most definitely a bad decision. If you are trying to crowbar the functionality you desire into a class using Traits, you are probably doing something wrong.

As with almost everything in computer programming, there is most definitely right and wrong situations to use certain components or patterns.

PHP Traits might solve your immediate problem, but using composition might be the real answer to your predicament.

Don’t look for situations to use Traits, instead try and choose the right tool from your tool belt when facing a problem. Understanding when and where to use Traits adds another weapon to your arsenal.

Your little help will keep this site alive and help us to produce quality content for you.