Summary: in this tutorial, you will learn how to use PHP traits to share functionality across independent classes, which are not in the same inheritance hierarchy.
Introduction to PHP traits
Code reuse is one of the most important aspects of object-oriented programming. In PHP, you use inheritance to enable code reuse in different classes with the same inheritance hierarchy.
To achieve code reuse, you move the common functionality of classes to methods of the parent class. However, inheritance makes the code very tightly coupled. Therefore, overuse of inheritance may cause the code very hard to maintain.
To overcome this problem, PHP 5.4 introduced a new reusable unit of code called trait
. Traits allow you to reuse various methods freely in many different classes that do not need to be in the same class hierarchy.
Inheritance allows classes to reuse the code vertically while the traits allow classes reuse the code horizontally.
A trait is similar to a class, but it is only for grouping methods in a fine-grained and consistent way. PHP does not allow you to create an instance of a Trait like an instance of a class. And there is no such concept of an instance of a trait.
PHP Trait example
To define a trait, you use the trait keyword followed by a name as follows:
<?php
trait Logger
{
public function log($msg)
{
echo '<pre>';
echo date('Y-m-d h:i:s') . ':' . '(' . __CLASS__ . ') ' . $msg . '<br/>';
echo '</pre>';
}
}
Code language: HTML, XML (xml)
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.
The following example demonstrates how to use the Logger
trait in the BankAccount
class:
class BankAccount
{
use Logger;
private $accountNumber;
public function __construct($accountNumber)
{
$this->accountNumber = $accountNumber;
$this->log("A new $accountNumber bank account created");
}
}
Code language: PHP (php)
And you can reuse the Logger
trait in the User
class as follows:
class User
{
use Logger;
public function __construct()
{
$this->log('A new user created');
}
}
Code language: PHP (php)
Both BankAccount
and User
classes reuse methods of the Logger
trait, which is very flexible.
Using multiple traits
A class can use multiple traits. The following example demonstrates how to use multiple traits in the IDE class. It simulates the C compilation model in PHP for the sake of demonstration.
<?php
trait Preprocessor
{
public function preprocess()
{
echo 'Preprocess...done' . '<br/>';
}
}
trait Compiler
{
public function compile()
{
echo 'Compile code... done' . '<br/>';
}
}
trait Assembler
{
public function createObjCode()
{
echo 'Create the object code files... done.' . '<br/>';
}
}
trait Linker
{
public function createExec()
{
echo 'Create the executable file...done' . '<br/>';
}
}
class IDE
{
use Preprocessor, Compiler, Assembler, Linker;
public function run()
{
$this->preprocess();
$this->compile();
$this->createObjCode();
$this->createExec();
echo 'Execute the file...done' . '<br/>';
}
}
$ide = new IDE();
$ide->run();
Code language: HTML, XML (xml)
Composing multiple traits
PHP allows you to compose multiple traits into a trait by using the use
statement in the trait’s declaration. For example:
<?php
trait Reader
{
public function read($source)
{
echo sprintf('Read from %s <br>', $source);
}
}
trait Writer
{
public function write($destination)
{
echo sprintf('Write to %s <br>', $destination);
}
}
trait Copier
{
use Reader, Writer;
public function copy($source, $destination)
{
$this->read($source);
$this->write($destination);
}
}
class FileUtil
{
use Copier;
public function copyFile($source, $destination)
{
$this->copy($source, $destination);
}
}
Code language: HTML, XML (xml)
How it works.
- First, define
Reader
andWriter
traits. - Second, define a new trait called
Copier
that is composed ofReader
andWriter
traits. In thecopy()
method of theCopier
trait, call theread()
andwrite()
methods of theReader
andWriter
traits. - Third, use the
Copier
trait in thecopyFile()
method of theFileUtil
class to simulate the file copy.
PHP trait’s method conflict resolution
Overriding trait method
When a class uses multiple traits that share the same method name, PHP will raise a fatal error.
Fortunately, you can instruct PHP to use the method by using the inteadof
keyword. For 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');
Code language: HTML, XML (xml)
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.
What if you want to use both log()
methods from the FileLogger
and DatabaseLogger
traits? if so, you can use an 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.
The following example illustrates how to alias trait method to resolve the method name conflict:
class Logger
{
use FileLogger, DatabaseLogger{
DatabaseLogger::log as logToDatabase;
FileLogger::log insteadof DatabaseLogger;
}
}
$logger = new Logger();
$logger->log('this is a test message #1');
$logger->logToDatabase('this is a test message #2');
Code language: PHP (php)
The method log()
of the DatabaseLogger
class has a new name ( logToDatabase
) in the context of the Logger
class.
In this tutorial, you have learned how to use PHP traits to reuse the code outside of a class hierarchy.