Traits in PHP
Traits in PHP
https://www.sitepoint.com/using-traits-in-php-5-4
In this article I will discuss traits, a new feature introduced in PHP 5.4
to overcome such issues. The concept of traits itself is nothing new to
programming and is used in other languages like Scala and Perl. They
allows us to horizontally reuse code across independent classes in
different class hierarchies.
class FileReader
{
use Singleton;
}
- Constants [0] {
}
- Static properties [1] {
Property [ private static $_instance ]
}
- Static methods [1] {
Method [ static public method instance ] {
@@ /home/shameer/workplace/php54/index.php 6
- 11
}
}
- Properties [0] {
}
- Methods [0] {
}
}
Multiple Traits
So far we have used only one trait with a class, but in some cases we
may need to incorporate the functionality of more than one trait.
<?php
trait Hello
{
function sayHello() {
echo "Hello";
}
}
trait World
{
function sayWorld() {
echo "World";
}
}
class MyWorld
{
use Hello, World;
}
Here we have two traits, Hello and World. Trait Hello is only able
to say “Hello” and trait World can say “World”. In the MyWorld class
we have applied Hello and World so that the MyWorld object will
have methods from both traits and be able to say “Hello World”.
class MyWorld
{
use HelloWorld;
}
Here we have created the trait HelloWorld, using traits Hello and
World, and included it in MyWorld. Since the HelloWorld trait has
methods from the other two traits, it’s just the same as if we had
including the two traits in the class ourselves.
Precedence Order
As I’ve already mentioned, traits work as if their methods have been
copied and pasted into the classes using them and they are totally
flattened into the classes’ definition. There may be methods with the
same name in different traits or in the class itself. You might wonder
which one will be available in the object of child class.
function sayWorld() {
return "Trait World";
}
function sayHelloWorld() {
echo $this->sayHello() . " " . $this->sayWorld();
}
function sayBaseWorld() {
echo $this->sayHello() . " " . parent::sayWorld();
}
}
class Base
{
function sayWorld(){
return "Base World";
}
}
$h = new HelloWorld();
$h->sayHelloWorld(); // Hello World
$h->sayBaseWorld(); // Hello Base World
trait Music
{
function play() {
echo "Playing music";
}
}
class Player
{
use Game, Music;
}
Such trait conflicts aren’t resolved automatically for you. Instead, you
must choose which method should be used inside the composing
class using the keyword insteadof.
<?php
class Player
{
use Game, Music {
Music::play insteadof Game;
}
}
$player = new Player();
$player->play(); //Playing music
Here we have chosen to use the play() method of the Music trait
inside the composing class so the class Player will play music, not a
game.
In the above example, one method has been chosen over the other
from two traits. In some cases you may want to keep both of them, but
still avoiding conflicts. It’s possible to introduce a new name for a
method in a trait as an alias. An alias doesn’t rename the method, but
offers an alternate name by which it can be invoked. Aliases are
created using the keyword as.
<?php
class Player
{
use Game, Music {
Game::play as gamePlay;
Music::play insteadof Game;
}
}
Reflection
The Reflection API is one of the powerful features of PHP to analyze
the internal structure of interfaces, classes, and methods and reverse
engineer them. And since we’re talking about traits, you might be
interested to know about the Reflection API’s support for traits. In PHP
5.4, four methods have been added to ReflectionClass to get
information about traits in a class.
Other Features
Apart from the above mentioned, there are other features that makes
traits more interesting. We know that in classical inheritance the
private properties of a class can’t be accessed by child classes. Traits
can access the private properties or methods of the composing
classes, and vice versa! Here is an example:
<?php
trait Message
{
function alert() {
echo $this->message;
}
}
class Messenger
{
use Message;
private $message = "This is a message";
}
function alert() {
$this->define();
echo $this->message;
}
abstract function define();
}
class Messenger
{
use Message;
function define() {
$this->message = "Custom Message";
}
}
Unlike traits in Scala, traits in PHP can have a constructor but it must
be declared public (an error will be thrown if is private or protected).
Anyway, be cautious when using constructors in traits, though,
because it may lead to unintended collisions in the composing
classes.
Summary
Traits are one of the most powerful features introduced in PHP 5.4,
and I’ve discussed almost all their features in this article. They let
programmers reuse code fragments horizontally across multiple
classes which do not have to be within the same inheritance
hierarchy. Instead of having complex semantics, they provide us with
a light weight mechanism for code reuse. Though there are some
drawbacks with traits, they certainly can help improve the design of
your application removing code duplication and making it more DRY.