Originally developed by Rasmus Lerdorf
in 1994, PHP is one of the most used general-purpose programming languages. Originally born as a template language, during the years it evolved into a fully-featured language with proper OOP
(Object Oriented Programming) support. Its latest incarnation, 7.x
, introduced new features like return type declaration
for functions and huge performance improvements. PHP is a single inheritance
language: this means that a class can inherit only for one parent or base class. To overcome this problem a feature was introduced in the language: traits
. In this article we will see how to use it and how it works.
In this tutorial you will learn:
- How to use traits in Php
Software Requirements and Conventions Used
Category | Requirements, Conventions or Software Version Used |
---|---|
System | Os-independent |
Software | PHP |
Other | Knowledge of PHP and basic object oriented programming concepts |
Conventions | # – requires given linux commands to be executed with root privileges either directly as a root user or by use of sudo command$ – requires given linux commands to be executed as a regular non-privileged user |
Introducing traits
PHP is a single inheritance language: a child class can inherit only from one single parent . This could pose a problem in terms of code reuse, if the class needs to inherit multiple behaviors: traits
are a way to solve this problem. Let’s see how. The way we create a trait is very similar to the way we create a class, but one of the big differences between traits and classes is that a trait cannot be instantiated, but can only be used inside a class or inside another trait. Creating a trait it’s easy:
<?php
trait traitone {
public function log() {
echo "I am a method of traitone!";
}
}
The code contained inside “traitone” is very trivial: it just logs a message. Let’s see how we can use it inside a class:
class Test {
use traitone;
}
The key here is the use
keyword, that, when used inside a class, let us “import” the functionalities grouped in the trait. It’s hard to imagine a case in which the class we created above could be useful, since it has no methods or properties of its own. Since the class makes use of the “traitone” trait, we can, however, access to the “log” method:
$test = new Test();
$test->log();
"I am a method of traitone!"
That was pretty easy, but what happens when a class has already a method with the same name of one included in the trait it uses ? Let’s verify it. We add a “log” method inside the Test class:
class Test {
use traitone;
public function log() {
echo "I am a method of the Test class!";
}
}
If we now call the “log” method we obtain a different result:
$test = new Test();
$test->log();
"I am a method of the Test class!"
What if the Test class had no “log” method, but inherited it from a base class it extended? Let’s see:
class Base {
public function log() {
echo "I am a method of the Base class!";
}
}
class Test extends Base {
use traitone;
}
$test = new Test();
$test->log();
"I am a method of traitone!"
As you can see we got “I am a method of traitone!” as the result of invoking the log method. This means that the Base
class method has been overridden by the one inside the trait. The hierarchy to keep in mind here is simple. Inside a class:
- Traits override inherited methods;
- Current class methods override trait methods;
Using multiple traits
Inside a class we can use multiple traits: all we have to do, is to include them after the use
keyword, separated by a comma:
class Test {
use traitone, traittwo;
}
The use of multiple traits, however can possibly create name conflicts. A fatal error is returned if the traits have methods with the same name:
trait traitone {
public function log() {
echo "I am a method of traitone!";
}
}
trait traittwo {
public function log() {
echo "I am a method of traittwo!";
}
}
class Test {
use traitone, traittwo;
}
PHP Fatal error: Trait method log has not been applied, because there are
collisions with other trait methods
1
What can we do to solve such type of conflicts? We can use the insteadof
operator, excluding the method we don’t want to use:
class Test {
use traitone, traittwo {
traitone::log insteadof traittwo;
}
}
The syntax is very simple: we specified that we want to use the “log” method from the traitone trait, solving the conflict. What if we want to do as above, but we still want to keep the functionality provided by the “log” method from traittwo? We must create an alias
for it. We can do it with the use of the as
operator. In the following example we alias the method “log” as “traittwo_log”:
class Test {
use traitone, traittwo {
traitone::log insteadof traittwo;
traittwo::log as traittwo_log;
}
This way we can still access the “log” method of traittwo, invoking it as traittwo_log():
$test = new Test();
$test->log();
I am a method of traitone!
$test->traittwo_log();
I am a method of traittwo!
Using the as operator to change visibility of a trait method
Inside a class, the as
operator can also be used to change the visibility of a method imported as part of a trait. In the previous example, all the methods of the traits are public
. We can change this inside the class:
class Test {
use traitone {
log as protected;
}
}
With the syntax above we specified that the log method from “traitone” should be used as protected
even if it was originally declared with a public
visibility. Indeed, if we try to invoke the log method we receive an error:
$test = new Test();
$test->log();
PHP Warning: Uncaught Error: Call to protected method Test::log() from context
'' in php shell code:1
If we provide a name for the alias, the change of visibility will be applied to the aliased method, while the original one will maintain its visibility:
class Test {
use traitone {
log as protected aliased_log;
}
}
$test = new Test();
$test->log();
I am a method of traitone!
$test->aliased_log();
PHP Warning: Uncaught Error: Call to protected method Test::aliased_log() from
context '' in php shell code:1
Abstract and static methods inside a trait
Inside a trait we can define both abstract
and static
methods. In the first case, the trait will require the class which uses it to provide a concrete implementation of the method. A fatal error will be generated if the class doesn’t implement the method or it is not declared itself as abstract:
<?php
trait traitone {
abstract function log();
}
class Test {
use traitone;
}
PHP Fatal error: Class Test contains 1 abstract method and must therefore be
declared abstract or implement the remaining methods (Test::log)
A trait method can also be declared and used as static
:
<?php
trait traitone {
public static function log() {
echo "I am a static method of traitone!";
}
}
class Test {
use traitone;
}
Test::log();
I am a static method of traitone!
Trait properties
Not only methods but also properties can be defined inside traits, the only rule to respect is that if a property is defined inside a trait, the class that makes use of it cannot define a property with the same name, unless it does have the same visibility and initial value. Here is an example:
<?php
/**
* This will generate a fatal error: both the trait and the class define a property
* with the same name and they have a different initial value
*/
trait traitone {
protected $name = "traitone";
}
class Test {
use traitone;
protected $name = "traittwo";
}
PHP Fatal error: Test and traitone define the same property ($name) in the
composition of Test. However, the definition differs and is considered
incompatible.
The $name
property is defined in “traitone” and also in the “Test” class. Both have the same visibility (protected), but their initial value is different, therefore they are considered incompatible, and this results into a fatal error.
Closing thoughts
In this article we learned to know traits
, a feature that enhances code reuse in PHP. We saw how traits cannot be instantiated but only used inside classes, via the use
keyword. We saw what is the precedence order that is used when a trait and the class it is used in implement methods with the same name, how to resolve such conflicts by using the insteadof
operator, and how to create aliases for the trait methods and how it’s possible to change their visibility with the as
operator.
Finally we saw how it’s also possible to define properties and static or abstract methods inside a trait. Want to know more about PHP? We have many articles on the subject!