In PHP, what does use SoftDeletes; inside a class body do, and how is a trait different from a parent class?
answer
- compile-time copy into the class
- many traits, one parent
- not a type: instanceof is false
- $this and __CLASS__ mean the host
- Cannot instantiate trait
basics
~20 sInside 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
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
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.
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.
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.
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