Categories
PHP

OOP Traits

A trait defines methods and properties intended to be used by multiple classes. The structure of a trait is the same as a class, except that it’s declared using the keyword trait instead of class. You import traits into a class definition with the use keyword.

PHP trait

We know that extending from classes allows us to inherit code (properties and method implementations), but it has the limitation of extending only from one class each time. So, what do we do if two unrelated PHP classes need to exhibit similar behavior? To solve the problem of single inheritance, PHP 5.4 introduced a feature called traits:

  • A trait contains a set of methods and properties just like a class, but cannot be instantiated by itself.
  • Instead, the trait is included into a class and the class can then use its methods and properties as if they were declared in the class itself.
  • A class may use multiple traits by placing them in a comma-separated list. Similarly, a trait may be composed of one or more other traits.

The structure of a trait is the same as a class, except that it’s declared using the keyword trait instead of class. It is considered good practice to define only one trait per file, just like class and interface definitions:

<?php
 trait Users {
  //... 
 }

Example: Creating a Users trait:

<?php
trait Users {
 protected $firstName;
 protected $lastName;

 public function setFirstName($fn) {
  $this->firstName = $fn;
 }
 public function setLastName($ln) {
  $this->lastName = $ln;
 }
 public function getName() {
  return $this->firstName .  $this->lastName;
 }
}

PHP use a Trait

Using a PHP trait is easy. We import traits into a class with the use keyword (both namespaces and traits are imported with the use keyword). To use the Users trait, add the code use Users inside some PHP classes definition. Here’s an example:

<?php
 class BrainBellLibrary {
  use Users;
  // class code
 }

 class AnotherClass {
  use Users;
 }

 $x = new BrainBellLibrary();
 $x->setFirstName ('Brain');
 $x->setLastName ('Bell');
 echo $x->getName();  //Prints BrainBell

 $n = new AnotherClass();
 $n->setFirstName ('Abc');
 $n->setLastName ('Xyz');
 echo $n->getName();  //Prints AbcXyz

The trait’s methods behave as if they were directly defined in that class.

Inheritance and Traits

Trait methods override inherited methods. Likewise, methods defined in the class override methods inserted by a trait.

<?php
trait MyTrait {
 public function sayHello() {
  echo 'Trait says hello'; 
 }
}

class MyParent {
 public function sayHello() {
  echo 'MyParent class says hello'; 
 }
}

class MyClass extends MyParent {
 use MyTrait;
}

class MySecClass extends MyParent {
 use MyTrait;
 public function sayHello() {
  echo 'The child class says hello'; 
 }
}

$a = new MyClass();
$a->sayHello(); //Override the MyParent
//Prints: Trait says hello

$b = new MySecClass();
$b->sayHello(); //Override the MyTrait and MyParent
//Prints: The child class says hello

PHP trait conflicts

PHP will generate a fatal error if two traits attempt to insert a method with the same name:

Example: Using traits having the same name methods

<?php
 trait Users {
  protected $uname;
  public function setName($n){
   $this->uname = $n;
  }
 }

 trait Books {
  protected $bname;
  public function setName($n){
   $this->bname = $n;
  }
 }
 
 //Use both traits
 class MyClass {
  use Books, Users;
 }

When you execute the above code, it generates the following error:
Fatal error: Trait method Users::setName has not been applied as MyClass::setName, because of collision with Books::setName in…

Excluding trait methods

Using insteadof keyword to choose a method between traits to resolve conflict

To resolve the conflict you can use the insteadof keyword to specify which of the conflicting methods you want to use. In the following example, we choose Users trait’s method:

<?php
 class MyClass {
  use Books, Users {
    Users::setName insteadof Books;
  }
 }

 $myclass = new MyClass();
 $myclass->setName('My username');

Aliasing trait methods

Using as keyword to keep conflicting methods between traits.

The above code lets you exclude one of the trait methods, but if you want to keep both methods, you also need to use the as keyword:

<?php
 class MyClass {
  use Users,Books {
   Books::setName insteadof Users;
   Users::setName as setUsername;
  }
 }

 $myclass = new MyClass();
 $myclass->setName('My Book Name');
 $myclass->setUsername('My username');

First, use the insteadof keyword to exclude the conflicting method, and then use the as keyword to include that method with a different name to reference it, this way you can use the conflicting methods.


PHP OOP Tutorials: