What is trait in PHP?

Trait in PHP

PHP is a single inheritance language. It means a child class can inherit only from only one parent in PHP.

I have added a diagram below to illustrate how single inheritance works in PHP.

In the above example we can see that Class A is the root level parent class. Class B and class C can inherit this class A. Again Class D, Class E can inherit the upper level class B. But class D or class E cannot inherit class B and class C together.

But if Class D or Class E needs to inherit the behaviours of both Class B and Class C together then we can provide a solution using trait.

So, Trait is a method to reuse code in single inheritance languages, like PHP.

A trait is similar to a class but when declaring it the trait keyword is used instead of class. The Trait cannot be instantiated on its own. 

The syntax of the trait is as follows.

trait traitName {
	public function functionName() {     
	}
}

Now if a class needs to use the methods or properties of the above trait without inheriting it then the syntax will be as follows:

class className {
    use traitName;
}

Here we see that a trait can be inserted into a class by listing in the use statement.

Now we will learn about Precedence of Trait.

Precedence

A member inserted by the Trait overrides an inherited member from a basic class. The precedence order is that members of the current class override Trait methods, which override inherited methods.

We can understand this from the following example. For this we will create a file called precedence.php.

Example 1
<?php
class NameClass {
    public function getName() {
        return 'John';
    }
}

trait NameTrait {
    public function getName() {
        return 'Wade';
    }
}

class UserName extends NameClass {
    use NameTrait;
}

$obj = new UserName();
echo $obj->getName();
?>

If we run this file in the browser then the output will be: Wade

Here the getName method of the NameTrait trait overrides the getName method of the NameClass class.

Now if we want to call any common method of both class and trait and call the method of class first then the code can be as follows.

Example 2
<?php
class NameClass {
    public function getName() {
        return 'John';
    }
}

trait NameTrait {
    public function getName() {
        $class_return = parent::getName();
        return $class_return. ' '. 'Wade';
    }
}

class UserName extends NameClass {
    use NameTrait;
}

$obj = new UserName();
echo $obj->getName();
?>

If we run this file in the browser then the output will be: John Wade

Now we will give an example of precedence order where the class that uses a trait also has the same method of the trait.

Example 3
<?php
trait NameTrait {
    public function getName() {
        return 'Wade';
    }
}

class UserName {
    use NameTrait;
    public function getName() {
        return 'Jack';
    }
}

$obj = new UserName();
echo $obj->getName();
?>

If we run this file in the browser then the output will be: Jack

Here the getName method of the UserName class overrides the getName method of the NameTrait trait.

Now we will learn how to use multiple traits.

Multiple Traits

We can add multiple traits to a class by specifying them in the use statement, separated by commas.

We can understand this from the following example. For this we will create a file called multiple.php.

Example of multiple traits
<?php
trait First {
    public function getFirstName() {
        return 'John';
    }
}

trait Last {
    public function getLastName() {
        return 'Wade';
    }
}

class Name {
    use First, Last;
    public function greet() {
        return 'Hello';
    }
}

$obj = new Name();
echo $obj->greet();
echo ", ";
echo $obj->getFirstName();
echo " ";
echo $obj->getLastName();
?>

If we run this file in the browser then the output will be: Hello, John Wade

In the above example I inserted two traits named First and Last into the Name class. Then I created an instance of the Name class. Then I accessed the getFirstName method of the Fast trait and the getLastName method of the Last trait.

Conflict

A fatal error occurs when two Traits insert a method with the same name. In the following example we will get this fatal error.

Example of naming conflict
<?php
trait First {
    public function getFirstName() {
        return 'John';
    }
    public function getAddress() {
        return 'Address 1';
    }
}

trait Last {
    public function getLastName() {
        return 'Wade';
    }
    public function getAddress() {
        return 'Address 2';
    }
}

class Name {
    use First, Last;
}

$obj = new Name();
echo $obj->getAddress();
?>

For the above example we get fatal error in the browser:

Fatal error: Trait method Last::getAddress has not been applied as Name::getAddress, because of collision with First::getAddress

To resolve naming conflicts like the example above, the insteadof keyword is used to select exactly one of the conflicting methods.

Example to resolve naming conflict
<?php
trait First {
    public function getFirstName() {
        return 'John';
    }
    public function getAddress() {
        return 'Address 1';
    }
}

trait Last {
    public function getLastName() {
        return 'Wade';
    }
    public function getAddress() {
        return 'Address 2';
    }
}

class Name {
    use First, Last {
        Last::getAddress insteadof First;
    }
}

$obj = new Name();
echo $obj->getAddress();
?>

If we run the above example in the browser then the output will be: Address 2

In the above example, we inserted two traits Fisrt and Last in the Name class and selected the getAddress method of the Last trait using the insteadof operator instead of the getAddress method of the Fisrt trait. Next we created an instance of the Name class and accessed the getAddress method of the Last trait. The Last trait’s getAddress method returns ‘Address 2’ so in the above example we get the output: Address 2.

Traits Composed from Traits

We can use one or more traits in a trait definition. We will see this in the following example.

Example of traits composed from traits
<?php
trait FirstName {
    public function getFirstName() {
        return 'John';
    }
}

trait LastName {
    public function getLastName() {
        return 'Wade';
    }
}

trait FullName {
    use FirstName, LastName;
}

class Name {
    use FullName;
}

$obj = new Name();
echo $obj->getFirstName();
echo " ";
echo $obj->getLastName();
?>

If we run the above example in the browser then the output will be: John Wade

In the above example we have inserted two traits called FirstName and LastName in the trait called FullName. Then I inserted this FullName attribute in the class called Name. Then I created an instance of the Name class and finally accessed two methods called getFirstName and getLastName.

Static Trait Members

Traits can habe static variables, static methods and static properties. In the following example we will define static property and static method within the trait.

Example of static trait members
<?php
trait StaticName {
    
    public static $greet = 'Hello';
    
    public static function getName() {
        return 'John';
    }
    
}

class Name {
    use StaticName;
}

echo Name::$greet;
echo ", ";
echo Name::getName();
?>

If we run the above example in the browser then the output will be: Hello, John

Properties and Constants

Traits can define properties and constants (as of PHP 8.2.0). Here We will create a PHP file and write the following code.

Example of Properties and Constants
<?php
trait UserTrait {
    public $age = 25;
    public const ACTIVE = 1;
}

class UserClass {
    use UserTrait;
    // public $age = 25; // will work
    // public $age = 26; // fatal error
    // public const ACTIVE = 1; // will work
    // public const ACTIVE = 2; // fatal error
}

$obj = new UserClass;
echo "age: ";
echo $obj->age;
echo " active: ";
echo $obj::ACTIVE;
?>

If we run the above example in the browser then the output will be: age: 25 active: 1

In this case, the UserTrait trait is inserted in the UserClass class. Then I created an instance of UserClass and then accessed the age property and the ACTIVE constant of UserTrait.

If a property or constant is defined in a trait, a class that inserts this trait cannot define a property or constant with the same name unless it is compatible, i.e. with the same visibility, initial value, type (for property), readonly modifier (for property), and finality (for constant).

In the above example, if we write public $age = 26 or public const ACTIVE = 2 in the UserClass, then running this PHP file in the browser will throw fatal error because the trait has defined public $age = 25 and public const ACTIVE = 1.

In this article I have discussed about PHP traits. You can learn more about this at: https://www.php.net/manual/en/language.oop5.traits.php.