What Problem Do Traits Solve?

As we learned in the previous lesson, PHP only allows a class to inherit from one parent (single inheritance). But what if you want to share a block of code between several unrelated classes? For example, you might want both a User class and a Product class to have logging capabilities. They don't share a logical parent, so inheritance doesn't fit.

This is where Traits come in. A Trait is a mechanism for code reuse in single inheritance languages like PHP. Think of it as a "copy-and-paste" mixin or a set of "add-on features" that any class can use without needing to be related.


How to Use Traits

Using traits is a two-step process:

  1. Define the Trait: You create a trait block, which looks very similar to a class, and place the reusable methods inside it.
  2. Use the Trait: In any class where you want to use those methods, you add the use keyword followed by the Trait's name.

Example:


// Step 1: Define the Trait
trait Loggable {
    public function log($message) {
        // In a real application, this would write to a file.
        echo "[LOG]: " . $message;
    }
}

// Create a class that has nothing to do with logging
class User {
    // Step 2: Use the Trait to import the functionality
    use Loggable;

    public $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function createUser() {
        // Now we can use the log() method as if it belongs to this class!
        $this->log("User '{$this->name}' created.");
    }
}

class Product {
    // This class is completely unrelated to User, but it can also use the Trait.
    use Loggable;

    public $productName;

    public function __construct($productName) {
        $this->productName = $productName;
    }

    public function updateStock() {
        $this->log("Stock updated for product '{$this->productName}'.");
    }
}

$user = new User("Alice");
$user->createUser(); // Outputs: [LOG]: User 'Alice' created.

echo "<br>";

$product = new Product("PHP Course");
$product->updateStock(); // Outputs: [LOG]: Stock updated for product 'PHP Course'.

Resolving Name Conflicts

What happens if a class uses two different traits that both have a method with the same name? PHP will produce a fatal error. To solve this, you must explicitly tell PHP which method to use with the insteadof and as keywords.


trait TraitA {
    public function doSomething() {
        echo "Doing something from Trait A.";
    }
}

trait TraitB {
    public function doSomething() {
        echo "Doing something from Trait B.";
    }
}

class MyClass {
    use TraitA, TraitB {
        // Explicitly say we want to use doSomething() from TraitA instead of TraitB
        TraitA::doSomething insteadof TraitB;

        // Optionally, we can make TraitB's method available with a new name
        TraitB::doSomething as doSomethingElse;
    }
}

$obj = new MyClass();
$obj->doSomething(); // Outputs: Doing something from Trait A.
echo "<br>";
$obj->doSomethingElse(); // Outputs: Doing something from Trait B.

Conclusion: Trait vs. Inheritance

  • Use Inheritance (extends) for "is-a" relationships (a Motorcycle is a Vehicle). It defines what an object is.
  • Use Traits (use) for "can-do" capabilities (a User can do logging, a Product can do logging). It defines what an object can do.