As a PHP programmer you might encountered scenarios that you need to extend more than one class to accomplish a certain task but unfortunately php doesn’t support multi inheritance so you can only inherit from one class.
However in php 5.4 a new feature added known as Traits. A Trait is 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 inheritance.
So What are PHP Traits?
A trait is similar to a class but for grouping methods in a fine-grained and consistent way. It is not allowed to instantiate a trait on its own. So a trait is just a container for a group of methods that you can reuse in another classes.
How do Traits work?
A Trait is basically just a way to “copy and paste” code during run time. So when you run your application all trait methods are copied and pasted into the class using them as if they defined in the same class.
Traits examples
Declaring a new trait is similar to declaring a new class as shown in the following example:
<?php trait Logger { function log($msg) { echo '<pre>'; echo date('Y-m-d h:i:s') . ':' . '(' . __CLASS__ . ') ' . $msg . '<br/>'; echo '</pre>'; } }
To use a trait in a class, you use the use
keyword. All the trait’s methods are available in the class where it is used. Calling a method of a trait is similar to calling an instance method.
class User{ use Logger; function __construct(){ $this->log("A new user created"); } }
We can also reuse the Logger
trait in the Post
class as follows:
class Post{ use Logger; function __construct() { $this->log("A new post created"); } }
Both User
and Post
classes reuse methods of the Logger
trait, which is very flexible.
We can test our script to see how it works.
$user = new User(); $post = new Post();
Using multi traits
A class can use multi traits. Consider that you have a class that needs to import methods from more than one trait as shown in the following example.
trait Logger{ function log($msg) { echo $msg; } } trait DateHelper{ function format($format) { return date($format); } } class Formatter{ use Logger, DateHelper; public function __construct() { $this->log("Post class initiated"); } function create($format) { echo $this->format($format); } }
Using traits in another traits
A trait can be composed of other traits by using the use
statement in the trait’s declaration. See the following example:
trait Logger{ function log($msg) { echo $msg; } } trait Post{ use Logger; $this->log("a new Post obj created"); }
In the above code we created a Logger trait. Next we created a new trait Post. to use the Logger in the Post trait we import it the same way we do it in a class using the use
statement in the trait’s declaration. Now we able to use the Logger method.
PHP trait’s method conflict resolution
If a class uses multiple traits that share the same method name, PHP will raise a fatal error. Fortunately, you can tell PHP which method of which trait to be used by using inteadof
keyword. Let’s take a look at the following example:
<?php trait FileLogger{ public function log($msg){ echo 'File Logger ' . date('Y-m-d h:i:s') . ':' . $msg . '<br/>'; } } trait DatabaseLogger{ public function log($msg){ echo 'Database Logger ' . date('Y-m-d h:i:s') . ':' . $msg . '<br/>'; } } class Logger{ use FileLogger, DatabaseLogger{ FileLogger::log insteadof DatabaseLogger; } } $logger = new Logger(); $logger->log('this is a test message #1'); $logger->log('this is a test message #2');
Both FileLogger
and DatabaseLogger
traits have the same log()
method. In the Logger
class, we resolved the method name conflict by specifying that the log()
method of the FileLogger
trait will be used instead of the DatabaseLogger
‘s.
But what about using both methods in the FileLogger
and DatabaseLogger
traits. you can use alias for the method of the trait within the class that uses the trait.
Aliasing trait method
By using aliases for the same method name of multiple traits, you can reuse all the methods in those traits. You use the as
keyword to alias a method of a trait to a different name within the class that uses the trait. See the below example
class Logger{ use FileLogger, DatabaseLogger{ DatabaseLogger::log as log2DB; FileLogger::log insteadof DatabaseLogger; } } $logger = new Logger(); $logger->log('this is a test message #1'); $logger->log2DB('this is a test message #2');
The method log()
of the DatabaseLogger
class has a new name ( log2DB
) in the context of the Logger
class.
So What are typical scenarios for using Traits?
As we see in the examples above traits is a container for group of methods so the typical scenarios for using it to reuse a chunk of code between a set of similar classes that should not inherit from the same abstract class.
I edited the last program segment in this article as follows.
class Logger{
use FileLogger, DatabaseLogger{
DatabaseLogger::log as logDB;
FileLogger::log as logFile;
}
}
$logger = new Logger();
$logger->logFile(‘this is a test message #1’);
$logger->logDB(‘this is a test message #2’);
However, I get this error: “Trait method log has not been applied, because there are collisions with other trait methods on Logger.” Why didn’t it work? The trait method names are now unique (or so it looks like?), so I thought it would be enough to prevent collisions without having the need to use the “insteadof” keyword.
No i think you should use the insteadof operator you can refer to this issue here
https://stackoverflow.com/questions/25064470/collisions-with-other-trait-methods