PHP Traits

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 and Writer traits.
  • Second, define a new trait called Copier that is composed of Reader and Writer traits. In the copy() method of the Copier trait, call the  read() and write() methods of the Reader and Writer traits.
  • Third, use the Copier trait in the  copyFile() method of the FileUtil 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.

Did you find this tutorial useful?