Traits

So far, you have learned that extending from classes allows you to inherit code (properties and method implementations), but it has the limitation of extending only from one class each time. On the other hand, you can use interfaces to implement multiple behaviors from the same class, but you cannot inherit code in this way. To fill this gap, that is, to be able to inherit code from multiple places, you have traits.

Traits are mechanisms that allow you to reuse code, "inheriting", or rather copy-pasting code, from multiple sources at the same time. Traits, as abstract classes or interfaces, cannot be instantiated; they are just containers of functionality that can be used from other classes.

If you remember, we have some code in the class that manages the assignment of IDs. This code is not really part of a person, but rather part of an ID system that could be used by some other entity that has to be identified with IDs too. One way to extract this functionality from —and we are not saying that it is the best way to do so, but for the sake of seeing traits in action, we choose this one—is to move it to a trait.

To define a trait, do as if you were defining a class, just use the keyword instead of . Define its namespace, add the statements needed, declare its properties and implement its methods, and place everything in a file that follows the same conventions. Add the following code to the file:

<?php

namespace Bookstore\Utils;

trait Unique {
    private static $lastId = 0;
    protected $id;

    public function setId(int $id) {
        if (empty($id)) {
            $this->id = ++self::$lastId;
        } else {
            $this->id = $id;
            if ($id > self::$lastId) {
                self::$lastId = $id;
            }
        }
    }

    public static function getLastId(): int {
        return self::$lastId;
    }
    public function getId(): int {
        return $this->id;
    }
}

Observe that the namespace is not the same as usual, since we are storing this code in a different file. This is a matter of conventions, but you are entirely free to use the file structure that you consider better for each case. In this case, we do not think that this trait represents "business logic" like customers and books do; instead, it represents a utility for managing the assignment of IDs.

We include all the code related to IDs from . That includes the properties, the getters, and the code inside the constructor. As the trait cannot be instantiated, we cannot add a constructor. Instead, we added a method that contains the code. When constructing a new instance that uses this trait, we can invoke this method to set the ID based on what the user sends as an argument.

The class will have to change too. We have to remove all references to IDs and we will have to define somehow that the class is using the trait. To do that, we use the keyword , like in namespaces, but inside the class. Let's see what it would look like:

<?php

namespace Bookstore\Domain;

use Bookstore\Utils\Unique;

class Person {
    use Unique;

    protected $firstname;
    protected $surname;
    protected $email;

    public function __construct(
        int $id,
        string $firstname,
        string $surname,
        string $email
    ) {
        $this->firstname = $firstname;
        $this->surname = $surname;
        $this->email = $email;
       $this->setId($id);
    }

    public function getFirstname(): string {
        return $this->firstname;
    }
    public function getSurname(): string {
        return $this->surname;
    }
    public function getEmail(): string {
        return $this->email;
    }
    public function setEmail(string $email) {
        $this->email = $email;
    }
}

We add the statement to let the class know that it is using the trait. We remove everything related to IDs, even inside the constructor. We still get an ID as the first argument of the constructor, but we ask the method from the trait to do everything for us. Note that we refer to that method with , as if the method was inside the class. The updated hierarchic tree would look like the following (note that we are not adding all the methods for all the classes or interfaces that are not involved in the recent changes in order to keep the diagram as small and readable as possible):

Traits

Let's see how it works, even though it does so in the way that you probably expect. Add this code into your file, include the necessary statements, and execute it in your browser:

$basic1 = new Basic(1, "name", "surname", "email");
$basic2 = new Basic(null, "name", "surname", "email");
var_dump($basic1->getId()); // 1
var_dump($basic2->getId()); // 2

The preceding code instantiates two customers. The first of them has a specific ID, whereas the second one lets the system choose an ID for it. The result is that the second basic customer has the ID 2. That is to be expected, as both customers are basic. But what would happen if the customers are of different types?

$basic = new Basic(1, "name", "surname", "email");
$premium = new Premium(null, "name", "surname", "email");
var_dump($basic->getId()); // 1
var_dump($premium->getId()); // 2

The IDs are still the same. That is to be expected, as the trait is included in the class, so the static property will be shared across all the instances of the class , including and customers. If you used the trait from and customer instead of (but you should not), you would have the following result:

var_dump($basic->getId()); // 1
var_dump($premium->getId()); // 1

Each class will have its own static property. All instances will share the same , different from the of instances. This should make clear that the static members in traits are linked to whichever class uses them, rather than the trait itself. That could also be reflected on testing the following code which uses our original scenario where the trait is used from :

$basic = new Basic(1, "name", "surname", "email");
$premium = new Premium(null, "name", "surname", "email");
var_dump(Person::getLastId()); // 2
var_dump(Unique::getLastId()); // 0
var_dump(Basic::getLastId()); // 2
var_dump(Premium::getLastId()); // 2

If you have a good eye for problems, you might start thinking about some potential issues around the usage of traits. What happens if we use two traits that contain the same method? Or what happens if you use a trait that contains a method that is already implemented in that class?

Ideally, you should avoid running into these kinds of situations; they are warning lights for possible bad design. But as there will always be extraordinary cases, let's see some isolated examples on how they would behave.

The scenario where the trait and the class implement the same method is easy. The method implemented explicitly in the class is the one with more precedence, followed by the method implemented in the trait, and finally, the method inherited from the parent class. Let's see how it works. Take for example the following trait and class definitions:

<?php

trait Contract {
    public function sign() {
        echo "Signing the contract.";
    }
}

class Manager {
    use Contract;

    public function sign() {
        echo "Signing a new player.";
    }
}

Both implement the method, which means that we have to apply the precedence rules defined previously. The method defined in the class takes precedence over the one from the trait, so in this case, the executed method will be the one from the class:

$manager = new Manager();
$manager->sign(); // Signing a new player.

The most complicated scenario would be one where a class uses two traits with the same method. There are no rules that solve the conflict automatically, so you have to solve it explicitly. Check the following code:

<?php

trait Contract {
    public function sign() {
        echo "Signing the contract.";
    }
}

trait Communicator {
    public function sign() {
        echo "Signing to the waitress.";
    }
}

class Manager {
    use Contract, Communicator;
}

$manager = new Manager();
$manager->sign();

The preceding code throws a fatal error, as both traits implement the same method. To choose the one you want to use, you have to use the operator . To use it, state the trait name and the method that you want to use, followed by and the trait that you are rejecting for use. Optionally, use the keyword to add an alias like we did with namespaces so that you can use both the methods:

class Manager {
    use Contract, Communicator {
        Contract::sign insteadof Communicator;
        Communicator::sign as makeASign;
    }
}

$manager = new Manager();
$manager->sign(); // Signing the contract.
$manager->makeASign(); // Signing to the waitress.

You can see how we decided to use the method of instead of , but added the alias so that both methods are available. Hopefully, you can see that even the conflicts can be solved, and there are specific cases where there is nothing to do but deal with them; in general, they look like a bad sign—no pun intended.

Learning PHP 7
titlepage.xhtml
part0000.html
part0001.html
part0002.html
part0003.html
part0004.html
part0005.html
part0006.html
part0007_split_000.html
part0007_split_001.html
part0007_split_002.html
part0008_split_000.html
part0008_split_001.html
part0009.html
part0010.html
part0011.html
part0012.html
part0013_split_000.html
part0013_split_001.html
part0013_split_002.html
part0013_split_003.html
part0013_split_004.html
part0014_split_000.html
part0014_split_001.html
part0014_split_002.html
part0014_split_003.html
part0014_split_004.html
part0015_split_000.html
part0015_split_001.html
part0015_split_002.html
part0015_split_003.html
part0015_split_004.html
part0016_split_000.html
part0016_split_001.html
part0016_split_002.html
part0016_split_003.html
part0016_split_004.html
part0017_split_000.html
part0017_split_001.html
part0017_split_002.html
part0017_split_003.html
part0018.html
part0019_split_000.html
part0019_split_001.html
part0019_split_002.html
part0019_split_003.html
part0019_split_004.html
part0020_split_000.html
part0020_split_001.html
part0021_split_000.html
part0021_split_001.html
part0021_split_002.html
part0021_split_003.html
part0022.html
part0023_split_000.html
part0023_split_001.html
part0024_split_000.html
part0024_split_001.html
part0025_split_000.html
part0025_split_001.html
part0025_split_002.html
part0025_split_003.html
part0025_split_004.html
part0025_split_005.html
part0025_split_006.html
part0026.html
part0027_split_000.html
part0027_split_001.html
part0027_split_002.html
part0027_split_003.html
part0027_split_004.html
part0027_split_005.html
part0027_split_006.html
part0027_split_007.html
part0028_split_000.html
part0028_split_001.html
part0028_split_002.html
part0028_split_003.html
part0028_split_004.html
part0029_split_000.html
part0029_split_001.html
part0029_split_002.html
part0029_split_003.html
part0030_split_000.html
part0030_split_001.html
part0030_split_002.html
part0030_split_003.html
part0030_split_004.html
part0031_split_000.html
part0031_split_001.html
part0031_split_002.html
part0031_split_003.html
part0032.html
part0033_split_000.html
part0033_split_001.html
part0033_split_002.html
part0033_split_003.html
part0033_split_004.html
part0033_split_005.html
part0034_split_000.html
part0034_split_001.html
part0035.html
part0036.html
part0037_split_000.html
part0037_split_001.html
part0037_split_002.html
part0038_split_000.html
part0038_split_001.html
part0038_split_002.html
part0038_split_003.html
part0039_split_000.html
part0039_split_001.html
part0040.html
part0041_split_000.html
part0041_split_001.html
part0041_split_002.html
part0041_split_003.html
part0042_split_000.html
part0042_split_001.html
part0042_split_002.html
part0043.html
part0044.html
part0045_split_000.html
part0045_split_001.html
part0045_split_002.html
part0046_split_000.html
part0046_split_001.html
part0046_split_002.html
part0046_split_003.html
part0047_split_000.html
part0047_split_001.html
part0047_split_002.html
part0047_split_003.html
part0047_split_004.html
part0048.html
part0049.html
part0050_split_000.html
part0050_split_001.html
part0050_split_002.html
part0050_split_003.html
part0051.html
part0052.html
part0053_split_000.html
part0053_split_001.html
part0053_split_002.html
part0053_split_003.html
part0054.html
part0055.html
part0056_split_000.html
part0056_split_001.html
part0057_split_000.html
part0057_split_001.html
part0057_split_002.html
part0057_split_003.html
part0057_split_004.html
part0058_split_000.html
part0058_split_001.html
part0058_split_002.html
part0058_split_003.html
part0058_split_004.html
part0059_split_000.html
part0059_split_001.html
part0059_split_002.html
part0059_split_003.html
part0060_split_000.html
part0060_split_001.html
part0060_split_002.html
part0060_split_003.html
part0060_split_004.html
part0060_split_005.html
part0060_split_006.html
part0060_split_007.html
part0061_split_000.html
part0061_split_001.html
part0061_split_002.html
part0061_split_003.html
part0061_split_004.html
part0061_split_005.html
part0062_split_000.html
part0062_split_001.html
part0062_split_002.html
part0063.html
part0064_split_000.html
part0064_split_001.html
part0064_split_002.html
part0064_split_003.html
part0065_split_000.html
part0065_split_001.html
part0065_split_002.html
part0065_split_003.html
part0066_split_000.html
part0066_split_001.html
part0066_split_002.html
part0066_split_003.html
part0066_split_004.html
part0067_split_000.html
part0067_split_001.html
part0067_split_002.html
part0067_split_003.html
part0068.html
part0069_split_000.html
part0069_split_001.html
part0070.html
part0071_split_000.html
part0071_split_001.html
part0071_split_002.html
part0071_split_003.html
part0072_split_000.html
part0072_split_001.html
part0072_split_002.html
part0072_split_003.html
part0072_split_004.html
part0073_split_000.html
part0073_split_001.html
part0073_split_002.html
part0074_split_000.html
part0074_split_001.html
part0074_split_002.html
part0074_split_003.html
part0075_split_000.html
part0075_split_001.html
part0075_split_002.html
part0075_split_003.html
part0075_split_004.html
part0075_split_005.html
part0075_split_006.html
part0075_split_007.html
part0076_split_000.html
part0076_split_001.html
part0076_split_002.html
part0076_split_003.html
part0076_split_004.html
part0077.html
part0078.html
part0079_split_000.html
part0079_split_001.html
part0080.html
part0081_split_000.html
part0081_split_001.html
part0081_split_002.html
part0081_split_003.html
part0082_split_000.html
part0082_split_001.html
part0082_split_002.html
part0082_split_003.html
part0082_split_004.html
part0083_split_000.html
part0083_split_001.html
part0083_split_002.html
part0084_split_000.html
part0084_split_001.html
part0084_split_002.html
part0084_split_003.html
part0084_split_004.html
part0084_split_005.html
part0085_split_000.html
part0085_split_001.html
part0085_split_002.html
part0085_split_003.html
part0085_split_004.html
part0085_split_005.html
part0086.html
part0087.html
part0088_split_000.html
part0088_split_001.html
part0088_split_002.html
part0088_split_003.html
part0088_split_004.html
part0088_split_005.html
part0089_split_000.html
part0089_split_001.html
part0089_split_002.html
part0089_split_003.html
part0089_split_004.html
part0090_split_000.html
part0090_split_001.html
part0090_split_002.html
part0090_split_003.html
part0091.html
part0092_split_000.html
part0092_split_001.html
part0092_split_002.html
part0092_split_003.html
part0092_split_004.html
part0092_split_005.html
part0092_split_006.html
part0092_split_007.html
part0092_split_008.html
part0092_split_009.html
part0092_split_010.html
part0092_split_011.html
part0092_split_012.html
part0092_split_013.html
part0092_split_014.html
part0092_split_015.html
part0092_split_016.html
part0092_split_017.html
part0092_split_018.html
part0092_split_019.html
part0092_split_020.html
part0092_split_021.html
part0092_split_022.html
part0092_split_023.html
part0092_split_024.html