---
title: Traits
source: 'https://academia.sh/en/courses/php-object-model/traits'
course: 'Object-Oriented PHP and Standards'
language: en
updated: '2026-10-21T05:03:35+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Defines a trait and mixes it into a class with use'
    - 'Resolves the conflict with insteadof and as when two traits bring a method of the same name'
    - 'Measures that adding a conflicting method to a used trait stops the consumer'
    - 'Shows that the same conflict does not reach a class that has its own method'
---

# Traits

Sharing methods between classes with a trait, resolving a conflict with insteadof and as, and the consumer stopping when a method of the same name is added to a trait it uses.

An abstract class with a body provided sharing, but left a class only one inheritance right. A trait makes this sharing horizontal: a set of methods is written once and mixed into many classes that are not related to each other. The question is what the using class experiences when a method is added to a trait it uses.

## Traits and use

A trait is not a class and cannot be instantiated. Its methods are taken into a class with `use` and work as if they were written in the class. A class can use as many traits as it likes; this overcomes the single parent class limit of inheritance.

```php
<?php
// Logged.php — a trait that carries the record-keeping behavior
trait Logged
{
    public function summary(): string
    {
        return 'record';
    }
}
```

```php
<?php
// Timed.php — a second trait that carries time information
trait Timed
{
    public function time(): string
    {
        return '12:00';
    }
}
```

```php
<?php
// Document.php — a class that uses two traits together
require_once 'Logged.php';
require_once 'Timed.php';

class Document
{
    use Logged, Timed;
}
```

```php
<?php
// document_usage.php — trait methods are called like the class's own methods
require 'Document.php';

$document = new Document();
echo $document->summary(), ' ', $document->time(), "\n";
var_dump($document instanceof Logged);
```

```
record 12:00
bool(false)
```

**OM13.** `Document` wrote neither `summary()` nor `time()`, and we could call both. `instanceof Logged` gave `false`, though: a trait is not a type, it is code copied into a class. For this reason a trait cannot be relied on as a parameter type; if a type is needed, go back to the path in the Interfaces lesson.

## Conflict Resolution

If two traits bring a method under the same name, the class has to declare which one it will use. `insteadof` decides the one selected, and `as` gives the method left out another name.

```php
<?php
// conflict.php — two traits bring a method of the same name and the conflict is resolved
trait Logged
{
    public function summary(): string { return 'record'; }
}

trait Timed
{
    public function time(): string { return '12:00'; }
    public function summary(): string { return 'time'; }
}

class Document
{
    use Logged, Timed {
        Logged::summary insteadof Timed;
        Timed::summary as timedSummary;
    }
}

$document = new Document();
echo $document->summary(), ' ', $document->timedSummary(), ' ', $document->time(), "\n";
```

```
record time 12:00
```

**OM14.** The name `summary()` went to `Logged`, and `Timed`'s method stayed as `timedSummary()`. The resolution block is inside the class that uses the traits: resolving the conflict is the job of the consumer, not of the trait. The measurement below shows the cost of this sentence.

## State and Abstract Methods in a Trait

A trait carries not only methods but properties too. In a method inside a trait, `$this` is the object of the class that uses the trait; that is, a trait does not work on its own, it gains meaning only when it enters a class. If a trait expects something from its user, it declares it with an abstract method (`abstract`): the using class must write that method. The property of each using object belongs to itself, because the property is copied into the class.

```php
<?php
// Tallied.php — a trait that carries a property and an abstract method
trait Tallied
{
    private int $tally = 0;

    abstract public function name(): string;

    public function increment(): static
    {
        $this->tally++;
        return $this;
    }

    public function status(): string
    {
        return $this->name() . '=' . $this->tally;
    }
}
```

```php
<?php
// Visit.php — a class that writes the abstract method the trait expects
require_once 'Tallied.php';

class Visit
{
    use Tallied;

    public function name(): string
    {
        return 'visit';
    }
}
```

```php
<?php
// visit_usage.php — two objects carry separate states of the same trait
require 'Visit.php';

$first = new Visit();
$second = new Visit();
$first->increment()->increment();

echo $first->status(), ' ', $second->status(), "\n";
```

```
visit=2 visit=0
```

`status()` was written in the trait but called the method the class wrote through `$this->name()`; `tally` stayed separate for each object. A class that does not write the abstract method cannot use the trait.

```bash
# abstract_missing.sh — if the trait's abstract method is not written, the class cannot be declared
cat > Missing.php <<'PHP'
<?php
trait Tallied { abstract public function name(): string; }
class Missing { use Tallied; }
PHP
php -d display_errors=0 Missing.php; echo "exit=$?"
```

```
exit=255
```

## Visibility and Alias

The `as` keyword is not only for resolving a conflict. Used alone, it changes the visibility of a trait method in the using class; when a name is added, it gives the same method a second name and visibility. That way the trait method's original name and original visibility can be closed to the outside of the class while the new name is left open.

```php
<?php
// visibility.php — as changes the visibility and adds an alias
trait Logged
{
    public function summary(): string
    {
        return 'record';
    }
}

class Hidden
{
    use Logged {
        summary as protected;
        summary as public recordSummary;
    }
}

$hidden = new Hidden();
echo $hidden->recordSummary(), "\n";

try {
    $hidden->summary();
} catch (Error $e) {
    echo get_class($e), "\n";
}
```

```
record
Error
```

The same method stood under two names: `recordSummary()` was open, while `summary()` raised `Error` when called from outside because it had been made protected.

## Precedence and the Difference from Inheritance

If a method of the same name comes from more than one place, the order is this: the class's own method, the trait method, the parent class's method. For this reason a trait can override the parent class's method, but it cannot override what the class itself wrote.

```php
<?php
// precedence.php — the winner when the class, a trait and the parent class carry the same name
class Base
{
    public function greet(): string { return 'base'; }
}

trait Polite
{
    public function greet(): string { return 'trait'; }
}

class Child extends Base
{
    use Polite;
}

class Custom extends Base
{
    use Polite;

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

echo (new Child())->greet(), ' ', (new Custom())->greet(), "\n";
```

```
trait class
```

`Child` did not write its own `greet()` method and the trait won; `Custom` did and the class won. The parent class stayed last in both cases.

A trait does not replace inheritance, because there are three differences between them. First, a trait is not a type: it cannot be asked about with `instanceof` and cannot be a parameter type. Second, the methods are copied into the using class, so the static property of two classes that use the same trait is a separate copy. Third, a trait has no parent class; it builds no related hierarchy, it only carries a body.

```php
<?php
// static_trait.php — a trait's static property is a separate copy per using class
trait Counter
{
    public static int $count = 0;

    public static function increment(): int
    {
        return ++self::$count;
    }
}

class First { use Counter; }
class Second { use Counter; }

First::increment();
First::increment();
echo First::$count, ' ', Second::$count, "\n";
```

```
2 0
```

`First` incremented twice and `Second` stayed at zero: if shared state is wanted, what is needed is not a trait but a class's static property or a parent class. Inheritance builds the "this is a thing" relationship, a trait builds the "this also carries that behavior" relationship; using inheritance to remove code repetition builds the relationship wrongly and grows the fragile base class problem from the Inheritance and Abstract Classes lesson.

## Using a Trait and an Interface Together

Because a trait is not a type, if the shared body is also to appear as a type, an interface and a trait are used together: the interface gives the contract and the type, the trait gives the body. The class declares the interface with `implements` and takes the body of the method with `use`.

```php
<?php
// together.php — the interface gives the type, the trait provides the body
interface Labeled
{
    public function label(): string;
}

trait LabelBody
{
    public function label(): string
    {
        return 'label';
    }
}

class Post implements Labeled
{
    use LabelBody;
}

$post = new Post();
var_dump($post instanceof Labeled);
echo $post->label(), "\n";
```

```
bool(true)
label
```

`Post` is of the type `Labeled` and has not written the body again. This combination brings the contract in the Interfaces lesson together with the sharing in this lesson; but the risk that a method added to a trait later stops its user stays exactly the same.

## The Measurement Core

This lesson's measurement also uses the file `reach.php`; the core was explained line by line in Classes and Objects.

```php
<?php
// reach.php — helpers that measure where a change reaches

function write_files(string $dir, array $files): void
{
    foreach ($files as $name => $content) {
        if ($content === null) {
            continue; // null: the file was deleted
        }
        $path = "$dir/$name";
        if (!is_dir(dirname($path))) {
            mkdir(dirname($path), 0777, true);
        }
        file_put_contents($path, $content);
    }
}

function read_files(string $dir): array
{
    $files = [];
    $walker = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
    );
    foreach ($walker as $file) {
        $name = substr($file->getPathname(), strlen($dir) + 1);
        $files[$name] = file_get_contents($file->getPathname());
    }
    return $files;
}

function run(string $dir): array
{
    $command = 'cd ' . escapeshellarg($dir) . ' && php -d display_errors=0 main.php 2>/dev/null';
    exec($command, $lines, $code);
    return [$code, implode("\n", $lines)];
}

function measure(string $name, array $base, array $change, array $seams = []): string
{
    if (array_key_exists('main.php', $change)) {
        throw new LogicException("$name: the consumer is not edited");
    }
    write_files("measure/$name/before", $base);
    write_files("measure/$name/after", array_merge($base, $change));
    [$codeBefore, $outputBefore] = run("measure/$name/before");
    [$codeAfter, $outputAfter] = run("measure/$name/after");
    if ($codeBefore !== 0) {
        throw new LogicException("$name: the baseline did not run");
    }
    $reach = match (true) {
        $codeAfter !== 0 => 'breaking',
        $outputBefore !== $outputAfter => 'implicit',
        array_intersect(array_keys($change), $seams) !== [] => 'at-seam',
        default => 'in-place',
    };
    printf(
        "%-18s files=%d  exit=%-3d output %-7s -> %s\n",
        $name,
        count($change),
        $codeAfter,
        $outputBefore === $outputAfter ? 'same' : 'changed',
        $reach
    );
    return $reach;
}
```

## A Method Is Added to the Trait

The change measured is adding a `summary()` method with the same name as `Logged`'s to the `Timed` trait. The using class `Document` applies both traits with `use` and prints the values of `summary()` and `time()`; `Document` and `main.php` are not edited, and the only file edited is the trait file.

```php
<?php
// trait.php — a method of the same name is added to a used trait
require_once 'reach.php';

measure('trait-conflict', [
    'traits.php' => <<<'PHP'
        <?php
        trait Logged
        {
            public function summary(): string { return 'record'; }
        }
        trait Timed
        {
            public function time(): string { return '12:00'; }
        }
        PHP,
    'main.php' => <<<'PHP'
        <?php
        require 'traits.php';
        class Document
        {
            use Logged, Timed;
        }
        $document = new Document();
        echo $document->summary(), ' ', $document->time(), "\n";
        PHP,
], [
    'traits.php' => <<<'PHP'
        <?php
        trait Logged
        {
            public function summary(): string { return 'record'; }
        }
        trait Timed
        {
            public function time(): string { return '12:00'; }
            public function summary(): string { return 'time'; }
        }
        PHP,
]);

foreach (['before', 'after'] as $stage) {
    [$code, $output] = run("measure/trait-conflict/$stage");
    echo $stage, ' exit=', $code, ' output=', $output, "\n";
}
```

```
trait-conflict     files=1  exit=255 output changed -> breaking
before exit=0 output=record 12:00
after exit=255 output=
```

**OM15.** Not a single line changed in the consumer file and the exit code became 255. The conflict is born on the `use` line: when two traits bring a method under the same name, the using class cannot be declared. This is the cost of the conflict resolution belonging to the consumer; the trait's author cannot see who `use`s it while adding a new method. The reach of this lesson is **breaking**.

## The Bounding Measurement

Does every addition break? If the using class already carries its own `summary()` method, the conflict between the traits' methods of the same name does not reach the class: the class's own method overrides all of the trait methods.

```php
<?php
// the_class_itself.php — the class writes summary() itself, the same addition is made to the trait
require_once 'reach.php';

measure('the-class-itself', [
    'traits.php' => <<<'PHP'
        <?php
        trait Logged
        {
            public function summary(): string { return 'record'; }
        }
        trait Timed
        {
            public function time(): string { return '12:00'; }
        }
        PHP,
    'main.php' => <<<'PHP'
        <?php
        require 'traits.php';
        class Document
        {
            use Logged, Timed;
            public function summary(): string { return 'document'; }
        }
        $document = new Document();
        echo $document->summary(), ' ', $document->time(), "\n";
        PHP,
], [
    'traits.php' => <<<'PHP'
        <?php
        trait Logged
        {
            public function summary(): string { return 'record'; }
        }
        trait Timed
        {
            public function time(): string { return '12:00'; }
            public function summary(): string { return 'time'; }
        }
        PHP,
]);

foreach (['before', 'after'] as $stage) {
    [$code, $output] = run("measure/the-class-itself/$stage");
    echo $stage, ' exit=', $code, ' output=', $output, "\n";
}
```

```
the-class-itself   files=1  exit=0   output same    -> in-place
before exit=0 output=document 12:00
after exit=0 output=document 12:00
```

**OM16.** The same addition, the same trait file; this time the class stayed in place and the output continued as `document 12:00`. The break was born from the name `summary()` reaching the class from two traits at once; the class's own definition cut off that reaching. So whether an addition to a trait will break lies not in the trait but in whether the user has already claimed the name `summary()`.

## Summary

- A trait mixes methods into a class with `use`, a class can use several traits, and a trait is not a type.
- If two traits bring the same name, the using class chooses with `insteadof` and gives the second method a new name with `as`.
- When a conflicting `summary()` was added to the used trait, the consumer file stopped without changing at all, exit 255; the reach of this lesson is **breaking**.
- If the using class writes the `summary()` method itself, the same addition stayed in place.
- The conflict resolution belongs to the consumer, so the trait's author does not see the classes while adding a new method.

## Next Step

Traits shared methods, but state and constants are another topic. The Static Members and Constants lesson asks about members bound to the class: why does moving the way of creating an object to a static factory method and adding a parameter to the constructor not stop the consumer?
