Cara menggunakan php trait conflict

Trying to use several traits into one class could result in issues involving conflicting methods. You need to resolve such conflicts manually.

For example, let's create this hierarchy:

trait MeowTrait {
    public function say() {
        print "Meow \n";
    }
}

trait WoofTrait {
    public function say() {
        print "Woof \n";
    }
}

abstract class UnMuteAnimals {
    abstract function say();
}

class Dog extends UnMuteAnimals {
    use WoofTrait;
}

class Cat extends UnMuteAnimals {
    use MeowTrait;
}

Now, let's try to create the following class:

class TalkingParrot extends UnMuteAnimals {
    use MeowTrait, WoofTrait;
}

The php interpreter will return a fatal error:

Fatal error: Trait method say has not been applied, because there are collisions with other trait methods on TalkingParrot

Traits are used to declare methods that can be used in multiple classes. Traits can have methods and abstract methods that can be used in multiple classes, and the methods can have any access modifier (public, private, or protected).

Traits are declared with the trait keyword:

Syntax

trait TraitName {
  // some code...
}
?>

To use a trait in a class, use the use keyword:

Syntax

class MyClass {
  use TraitName;
}
?>

Let's look at an example:

Example

trait message1 {
public function msg1() {
    echo "OOP is fun! ";
  }
}

class Welcome {
  use message1;
}

$obj = new Welcome();
$obj->msg1();
?>

Try it Yourself »

Example Explained

Here, we declare one trait: message1. Then, we create a class: Welcome. The class uses the trait, and all the methods in the trait will be available in the class.

If other classes need to use the msg1() function, simply use the message1 trait in those classes. This reduces code duplication, because there is no need to redeclare the same method over and over again.



PHP - Using Multiple Traits

Let's look at another example:

Example

trait message1 {
  public function msg1() {
    echo "OOP is fun! ";
  }
}

trait message2 {
  public function msg2() {
    echo "OOP reduces code duplication!";
  }
}

class Welcome {
  use message1;
}

class Welcome2 {
  use message1, message2;
}

$obj = new Welcome();
$obj->msg1();
echo "
";

$obj2 = new Welcome2();
$obj2->msg1();
$obj2->msg2();
?>

Try it Yourself »

Example Explained

Here, we declare two traits: message1 and message2. Then, we create two classes: Welcome and Welcome2. The first class (Welcome) uses the message1 trait, and the second class (Welcome2) uses both message1 and message2 traits (multiple traits are separated by comma).

What they are, how to use them, and how they differ from interfaces, abstract classes and global functions. All with plenty of examples.

Let’s get started.

Contents


What are Traits in PHP?

A trait is a PHP construct that lets you reuse the same code in different classes.

You can define a trait using the following syntax:

trait SayGreeting {
   private $name = 'Alex';
   
   public function sayHello() {
      echo 'Hello, I\'m ' . $this->name;
   }
}

Traits can contain methods (like sayHello()) and properties (like $name).

Traits also support visibility operators (public, protected and private), static methods and properties, and class operators such as parent::, self:: and $this.

PHP classes can use traits and include the traits’ code.

To make a class use a trait, you need the use keyword.

For example:

class MyClass {
   use SayGreeting;
}

In the above example, the MyClass class uses the SayGreeting trait. By doing so, it’s like all the code inside the trait is copied into the class.

The class now has the sayHello() method as well as the $name property:

$myClass = new MyClass();
$myClass->sayHello();
/* Output:
 * Hello, I'm Alex
*/

How to use Traits.

In a nutshell, a trait is a structure that prevents code duplication in classes.

Different classes can use the same trait and include the trait’s methods and properties.

Traits have some similarities with interfaces and with abstract classes.

They are also similar to “include” scripts that contain global functions.

But traits have also important differences that make them unique.

Let’s see which ones in detail.

Traits vs Interfaces.

An interface is an OOP construct that lets you share the same set of functionalities among different classes.

You can find an in-depth explanation of PHP interfaces in this tutorial.

Interfaces are more specific than traits, because they can only contain abstract methods.

It’s up to the class that implements the interface to actually implement the methods.

With interfaces there is no code reuse at all because every class must implement the methods.

Traits, on the other hand, can provide fully implemented methods, on top of properties and class operators.

The classes that use the trait can reuse all the trait’s own code, with no code duplication whatsoever.

Traits vs Abstract Classes.

Both traits and abstract classes can include methods, either abstract or fully implemented.

However, if you want to use the methods from an abstract class into another class, you must extend the abstract class.

You can find an in-depth explanation of PHP abstract classes in this tutorial.

If you want to use the methods from an abstract class, you must establish an inheritance relation with your class. This is not always what you want.

Moreover, PHP does not support multiple inheritance so you cannot extend from more than one class.

This makes it impossible to define different sets of functions in different abstract classes and then extend from them to create different combinations.

Traits solve both problems:

  1. You can use the same trait in completely unrelated classes, without establishing an inheritance relation between them.
  2. You can use more traits in the same class.

Traits vs Include Scripts with Global Functions.

“Include” scripts contain PHP functions. If you need those functions in another PHP script, you can simply include that script.

Traits offer a similar solution. In fact, the main purpose of traits is to define some code once and then use it in different classes.

However, there are three main differences between traits and global functions:

  1. Global functions can be used anywhere, while traits can only be used inside classes.
  2. Traits can contain class elements and operators such as methods, properties, visibility modifiers (public, protected, private) and class operators (self::, parent::, $this).
  3. Traits can be combined: a trait can use other traits. This lets you define different traits combinations without the need of complex include statements.

What can Traits contain?

PHP traits can contain many of the elements of a PHP class.

Including:

  • Methods and properties with visibility operators (public, protected, private).
  • Static methods and properties.
  • Class operators, such as parent::, self:: and $this.

For example:

trait SayGreeting {
   /* A public property */
   public $info;
   
   /* A protected property */
   protected $language;
   
   /* A private property */
   private $name = 'my name';
   
   /* A static property */
   public static $count = 0;
   
   /* A public method */
   public function sayHello() {
       echo 'Hello, I\'m ' . $this->name;
   }
   
   /* A private method */
   private function sayGoodbye() {
	   echo 'Goodbye!';
   }
   
   /* A static method */
   public static function printCount() {
	   echo 'Class count: ' . self::$count;
   }
}
class MyClass {
   use SayGreeting;
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello, I'm Alex";
MyClass::$count = 5;
MyClass::printCount(); // Prints: "Class count: 5"
$myClass->sayGoodbye(): // Fatal error: private method.

Traits also support abstract methods.

Abstract methods must be implemented by the class that uses the trait:

trait SayGreeting {
   abstract public function sayHello();
}
class MyClass {
   use SayGreeting;
   
   /* Must implement the abstract sayHello() method */
   public function sayHello() {
      echo 'Hello!';
   }
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello!";

Traits can also use other traits.

In this case, all the code from the used traits is included into the trait, and then into the classes that use the trait.

For example:

trait SayHello {
   
   public function sayHello() {
      
      echo 'Hello!';
   }
}
trait SayGoodbye {
   
   public function sayGoodbye() {
      
      echo 'Goodbye!';
   }
}
/* The SayGreeting trait uses the SayHello and SayGoodbye traits */
trait SayGreeting {
   
   use SayHello, SayGoodbye;
}
/* By using SayGreeting, the methods from SayHello and SayGoodbye are included */
class MyClass {
   
   use SayGreeting;
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello!";
$myClass->sayGoodbye(); // Prints: "Goodbye!";

Methods precedence.

When you use a trait in your class, the class gets the methods from the trait.

You can override a trait method by defining it again in the class. In this case, the class’ own method has precedence over the trait’s method.

For example:

trait SayHello {
   
   public function sayHello() {
      
      echo 'Hello!';
   }
}
class MyClass {
   
   use SayHello;
   
   /* Override the trait's method */
    public function sayHello() {
      
      echo 'Hello from the class!';
   }
}

$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello from the class!";

If your class extends a base class, the methods from the traits have precedence over the methods of the base class with the same name.

You can, however, access the base class’ methods by using the parent:: operator.

For example:

/* Trait */
trait SayHello {
   
   public function sayHello() {
      
      echo 'Hello!';
   }
}
/* Base class */
class BaseClass {
   
   public function sayHello() {
      
      echo 'Hello from the base class!';
   }
}
class MyClass extends BaseClass {
   
   use SayHello;
   
   public function baseClassHello() {
      
      /* Call the parent's method */
	  parent::sayHello();
   }
}
$myClass = new MyClass();
$myClass->sayHello(); // Prints: "Hello!";
$myClass->baseClassHello(); // Prints: "Hello from the base class!";

Methods aliasing and conflicts.

PHP traits support aliasing.

When you use a trait in a class, you can alias a trait’s method to change its name and visibility.

This is done by using the as keyword.

For example:

trait SayHello {
   
   private function sayHello() {
      
      echo 'Hello!';
   }
}
class MyClass {
   
   /* Change sayHello() to hello() and make it public */
   use SayHello {sayHello as public hello; }
}
$myClass = new MyClass();
$myClass->hello(); // Prints: "Hello!"; 

Conflicts.

If you use more traits in the same class, and a method with the same name exists in more than one trait, then you have a conflict.

PHP doesn’t know which method to use, and you must explicitly solve the conflict.

To do that, you must choose which method to keep by using the insteadof keyword.

For example:

class MyClass {
   use SayGreeting;
}
0

You can still access the hidden method by using aliasing.

Like this:

class MyClass {
   use SayGreeting;
}
1

Traits good practices.

Here are 2 pieces of advice to use traits correctly.

1: Do not use traits instead of interfaces.

Once you get familiar with traits, they become very easy to use. And you may be tempted to abuse them.

Other constructs, such as interfaces and abstract classes, are a better choice in some cases.

For instance, interfaces are better when you want to share a set of functionalities across similar classes. And abstract classes are a better choice when you want to enforce typing.

So, be sure to use the best construct even when traits look simpler and faster to implement.

2. Keep your traits small.

It is better to keep traits small and focused on a single set of functionalities. This makes it easier to use them in your classes and to maintain them.

If you want to create a more complete set of functionalities, you can create a trait that uses other traits.

For example:

class MyClass {
   use SayGreeting;
}
2

This gives you more flexibility in using traits in your classes.

For example:

class MyClass {
   use SayGreeting;
}
3

Conclusion.

You saw how PHP Traits are a way to reuse the same code in different classes.

You also learned how and when to use them, and how they compare to interfaces, abstract classes and “include” scripts.

Did you know about traits? Are you going to use them in your apps? Leave a comment below and let me know!