PHP Traits Explained

What PHP Traits are and how to use them.

 
PHP Traits
 

In this tutorial you’ll learn about PHP Traits.

What they are, how to use them, and how they differ from interfaces, abstract classes and global functions. All with plenty of examples.

Let’s get started.

Contents


What are Traits in PHP?

A trait is a PHP construct that lets you reuse the same code in different classes.

You can define a trait using the following syntax:

trait SayGreeting {
   private $name = 'Alex';
   
   public function sayHello() {
      echo 'Hello, I\'m ' . $this->name;
   }
}

Traits can contain methods (like sayHello()) and properties (like $name).

Traits also support visibility operators (public, protected and private), static methods and properties, and class operators such as parent::, self:: and $this.

PHP classes can use traits and include the traits’ code.

To make a class use a trait, you need the use keyword.

For example:

class MyClass {
   use SayGreeting;
}

In the above example, the MyClass class uses the SayGreeting trait. By doing so, it’s like all the code inside the trait is copied into the class.

The class now has the sayHello() method as well as the $name property:

$myClass = new MyClass();
$myClass->sayHello();
/* Output:
 * Hello, I'm Alex
*/

How to use Traits.

In a nutshell, a trait is a structure that prevents code duplication in classes.

Different classes can use the same trait and include the trait’s methods and properties.

Traits have some similarities with interfaces and with abstract classes.

They are also similar to “include” scripts that contain global functions.

But traits have also important differences that make them unique.

Let’s see which ones in detail.

Traits vs Interfaces.

An interface is an OOP construct that lets you share the same set of functionalities among different classes.

You can find an in-depth explanation of PHP interfaces in this tutorial.

Interfaces are more specific than traits, because they can only contain abstract methods.

It’s up to the class that implements the interface to actually implement the methods.

With interfaces there is no code reuse at all because every class must implement the methods.

Traits, on the other hand, can provide fully implemented methods, on top of properties and class operators.

The classes that use the trait can reuse all the trait’s own code, with no code duplication whatsoever.

Traits vs Abstract Classes.

Both traits and abstract classes can include methods, either abstract or fully implemented.

However, if you want to use the methods from an abstract class into another class, you must extend the abstract class.

You can find an in-depth explanation of PHP abstract classes in this tutorial.

If you want to use the methods from an abstract class, you must establish an inheritance relation with your class. This is not always what you want.

Moreover, PHP does not support multiple inheritance so you cannot extend from more than one class.

This makes it impossible to define different sets of functions in different abstract classes and then extend from them to create different combinations.

Traits solve both problems:

  1. You can use the same trait in completely unrelated classes, without establishing an inheritance relation between them.
  2. You can use more traits in the same class.

Traits vs Include Scripts with Global Functions.

“Include” scripts contain PHP functions. If you need those functions in another PHP script, you can simply include that script.

Traits offer a similar solution. In fact, the main purpose of traits is to define some code once and then use it in different classes.

However, there are three main differences between traits and global functions:

  1. Global functions can be used anywhere, while traits can only be used inside classes.
  2. Traits can contain class elements and operators such as methods, properties, visibility modifiers (public, protected, private) and class operators (self::, parent::, $this).
  3. Traits can be combined: a trait can use other traits. This lets you define different traits combinations without the need of complex include statements.

What can Traits contain?

PHP traits can contain many of the elements of a PHP class.

Including:

  • Methods and properties with visibility operators (public, protected, private).
  • Static methods and properties.
  • Class operators, such as parent::, self:: and $this.

For example:

trait SayGreeting {
   /* A public property */
   public $info;
   
   /* A protected property */
   protected $language;
   
   /* A private property */
   private $name = 'my name';
   
   /* A static property */
   public static $count = 0;
   
   /* A public method */
   public function sayHello() {
       echo 'Hello, I\'m ' . $this->name;
   }
   
   /* A private method */
   private function sayGoodbye() {
	   echo 'Goodbye!';
   }
   
   /* A static method */
   public static function printCount() {
	   echo 'Class count: ' . self::$count;
   }
}
class MyClass {
   use SayGreeting;
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello, I'm Alex";
MyClass::$count = 5;
MyClass::printCount(); // Prints: "Class count: 5"
$myClass->sayGoodbye(): // Fatal error: private method.

Traits also support abstract methods.

Abstract methods must be implemented by the class that uses the trait:

trait SayGreeting {
   abstract public function sayHello();
}
class MyClass {
   use SayGreeting;
   
   /* Must implement the abstract sayHello() method */
   public function sayHello() {
      echo 'Hello!';
   }
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello!";

Traits can also use other traits.

In this case, all the code from the used traits is included into the trait, and then into the classes that use the trait.

For example:

trait SayHello {
   
   public function sayHello() {
      
      echo 'Hello!';
   }
}
trait SayGoodbye {
   
   public function sayGoodbye() {
      
      echo 'Goodbye!';
   }
}
/* The SayGreeting trait uses the SayHello and SayGoodbye traits */
trait SayGreeting {
   
   use SayHello, SayGoodbye;
}
/* By using SayGreeting, the methods from SayHello and SayGoodbye are included */
class MyClass {
   
   use SayGreeting;
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello!";
$myClass->sayGoodbye(); // Prints: "Goodbye!";

Methods precedence.

When you use a trait in your class, the class gets the methods from the trait.

You can override a trait method by defining it again in the class. In this case, the class’ own method has precedence over the trait’s method.

For example:

trait SayHello {
   
   public function sayHello() {
      
      echo 'Hello!';
   }
}
class MyClass {
   
   use SayHello;
   
   /* Override the trait's method */
    public function sayHello() {
      
      echo 'Hello from the class!';
   }
}

$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello from the class!";

If your class extends a base class, the methods from the traits have precedence over the methods of the base class with the same name.

You can, however, access the base class’ methods by using the parent:: operator.

For example:

/* Trait */
trait SayHello {
   
   public function sayHello() {
      
      echo 'Hello!';
   }
}
/* Base class */
class BaseClass {
   
   public function sayHello() {
      
      echo 'Hello from the base class!';
   }
}
class MyClass extends BaseClass {
   
   use SayHello;
   
   public function baseClassHello() {
      
      /* Call the parent's method */
	  parent::sayHello();
   }
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello!";
$myClass->baseClassHello(); // Prints: "Hello from the base class!";

Methods aliasing and conflicts.

PHP traits support aliasing.

When you use a trait in a class, you can alias a trait’s method to change its name and visibility.

This is done by using the as keyword.

For example:

trait SayHello {
   
   private function sayHello() {
      
      echo 'Hello!';
   }
}
class MyClass {
   
   /* Change sayHello() to hello() and make it public */
   use SayHello {sayHello as public hello; }
}
$myClass = new MyClass();
$myClass->hello(); // Prints: "Hello!"; 

Conflicts.

If you use more traits in the same class, and a method with the same name exists in more than one trait, then you have a conflict.

PHP doesn’t know which method to use, and you must explicitly solve the conflict.

To do that, you must choose which method to keep by using the insteadof keyword.

For example:

trait SayHello {
   
   public function say() {
      
      echo 'Hello!';
   }
}
trait SayGoodbye {
   
   public function say() {
      
      echo 'Goodbye!';
   }
}
class MyClass {
   
   /* Both traits have the same say() method */
   use SayHello, SayGoodbye {
      
      /* Use the say() method from SayHello */
      SayHello::say insteadof SayGoodbye;
   }
}
$myClass = new MyClass();
$myClass->say(); // Prints: "Hello!"; 

You can still access the hidden method by using aliasing.

Like this:

class MyClass {
   
   use SayHello, SayGoodbye {
      
      SayHello::say insteadof SayGoodbye;
	  SayGoodbye::say as goodbye;
   }
}
$myClass = new MyClass();
$myClass->say(); // Prints: "Hello!"; 
$myClass->goodbye(); // Prints: "Goodbye!"; 

Traits good practices.

Here are 2 pieces of advice to use traits correctly.

1: Do not use traits instead of interfaces.

Once you get familiar with traits, they become very easy to use. And you may be tempted to abuse them.

Other constructs, such as interfaces and abstract classes, are a better choice in some cases.

For instance, interfaces are better when you want to share a set of functionalities across similar classes. And abstract classes are a better choice when you want to enforce typing.

So, be sure to use the best construct even when traits look simpler and faster to implement.

2. Keep your traits small.

It is better to keep traits small and focused on a single set of functionalities. This makes it easier to use them in your classes and to maintain them.

If you want to create a more complete set of functionalities, you can create a trait that uses other traits.

For example:

/* Trait specific for screwdriver */
trait Screwdriver {
   
   private $screwSize;
   public function screw() {}
}
/* Trait specific for hammer */
trait Hammer {
   
   private $nailSize;   
   public function hammer() {}
}
/* Trait specific for wrench */
trait Wrench {
   
   private $wrenchSize;
   public function useWrench() {}
}
/* Composite Trait for tools */
trait Tools {
   
   use Screwdriver, Hammer, Wrench;
}

This gives you more flexibility in using traits in your classes.

For example:

/* A class where only Hammer is needed */
class Blacksmith {
   
   use Hammer;
}
/* A class where all the tools are needed */
class Workshop {
   
   use Tools;
}

Conclusion.

You saw how PHP Traits are a way to reuse the same code in different classes.

You also learned how and when to use them, and how they compare to interfaces, abstract classes and “include” scripts.

Did you know about traits? Are you going to use them in your apps? Leave a comment below and let me know!

Alex

Image copyright: Open source vector created by vectorjuice – www.freepik.com

2 thoughts on “PHP Traits Explained”

  1. I will like to learn about the php mailer step by step on how to create a mailer for sending mails for my business advert. Mailer option should comprise of sender address,recipents,message body and send column.please send me the tutorial steps in simply form to enable me understand it better and quicker because am just a novice

    Reply

Leave a Comment