How to Reuse PHP Code Effectively – Introduction to PHP traits

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

Software Requirements and Linux Command Line Conventions
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:

  1. Traits override inherited methods;
  2. 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!



Comments and Discussions
Linux Forum