Lesson 24 of 100
Classes12 min5 checksPHP 8.5

Traits: Reuse Behavior Without Inheritance

Learn how a PHP trait groups methods and properties, how a class composes it with use, and how precedence and name conflicts are resolved.

01 · THE IDEAREAD → TRY → LEARN

Why traits exist

PHP classes can extend only one parent. Sometimes two unrelated classes need the same small piece of behavior, such as a counter, a formatter or a helper method. Putting that code in a shared parent forces an inheritance relationship that may not make sense. A trait solves this: it groups methods and properties that a class can copy in with the use keyword. This is called horizontal composition, because the behavior is added without a parent-child relationship.

A trait looks like a class but is declared with trait. You cannot instantiate a trait on its own. It only becomes useful when a class uses it.

A first worked example

The trait below holds a private counter and a method that increments it. Two unrelated classes use it. Each object gets its own copy of the property, so the counters do not interfere.

Example
<?php
trait Counts {
    private int $count = 0;

    public function increment(): int {
        $this->count++;
        return $this->count;
    }
}

class Clicks {
    use Counts;
}

class Downloads {
    use Counts;
}

$clicks = new Clicks();
$downloads = new Downloads();
$clicks->increment();
echo json_encode([$clicks->increment(), $downloads->increment()]);
echo "\n";
Output
[2,1]

The Clicks object was incremented twice and the Downloads object once, so the result is [2,1]. Inside a trait method, $this refers to the object of the class that uses the trait.

Precedence

When the same method name exists in several places, PHP picks one by a fixed order: a method written in the current class wins over a trait method, and a trait method wins over an inherited method.

Example
<?php
class Base {
    public function who(): string {
        return 'base';
    }
}

trait Marks {
    public function who(): string {
        return 'trait';
    }
}

class FromTrait extends Base {
    use Marks;
}

class FromClass extends Base {
    use Marks;

    public function who(): string {
        return 'class';
    }
}

echo json_encode([(new Base())->who(), (new FromTrait())->who(), (new FromClass())->who()]);
echo "\n";
Output
["base","trait","class"]

The three results are base, trait and class. This lets a class customize a trait method by simply defining its own version.

Using several traits and resolving conflicts

A class can list several traits after use, separated by commas. If two of those traits provide a method with the same name, PHP raises a fatal error unless you resolve the conflict. Use insteadof to choose which trait wins, and as to give another version an additional name. The as operator adds an alias; it does not rename or remove anything.

Example
<?php
trait A {
    public function talk(): string {
        return 'a';
    }
}

trait B {
    public function talk(): string {
        return 'b';
    }
}

class Talker {
    use A, B {
        B::talk insteadof A;
        A::talk as talkA;
    }
}

$t = new Talker();
echo json_encode([$t->talk(), $t->talkA()]);
echo "\n";
Output
["b","a"]

Here talk() comes from B, while the excluded A version is still reachable as talkA().

Other useful facts

  • A trait can declare properties. If the using class declares the same property, it must be compatible (same visibility, type, readonly modifier and initial value), or PHP raises a fatal error.
  • A trait can use other traits.
  • A trait can declare an abstract method to require that the using class provides it.
  • A static variable inside a trait method is separate for each class that uses the trait.
  • Call static members on the class that uses the trait, not on the trait itself; direct access on a trait is deprecated.

Common mistakes

  • Trying new on a trait. Traits are not instantiable.
  • Using two traits with the same method name and forgetting insteadof.
  • Using traits where a clear interface or a parent class would express the design better. Traits share code; they do not create a type you can check against.

Exercise contract

Write the function announceAll(array $messages): array. The starter already defines a trait Shouts with a shout() method and an empty Announcer class. Make Announcer use the trait, then return a list where each message has been passed through shout(), which trims the text, uppercases it and appends !. An empty input array returns an empty array, and an empty string becomes !.

Recap

A trait is a bundle of methods and properties copied into a class with use. The class wins over the trait, and the trait wins over the parent. Resolve duplicate names with insteadof and as.

Before you start

Classes

You’ll practice
  • Define a reusable trait method
  • Compose it into a class without inheritance
Your task

Make announceAll() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.