Traits and Conflict Resolution

Traits inject reusable methods into unrelated classes — horizontal reuse. When two traits collide, insteadof and as sort it out.

PHP has single inheritance, but sometimes several unrelated classes need the same helper methods. A trait is a bundle of methods you can use inside any class, as if you'd copied and pasted them — but maintained in one place.

Easy example

<?php
trait Timestampable {
    public ?string $createdAt = null;
    public function stamp(): void {
        $this->createdAt = 'now';
    }
}

class Article {
    use Timestampable;
}

$a = new Article();
$a->stamp();
echo $a->createdAt; // now

When traits collide

If two traits define a method with the same name, PHP forces you to resolve it with insteadof (pick one) and optionally as (alias the other so you keep both).

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Share a timestamps() method (created_at, updated_at) across User, Post, and Order models using a trait.
  • Inject logging behaviour into unrelated classes with a LoggableTrait without duplicating the Logger dependency.
  • Resolve a naming conflict between two traits using insteadof and as to alias the alternative method.

More examples

Reusable trait across classes

The trait injects timestamp properties and methods into Post (and any other class that uses it) without inheritance.

Example · php
<?php
trait HasTimestamps {
    public string $createdAt;
    public string $updatedAt;

    public function touch(): void {
        $this->updatedAt = date('Y-m-d H:i:s');
    }
    public function initTimestamps(): void {
        $now = date('Y-m-d H:i:s');
        $this->createdAt = $now;
        $this->updatedAt = $now;
    }
}

class Post {
    use HasTimestamps;
    public function __construct(public string $title) {
        $this->initTimestamps();
    }
}

$p = new Post('Hello');
echo $p->createdAt;

Trait conflict resolution

insteadof chooses which trait's method wins; as creates an alias for the losing method so both are still accessible.

Example · php
<?php
trait A {
    public function hello(): string { return 'Hello from A'; }
}
trait B {
    public function hello(): string { return 'Hello from B'; }
}

class MyClass {
    use A, B {
        A::hello insteadof B;   // prefer A's hello
        B::hello as helloB;     // alias B's hello
    }
}

$obj = new MyClass();
echo $obj->hello();   // Hello from A
echo $obj->helloB();  // Hello from B

Abstract method in a trait

A trait can declare abstract methods to enforce that the using class provides the required implementation.

Example · php
<?php
trait Validatable {
    abstract protected function rules(): array;

    public function validate(array $data): array {
        $errors = [];
        foreach ($this->rules() as $field => $rule) {
            if ($rule === 'required' && empty($data[$field])) {
                $errors[] = "$field is required";
            }
        }
        return $errors;
    }
}

class RegisterForm {
    use Validatable;
    protected function rules(): array {
        return ['email' => 'required', 'password' => 'required'];
    }
}

$form   = new RegisterForm();
$errors = $form->validate(['email' => '', 'password' => 'secret']);
print_r($errors);

Discussion

  • Be the first to comment on this lesson.