PHP: Traits

Traits facilitate code reuse in PHP by providing a simple way to define functionality and reuse those functionalities when we need it.

Trait is a group of methods(and properties) that can be inserted and used in a class.

As we can use multiple traits in a class, so it compensates for the lack of multiple inheritance nature of PHP.

Traits do not comply with the “is-a” nature of inheritance, so traits do not follow the true strict inheritance behavior.

NOTE

Think of using a Trait as copy-pasting code & functionality.

When we use a Trait in a class, just imagine that it has copied the code from the Trait(s), and pasted it in the class.

After including(using) a trait in a class, then methods(and properties) behave as if those are defined in the class itself.

Traits Capability

Let’s see what Traits can and can not do-

Traits can do

Traits can contain properties.
Traits can contain constants.
Traits can contain methods (including abstract methods).
Traits can use properties and constants from the class using the trait.
Traits can call methods from the class using the trait.
Traits can be included/used in a class and the class can use the properties and methods from the trait.
A trait can be used in multiple classes.
Multiple traits can be used in a single class.
A trait can use one or more other traits inside it.

Traits can not do

Traits can not be instantiated by itself.
Traits can not implement interfaces.
Traits can not extend a class.

NOTE

Before PHP version 8.2, it was not possible to define a constant in a trait. If a constant is defined in an older version then an error with the following message would be generated-

Fatal Error: Traits can not have constants…..

NOTE

We have a full comparison of the Interface, Abstract Class, and trait.

Check the link below for a full comparison-

Trait Declaration

Use the keyword “trait” and then the name of the trait, to define a trait.
Use curly braces to wrap the code that belongs to the trait.
// Trait declaration

trait SampleTraitOne {
    // properties and methods here
}

Using Traits

To use the trait in a class just use the keyword “use” and then the trait name, like below-

// Trait usage

class SampleClassOne {
    // Use trait in the class
    use SampleTraitOne;
    
    // class properties and methods here
}

We can use one or more traits in a class-

The “use” statement can contain multiple trait names in a single “use” statement, in that case all the mentioned traits will be used by the class.
We can use multiple “use” statements, for using multiple traits.
Both multiple traits in a single “use” statement or multiple “use” statements can be used together in a class, at the same time.
// Trait usage

class SampleClassOne {
    // Use multiple traits
    use SampleTraitOne, SampleTraitTwo, TraitThree;
    
    // Declare trait use in multiple line
    use TraitFour;
    
    // class properties and methods here
}

NOTE

Traits can not be instantiated by itself. It has to be used in a class, to use its functionality.

Let’s check what happens when we print the class name inside a trait-

<?php

trait MyTrait {
    public function printClassName(): void {
        echo 'Class name (inside trait): ' . __CLASS__ . PHP_EOL;
    }

    public function printTraitName(): void {
        echo 'Trait name: ' . __TRAIT__ . PHP_EOL;
    }
}

class BigBoxOne {
    use MyTrait;
}

class BigBoxTwo {
    use MyTrait;
}

// Demo
$bigBoxOne = new BigBoxOne();
$bigBoxTwo = new BigBoxTwo();

$bigBoxOne->printClassName();
$bigBoxOne->printTraitName();

$bigBoxTwo->printClassName();
$bigBoxTwo->printTraitName();

Output:

It will print the class name where the trait is being used-

Class name (inside trait): BigBoxOne
Trait name: MyTrait


Class name (inside trait): BigBoxTwo
Trait name: MyTrait

Using Method from Trait

To use a method from a trait, make sure the trait is being used in the class.

Then call the method as if, it is declared inside the class.

<?php

// Trait definition
trait FirstTrait {
    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait;
}

// Demo
$bigBoxObj = new BigBoxClassExample();
// Call method from object, which is defined in trait
$bigBoxObj->myFunc();

NOTE

If you call the trait method inside any method of the class, then add a check with the function “method_exists()“.

class BigBoxClassExample {
    use FirstTrait;
    
    public function myClassFunc(): void {
        if (method_exists($this, 'myFunc')) {
            $this->myFunc();
        }
        
        // Some other code here
    }
}

This will make sure that the script does not throw errors in case the trait is not added in the class.

Declaring Abstract Method in Trait

We can add an abstraction function in the trait-

Declare an abstract function in the trait.
In the class add the “use” statement for the trait.
In the class define the function which was declared as abstract in the trait.
<?php

// Trait definition
trait FirstTrait {
    const BIG_BOX_CONST = "Some Special Value";

    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }

    // Declare an abstract function
    abstract public function myAbsFunc(): string;
}

// Class definition
class BigBoxClassExample {
    use FirstTrait;
    
    // Add the definition of the abstract function 
    public function myAbsFunc(): string
    {
        return "some string result generated in the abstract method" . PHP_EOL;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

echo $bigBoxObj->myAbsFunc();

Output:

myFunc in the First Trait
some string result generated in the abstract method

WARNING

Using abstract method is helpful when some part of the implementation will depend on the class(that is using the trait). So we can define those parts in the class, in the abstract class definition.

If the definition of the abstract method is not added in the class then we get an error like below-

PHP Fatal error:  Class BigBoxClassExample contains 1 abstract method 
and must therefore be declared abstract or implement the remaining methods 
(BigBoxClassExample::myAbsFunc) in test.php on line 16

Declaring Static Method in Trait

We can define a static method in the trait. It will behave as if the static method is defined in the class, that is using the trait.

<?php

// Trait definition
trait FirstTrait {
    const BIG_BOX_CONST = "Some Special Value";

    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }

    // Declare a static function
    public static function myStaticFunc(): string {
        return "some string result generated in the static method" . PHP_EOL;
    }
}

// Class definition
class BigBoxClassExample {
    use FirstTrait;
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

// Use the static method
echo $bigBoxObj::myStaticFunc();

Output:

myFunc in the First Trait
some string result generated in the static method

Declaring Final Method in Trait

We can define a method as final in the trait, but it will still be overridden in the class using the trait-

<?php

// Trait definition
trait FirstTrait {
    final public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

// Class definition
class BigBoxClassExample {
    use FirstTrait;

    public function myFunc(): void {
        echo "myFunc in the class" . PHP_EOL;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

Output:

myFunc in the class

Unlike a parent class. When we define a method as final in the parent and try to redefine it in the child, we get an error-

<?php

class BigBoxParent {
    final public function myFunc(): void {
        echo "myFunc in the Parent" . PHP_EOL;
    }
}

class ChildOne extends BigBoxParent{
    public function myFunc(): void {
        echo "myFunc in the child" . PHP_EOL;
    }
}

// Demo
$child = new ChildOne();
$child->myFunc();

Output:

PHP Fatal error:  Cannot override final method BigBoxParent::myFunc() in ..../sample.php on line 10

So when we extend the class that is using trait, and then try to redefine the method in the child, then we get an error-

<?php

// Trait definition
trait FirstTrait
{
    final public function myFunc(): void
    {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

// Parent Class definition
class BigBoxClassExample
{
    use FirstTrait;
}

// Child class definition
class BigBoxChild extends BigBoxClassExample
{
    // Try to override a final method
    public function myFunc(): void
    {
        echo "myFunc in the child class" . PHP_EOL;
    }
}

// Demo
$bigBoxObj = new BigBoxChild();
$bigBoxObj->myFunc();

Output:

PHP Fatal error:  Cannot override final method BigBoxClassExample::myFunc() in .../sample.php on line 22

Method Precedence

When a class uses a trait, and the same method is defined in the trait and the class(or in the parent class), then-

Methods(same) declared in a class have higher precedence than the same method declared in the trait.
Method declared in a trait has higher precedence than the same method declared in a parent class(of the class using trait)

Here is the precedence in a nutshell-

Class Method > Trait Method > Inherited Method( from Parent)

Case #1: Same function in Trait and Class

If a function with the same name is defined in the trait and the class using it, then the definition in the class gets higher priority

<?php

// Trait definition
trait BigBoxTraitExample {
    public function myFunc(): void {
        echo "myFunc in the trait" . PHP_EOL;
    }
}

// Class definition
class BigBoxClassExample {
    use BigBoxTraitExample;

    // Same function defined in the class and trait
    public function myFunc(): void
    {
        echo "myFunc in the class" . PHP_EOL;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

Output:

myFunc in the class

Case #2: Same function in Trait and Parent Class

If a function with same name is defined in the trait, and the parent class of the class using the trait.

And the function is not defined in the class it self, then the definition in trait gets higher priority than the function definition of the parent class

<?php

// Trait definition
trait BigBoxTraitExample {
    public function myFunc(): void {
        echo "myFunc in the trait" . PHP_EOL;
    }
}

// Parent class
class BigBoxParent {
    public function myFunc(): void {
        echo "myFunc in the parent" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample extends BigBoxParent {
    use BigBoxTraitExample;
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

Output:

myFunc in the trait

Case #3: Redefine the Function with a Different Signature

We can redefine a method, in the class with different signature. We can change the main signature, return type, even visibility-

<?php

// Trait definition
trait FirstTrait {
    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait;

    private function myFunc(): string {
        return "myFunc in the class" . PHP_EOL;
    }

    public function sampleFunc() {
        echo $this->myFunc();
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->sampleFunc();

Output:

myFunc in the class

Conflict Resolution

Let’s see what happens when we have function with the same name it multiple traits, and we want to use those multiple traits in a class-

Problem

Here we have function with name “myFunc” in 2 traits “FirstTrait” and “SecondTrait“.

And we are using “FirstTrait” and “SecondTrait” in the class “BigBoxClassExample”.

This will generate an error.

<?php

// Trait definition
trait FirstTrait {
    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

trait SecondTrait {
    public function myFunc(): void {
        echo "myFunc in the Second Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait, SecondTrait;
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

Output:

When we run the code, we get error like below-

PHP Fatal error:  Trait method SecondTrait::myFunc has not been applied as BigBoxClassExample::myFunc, because of collision with FirstTrait::myFunc in ...sample.php on line 17

In this case we can do one of the following-

Case #1: Resolve Conflict

While adding the “use” statement for the traits, for one of the traits we can use “insteadof” to give this priority. Like-

HighPriotiryTraitName::functionNameHere insteadof LowPriorityTraitName

Check the example below-

<?php

// Trait definition
trait FirstTrait {
    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

trait SecondTrait {
    public function myFunc(): void {
        echo "myFunc in the Second Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait;
    
    // Prefer myFunc from SecondTrait
    use SecondTrait {
        SecondTrait::myFunc insteadof FirstTrait;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

Output:

myFunc in the Second Trait

Case #2: Resolve Conflict and Use Both Methods

If we want to use both of the method definitions, then-

Give one of the method definitions higher priority by using “insteadof“.
Give the low-priority trait method a different name with the “as” keyword.
<?php

// Trait definition
trait FirstTrait {
    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

trait SecondTrait {
    public function myFunc(): void {
        echo "myFunc in the Second Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait {
        // Give myFunc from FirstTrait a different name
        FirstTrait::myFunc as someChangedFuncName;
    }
    
    // Prefer myFunc from SecondTrait
    use SecondTrait{
        SecondTrait::myFunc insteadof FirstTrait;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

// Calling with the changed name
$bigBoxObj->someChangedFuncName();

Output:

myFunc in the Second Trait
myFunc in the First Trait

Using Trait Property

We can define properties inside the trait. If a property is defined in the trait, and the trait is used in a class, then-

The property can be accessed from inside the trait.
The property can be accessed from inside the methods of the class.
If we use the “ReflectionClass” to check the class properties, the property defined in the trait seems like it belongs to the class itself.
<?php

// Trait definition
trait FirstTrait {
    protected string $someTraitProp = "some val of prop- 3456";

    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
        echo "value of \$someTraitProp: " . $this->someTraitProp . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait;

    public function myClassFunc(): void {
        echo 'Value of property defined in trait is: ' . $this->someTraitProp . PHP_EOL;
    }

    // Utility function for debugging purpose
    // Check properties
    public function printPropInfo(): void
    {
        $classRef = new ReflectionClass($this);
        $props = $classRef->getProperties();

        foreach ($props as $prop) {
            $output = "Prop: {$prop->getName()} | Type: {$prop->getType()} | Value: {$prop->getValue($this)} | ";

            if ($prop->isPublic()) {
                $output .=  "Visibility: Public";
            }

            if ($prop->isPrivate()) {
                $output .=  "Visibility: Private";
            }

            if ($prop->isProtected()) {
                $output .=  "Visibility: Protected";
            }

            echo $output . PHP_EOL;
        }
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();
$bigBoxObj->myClassFunc();
$bigBoxObj->printPropInfo();

Output:

myFunc in the First Trait
value of $someTraitProp: some val of prop- 3456

Value of property defined in trait is: some val of prop- 3456

Prop: someTraitProp | Type: string | Value: some val of prop- 3456 | Visibility: Protected

NOTE

If you call the trait property inside the class, then add a check with the function “property_exists()“.

class BigBoxClassExample {
    use FirstTrait;
    
    public function myClassFunc(): void {
        if (property_exists($this, 'someTraitProp')) {
            // Safe the use the trait property here
            $totalValue = $this->someTraitProp * 80 / 100;
        }
        
        // Some other code here
    }
}

This will make sure that the script does not throw errors in case the trait is not added in the class.

Redeclare Trait Property in Class

If there is a property defined in a trait, and we want to redeclare the same property in the class using the trait, then it is a little tricky. Follow the information below-

NOTE

If we redeclare a property(already defined in a trait) in a class(that is using the trait), then the definition needs to be exactly the same.

Property visibility, type, and value need to be fully compatible(exactly the same). Otherwise, we will get an error.

Let’s take a look at a few cases of this property redeclaration-

Case #1: Redeclare property with different value [Error]

The value of the property in the trait and in the class needs to be exactly the same. Else we will get an error-

<?php

// Trait definition
trait FirstTrait {
    protected string $someTraitProp = "some val of prop- 3456";

    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
        echo "value of \$someTraitProp: " . $this->someTraitProp . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    protected string $someTraitProp;

    // Use SecondTrait only (which in turn uses FirstTrait)
    use FirstTrait;

    public function myClassFunc(): void {
        echo 'Value of property defined in trait is: ' . $this->someTraitProp . PHP_EOL;
    }

}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();
$bigBoxObj->myClassFunc();

Output:

PHP Fatal error:  BigBoxClassExample and FirstTrait define the same property ($someTraitProp) in the composition of BigBoxClassExample. However, the definition differs and is considered incompatible. Class was composed in .../sample.php on line 23  

Case #2: Redeclare property with different visibility [Error]

The visibility(access modifier) needs to be the same for the property in the trait and in the class, else we will get error-

<?php

// Trait definition
trait FirstTrait {
    protected string $someTraitProp = "some val of prop- 3456";

    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
        echo "value of \$someTraitProp: " . $this->someTraitProp . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    protected string $someTraitProp = "some different value";

    use FirstTrait;

    public function myClassFunc(): void {
        echo 'Value of property defined in trait is: ' . $this->someTraitProp . PHP_EOL;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();
$bigBoxObj->myClassFunc();

Output:

PHP Fatal error:  BigBoxClassExample and FirstTrait 
define the same property ($someTraitProp) 
in the composition of BigBoxClassExample. 
However, the definition differs 
and is considered incompatible. 
Class was composed in .../sample.php on line 23  

Case #3: Change Property Value Later

Once the property is defined in the class(with exact same definition), then we can change the value at any point of the execution later-

<?php

// Trait definition
trait FirstTrait {
    protected string $someTraitProp = "some val of prop- 3456";

    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
        echo "value of \$someTraitProp: " . $this->someTraitProp . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    protected string $someTraitProp = "some val of prop- 3456";
    
    use FirstTrait;

    public function myClassFunc(): void {
        $this->someTraitProp = "value changed in the class";
        
        echo 'Value of property defined in trait is: ' . $this->someTraitProp . PHP_EOL;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();
$bigBoxObj->myClassFunc();

Output:

myFunc in the First Trait
value of $someTraitProp: some val of prop- 3456

Value of property defined in trait is: value changed in the class

Using Static Property in Trait

We can declare a static property in the trait, and a class can use the static property-

<?php

trait BigBoxTrait
{
    protected static int $totalCount = 0;

    function getTraitTotalCount(): int
    {
        return self::$totalCount;
    }
}

class BigBoxOne
{
    use BigBoxTrait;

    public function getTotalCount(): int
    {
        return self::$totalCount;
    }

    public function setTotalCount(int $count): void
    {
        self::$totalCount = $count;
    }
}


// Demo
$bigBoxOne = new BigBoxOne();

echo 'Total count in BigBoxOne: ' . $bigBoxOne->getTotalCount() . PHP_EOL;

// Change total count from BigBoxOne
$bigBoxOne->setTotalCount(999);

echo "After change" . PHP_EOL;

echo 'Total count in BigBoxOne: ' . $bigBoxOne->getTotalCount() . PHP_EOL;

Output:

Total count in BigBoxOne: 


After change
Total count in BigBoxOne: 999

NOTE

If a trait is used in multiple class, then static properties are not shared between those classes.

Unlike we have in a parent class. If 2 classes extend the same parent class and the parent has a static property, then the same static property is shared between the parent and child.

For clarity, check the following example with parent and child classes-

Define a static property in a parent class.
Define multiple child classes.
Change the value of the static property from any of the child class or parent.

The static property is shared between the parent and all children, so changing the static value from anywhere will change it for the parent and all children.

<?php

class BigBoxParent {
    protected static int $totalCount = 0;

    function getParentTotalCount(): int {
        return self::$totalCount;
    }
}

class BigBoxOne extends BigBoxParent{
    function getTotalCount(): int {
        return self::$totalCount;
    }

    function setTotalCount(int $count): void {
        self::$totalCount = $count;
    }
}

class BigBoxTwo extends BigBoxParent{
    function getTotalCount(): int {
        return self::$totalCount;
    }
}


// Demo
$bigBoxParent = new BigBoxParent();
$bigBoxOne = new BigBoxOne();
$bigBoxTwo = new BigBoxTwo();

echo 'Total count in BigBoxParent: ' . $bigBoxParent->getParentTotalCount() . PHP_EOL;
echo 'Total count in BigBoxOne: ' . $bigBoxOne->getTotalCount() . PHP_EOL;
echo 'Total count in BigBoxTwo: ' . $bigBoxTwo->getTotalCount() . PHP_EOL;

// Change total count from BigBoxOne
$bigBoxOne->setTotalCount(999);

echo "After change" . PHP_EOL;

echo 'Total count in BigBoxParent: ' . $bigBoxParent->getParentTotalCount() . PHP_EOL;
echo 'Total count in BigBoxOne: ' . $bigBoxOne->getTotalCount() . PHP_EOL;
echo 'Total count in BigBoxTwo: ' . $bigBoxTwo->getTotalCount() . PHP_EOL;

Output:

Total count in BigBoxParent: 
Total count in BigBoxOne:
Total count in BigBoxTwo:


After change
Total count in BigBoxParent: 999
Total count in BigBoxOne: 999
Total count in BigBoxTwo: 999

Now let’s try the same thing with trait

Define a trait.
Define static property in the trait.
Define multiple classes.
Use the trait from those multiple classes.
Change the static property from any of the classes.

The static property is not shared between the classes using the trait, so changing the static value from anywhere will not affect others.

<?php

trait BigBoxTrait
{
    protected static int $totalCount = 0;

    function getTraitTotalCount(): int
    {
        return self::$totalCount;
    }
}

class BigBoxOne
{
    use BigBoxTrait;

    function getTotalCount(): int
    {
        return self::$totalCount;
    }

    function setTotalCount(int $count): void
    {
        self::$totalCount = $count;
    }
}

class BigBoxTwo
{
    use BigBoxTrait;

    function getTotalCount(): int
    {
        return self::$totalCount;
    }
}


// Demo
$bigBoxOne = new BigBoxOne();
$bigBoxTwo = new BigBoxTwo();

echo 'Total count in BigBoxOne: ' . $bigBoxOne->getTotalCount() . PHP_EOL;
echo 'Total count in BigBoxTwo: ' . $bigBoxTwo->getTotalCount() . PHP_EOL;

// Change total count from BigBoxOne
$bigBoxOne->setTotalCount(999);

echo "After change" . PHP_EOL;

echo 'Total count in BigBoxOne: ' . $bigBoxOne->getTotalCount() . PHP_EOL;
echo 'Total count in BigBoxTwo: ' . $bigBoxTwo->getTotalCount() . PHP_EOL;

Output:

Total count in BigBoxOne: 
Total count in BigBoxTwo:


After change
Total count in BigBoxOne: 999
Total count in BigBoxTwo:

Trait Composition(from other Traits)

We can use one or more other Traits inside a trait. We can include the taits by “use” keyword-

<?php

// Trait definition
trait FirstTrait {
    public function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

// Second trait definition
trait SecondTrait {
    // Use FirstTrait in SecondTrait
    use FirstTrait;

    public function mySecondFunc(): void {
        echo "mySecondFunc in the Second Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    // Use SecondTrait only (which in turn uses FirstTrait)
    use SecondTrait;
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();
$bigBoxObj->mySecondFunc();

Output:

myFunc in the First Trait
mySecondFunc in the Second Trait

Change Visibility

We can change the visibility of a method of a trait, while using it in a class.

Problem

Say we have a trait with a private method. and we want to call the method from the class object-

<?php

// Trait definition
trait FirstTrait {
    private function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait;
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

Output:

We will get an error as the methods private, and we are invoking it from the class object.

PHP Fatal error:  Uncaught Error: Call to private method BigBoxClassExample::myFunc() from global scope in .../sample.php:31
Stack trace:
#0 {main}
  thrown in .../sample.php on line 31

Change Visibility of the Method

We can change the visibility of the method while including the trait in a class, using the “as” keyword and then the access modifier(public/protected/public).

In the following example we have changed the visibility of “myFunc” method to “public“-

<?php

// Trait definition
trait FirstTrait {
    private function myFunc(): void {
        echo "myFunc in the First Trait" . PHP_EOL;
    }
}

// Our class
class BigBoxClassExample {
    use FirstTrait {
        // Change visibility of the method from private to public
        FirstTrait::myFunc as public;
    }
}

// Demo
$bigBoxObj = new BigBoxClassExample();
$bigBoxObj->myFunc();

Output:

myFunc in the First Trait

Example

<?php

// Define trait for NOS
trait Nos
{
    private bool $usingNos = false;

    public function addNos(): void
    {
        $this->usingNos = true;

        echo "Adding NOS" . PHP_EOL;
    }
}

// Define class Car
class Car
{
    // Use trait Nos in the class
    use Nos;

    public function __construct(
        private int $wheel,
        private int $engine,
        private int $seat,
        private int $door,
        private bool $interior
    ) {
    }

    public function start(): void
    {
        echo "Car started" . PHP_EOL;
    }

    public function stop(): void
    {
        echo "Car Stopped" . PHP_EOL;
    }

    public function repair(): void
    {
        echo "Repair Car" . PHP_EOL;
    }

    // Utility function for debugging purpose
    public function printInfo(): void
    {
        $carRef = new ReflectionClass($this);
        $props = $carRef->getProperties();

        foreach ($props as $prop) {
            echo "Prop: {$prop->getName()} | Type: {$prop->getType()} | Value: {$prop->getValue($this)}" . PHP_EOL;
        }
    }
}



$car = new Car(4, 2, 4, 4, true);

$car->start();
$car->stop();
$car->repair();

$car->addNos();

$car->printInfo();

Output:

Car started
Car Stopped
Repair Car

Adding NOS

Prop: wheel | Type: int | Value: 4
Prop: engine | Type: int | Value: 2
Prop: seat | Type: int | Value: 4
Prop: door | Type: int | Value: 4
Prop: interior | Type: bool | Value: 1

Prop: usingNos | Type: bool | Value: 1

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.