In traditional OOP, a class can only inherit from one parent class (a concept known as single inheritance). This can limit code reuse when you need to share a small set of methods across several unrelated classes.
Traits solve this problem. A Trait is a mechanism for code reuse in single inheritance languages. It allows you to group methods and properties that you want to include in multiple classes without requiring them to share a common parent.
- Key Idea: Traits offer horizontal reuse, meaning they allow classes from different family trees to reuse the same set of methods.
1. Defining and Using a Trait
You define a Trait using the trait keyword, and you use it inside a class with the use keyword.
| Keyword | Purpose |
trait | Used to define the reusable code block. |
use | Used inside a class to import the Trait’s methods and properties. |
Example: Reusing Logging and Sending Functionality
Imagine you have a Blog class and a User class. They are unrelated, but both need methods to send notifications and log events.
<?php
// 1. Define the Trait
trait Notifier {
public function sendEmail($recipient, $subject) {
return "Notifying $recipient: Email sent with subject '$subject'.";
}
public function logActivity($message) {
$time = date("H:i:s");
return "[$time] Logged activity: $message";
}
}
// 2. Use the Trait in two different, unrelated classes
class BlogArticle {
// Import the Notifier Trait
use Notifier;
public $title = "New Feature Launch";
}
class SystemUser {
// Import the Notifier Trait
use Notifier;
public $username = "Admin";
}
$article = new BlogArticle();
$user = new SystemUser();
// The BlogArticle object can now use the Trait's methods:
echo $article->sendEmail("subscribers", $article->title) . "<br>";
// The SystemUser object can also use the exact same methods:
echo $user->logActivity("User {$user->username} performed a system check.");
// Output:
// Notifying subscribers: Email sent with subject 'New Feature Launch'.
// [14:03:37] Logged activity: User Admin performed a system check.
?>
2. Priority and Conflict Resolution
What happens if a class defines a method with the exact same name as a method in the Trait it is using?
The priority order is strictly defined:
- Class Method (The method defined directly in the class).
- Trait Method (The method imported from the Trait).
- Parent Class Method (The method inherited from the parent class).
The class method always wins and overrides the Trait method.
Example: Overriding a Trait Method
If the BlogArticle class above had its own logActivity() method, that method would be executed instead of the one from the Notifier Trait.
class BlogArticle {
use Notifier;
// This method will be used instead of the one in the Trait
public function logActivity($message) {
return "Blog Logger: " . $message;
}
}
3. Resolving Name Clashes (Multiple Traits)
A conflict occurs if a class uses two different Traits that both define a method with the same name. You must use the insteadof operator to explicitly choose which method to use.
trait LogSQL {
public function log() { return "Logging SQL Query..."; }
}
trait LogFile {
public function log() { return "Logging to a file..."; }
}
class Database {
use LogSQL, LogFile {
// Use the log() method from LogSQL, and exclude the one from LogFile
LogSQL::log insteadof LogFile;
}
}
$db = new Database();
echo $db->log();
// Output: Logging SQL Query...
⏭️ Next Steps
Traits complete the discussion on code reusability. The remaining pending OOP topics are: Class Constants, and Iterables.
