skip to content

Traits

A trait copies methods and properties into a class through use, and conflicts are resolved with insteadof and as. Interviewers ask about precedence and when a trait beats inheritance or composition.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In PHP, what does use SoftDeletes; inside a class body do, and how is a trait different from a parent class?

level: juniorimportance: must knowfreq 68%

answer

  1. compile-time copy into the class
  2. many traits, one parent
  3. not a type: instanceof is false
  4. $this and __CLASS__ mean the host
  5. Cannot instantiate trait

basics

~20 s

Inside a class body, use SoftDeletes; copies the trait's methods, properties and constants into that class as if written there. Unlike a parent class, a trait is not a type, cannot be instantiated, and a class may use many.

solid answer

~50 s

`use SoftDeletes;` written **inside a class body** tells PHP to compose the trait's members into the class: its methods, properties and, since PHP 8.2, constants behave as if they were declared in the class itself. Inside those methods `$this`, `self` and `__CLASS__` refer to the using class. A class can list several traits (`use Timestamps, SoftDeletes;`) while it still has one parent slot. A trait is **not a type**: `$article instanceof SoftDeletes` is false, a parameter typed with a trait name accepts nothing, and `new SoftDeletes()` throws `Error: Cannot instantiate trait SoftDeletes`. A parent class is a type and a place in the hierarchy; a trait is only reusable code. When callers need a type, pair the trait with an interface. This `use` is unrelated to the namespace import `use` at the top of a file.

code

php · 30 lines
php
<?php
declare(strict_types=1);

trait SoftDeletes
{
    private ?DateTimeImmutable $deletedAt = null;

    public function softDelete(): void
    {
        $this->deletedAt = new DateTimeImmutable();
    }

    public function isDeleted(): bool
    {
        return $this->deletedAt !== null;
    }
}

final class Article
{
    use SoftDeletes;

    public function __construct(public string $title) {}
}

$article = new Article('Launch notes');
$article->softDelete();
var_dump($article->isDeleted());             // bool(true)
var_dump($article instanceof SoftDeletes);   // bool(false): a trait is not a type
var_dump(class_uses($article));              // ['SoftDeletes' => 'SoftDeletes']

go deeper

for a junior

Recall that use inside a class body applies a trait, that its members act as the class's own, and that a class can use several traits but extend one class.

for a middle

Explain that a trait is not a type, what $this, self and CLASS mean inside it, and why an interface is paired with the trait when callers need a type.

for a senior

Show you can decide when shared code across unrelated hierarchies justifies a trait, and keep it honest by pairing it with an interface and small, explicit requirements.

for a principal

Set guidance for how widely traits are used in a codebase, weighing duplicated boilerplate against hidden coupling between traits and host classes.

## Two different use statements PHP spells two unrelated things with `use`: - `use App\Model\Article;` at the **top of a file** imports a name into the namespace scope. That is a namespace feature. - `use SoftDeletes;` **inside a class body** applies a **trait** to the class. That is the subject here. ## What applying a trait does A **trait** is declared like a class with the `trait` keyword and holds methods, properties, abstract method requirements, static members and, since **PHP 8.2**, constants. When a class uses it, the engine **composes** those members into the class when the class is declared. The manual describes the semantics as close to copy and paste: 1. The trait's methods become methods of the class. Inside them, `$this` is the host object, `self` is the host class, and `__CLASS__` is the host class name (`__TRAIT__` gives the trait's own name). 2. The trait's properties become properties of the class, including `private` ones, which the host class can then read like its own. 3. A class can use **several** traits in one statement, `use Timestamps, SoftDeletes;`, and traits can themselves use other traits. In a CMS, `Article`, `Page` and `MediaFile` may extend different base classes, yet all need `createdAt`/`updatedAt` handling and a `deletedAt` soft-delete flag. Two traits give each of them the same code without forcing a common parent. ## How a trait differs from a parent class | | parent class | trait | |---|---|---| | how many per class | one, via `extends` | any number, via `use` | | is it a type? | yes: `instanceof`, parameter types | **no** | | can be instantiated? | yes, unless abstract | never: `Cannot instantiate trait X` | | where members live | in the parent; the child inherits them | copied into each using class | | `parent::` inside its methods | the parent's own parent | the **using class's** parent | | listed by | `get_parent_class()` | `class_uses()`, which ignores traits used by parent classes | The row that matters most in interviews is the **type** row. Because a trait is not a type, code cannot ask "is this thing soft-deletable?" through the trait. The fix is to declare an interface as well: ```php interface SoftDeletable { public function softDelete(): void; } final class Article implements SoftDeletable { use SoftDeletes; } ``` Now a repository can accept `SoftDeletable $entity`, and the trait merely supplies the implementation. ## Why PHP has traits at all PHP classes have **single inheritance**. Without traits, sharing a helper across classes in different hierarchies means duplicating it, pushing it up into a shared base class that some classes cannot extend, or moving it into a separate object. Traits give a fourth option: reuse the **code** without touching the hierarchy. ## Things a trait does not give you - **Substitutability.** Nothing about a trait lets code treat all users of it alike; an interface does that. - **Encapsulation from the host.** The host class sees and can overwrite the trait's private properties, because after composition they are the host's own. - **One shared copy.** Each using class gets its own copy of the methods, and static state is per using class, not per trait. - **Runtime flexibility.** Composition happens when the class is declared; an object cannot gain or lose a trait later. ## Common interview traps - Answering that a trait is like an interface with method bodies: it is not a type at all. - Expecting `instanceof` or a parameter type to recognise the trait. - Confusing the two `use` statements. - Forgetting that `class_uses()` does not report traits used by parent classes.

  • A repository method must accept any soft-deletable entity. How do you type its parameter when the behaviour lives in a trait?
    Declare an interface such as `SoftDeletable` with `softDelete()` and `isDeleted()`, have each entity class `implement` it and `use` the trait for the bodies, and type the parameter as `SoftDeletable`. The trait supplies code; the interface supplies the type that `instanceof` and parameter checks understand.
  • Inside a trait method, what do __CLASS__ and self refer to?
    Both refer to the class that uses the trait, because the method is composed into that class. `__TRAIT__` is the way to get the trait's own name. So a trait used by `Article` and `Page` reports a different `__CLASS__` in each.

saying these in an interview costs you the question

  • A trait is an interface that can carry method bodies
  • $obj instanceof SomeTrait tells you whether the class uses the trait
  • A class can use only one trait, like extending one class
  • Methods inside a trait see the trait as self, not the host class
  • class_uses() also returns traits used by parent classes
open as a page

In PHP, when a class, a trait it uses and its parent class all define touch(), which runs, and what does parent::touch() inside the trait call?

level: middleimportance: must knowfreq 55%

basics

~10 s

The class's own touch() wins, a trait's touch() overrides the inherited one, and the parent's runs only if neither exists. Inside a trait method, parent::touch() calls the using class's parent.

open as a page

In PHP, what rules govern a trait's abstract methods, static members, properties and constants once the trait is used by several classes?

level: middleimportance: should knowfreq 32%

basics

~20 s

Abstract trait methods are requirements the host must implement compatibly. Static state exists once per using class, not per trait. A host may redeclare a trait property or constant only identically, and trait constants (8.2) and static members are used through the using class.

open as a page

In PHP, how do insteadof and as resolve two traits that define the same method, and what else can the as operator change?

level: middleimportance: should knowfreq 42%

basics

~20 s

Two traits defining the same concrete method make PHP fail until insteadof picks one. The as operator adds an alias so the excluded body stays callable, and can also change visibility or, since 8.3, make the method final.

open as a page

In a PHP CMS where Article, Page and MediaFile need timestamps and soft deletes, when is a trait the right tool, and how do you stop it becoming hidden coupling?

level: seniorimportance: should knowfreq 30%

basics

~20 s

A trait fits small, cohesive behaviour that several unrelated classes need verbatim, such as timestamp and soft-delete fields. Keep it honest by pairing it with an interface, declaring its needs as abstract methods, and leaving real dependencies to injected services.

open as a page