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?
answer
- class beats trait beats parent
- trait method overrides inherited one
- parent:: resolves in the host class
- inserted methods obey override rules
- 8.5: traits bound before the parent
basics
~10 sThe 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.
solid answer
~50 sPHP resolves same-named methods in a fixed order: **members declared in the class** override **trait methods**, which in turn override **inherited methods**. So if `Article extends Entity` and uses `Timestamps`, and all three define `touch()`, `Article::touch()` runs; remove it and the trait's `touch()` runs; remove that too and `Entity::touch()` runs. A trait method is composed into the class, so `parent::touch()` inside it calls the **host class's parent**, here `Entity::touch()`, which is how a trait can extend inherited behaviour. The inserted method is checked like any override: it must be signature-compatible with the parent's method and satisfy any interface it implements. Since **PHP 8.5** traits are bound before the parent class, so a trait **property or constant** with the same name as an inherited one now takes precedence instead of raising an incompatible-composition error; method resolution did not change.
code
php · 36 lines<?php
declare(strict_types=1);
class Entity
{
public function touch(): string
{
return 'entity';
}
}
trait Timestamps
{
public function touch(): string
{
return parent::touch() . '+timestamps';
}
}
class Article extends Entity
{
use Timestamps;
}
class Page extends Entity
{
use Timestamps;
public function touch(): string
{
return 'page';
}
}
echo (new Article())->touch(), "\n"; // entity+timestamps
echo (new Page())->touch(), "\n"; // pagego deeper
Recall the order: the class's own method, then the trait's, then the parent's.
Explain that parent:: inside a trait resolves in the host class, that inserted methods are checked as overrides, and what PHP 8.5's binding change affects.
Diagnose surprises where a trait silently shadows an inherited method, and decide when a trait's parent:: dependency is acceptable across its hosts.
Discuss conventions that keep trait precedence predictable across a codebase, such as naming, aliasing and limiting parent:: in traits.
## The precedence rule When the same method name comes from more than one place, PHP picks by a fixed order that the manual states directly: 1. A method **declared in the class itself** wins. 2. Otherwise, a method **inserted by a trait** is used. 3. Otherwise, the method **inherited from the parent** class is used. | `Article` declares `touch()` | `Timestamps` trait declares it | `Entity` parent declares it | Which runs | |---|---|---|---| | yes | yes | yes | `Article::touch()` | | no | yes | yes | the trait's `touch()` | | no | no | yes | `Entity::touch()` | The first rule means a class can always replace a trait method by writing its own, without any `insteadof` clause. Internally, when the engine composes a trait method and finds a method already declared in the class, it keeps the class's method and skips the trait's. ## parent:: inside a trait method Because trait methods become methods of the using class, `parent::` inside them refers to the **using class's parent**, not to anything about the trait. This allows a trait to **decorate** inherited behaviour: - `Entity::touch()` updates an in-memory `updatedAt`. - `Timestamps::touch()` calls `parent::touch()` and then also records who made the change. - `Article` extends `Entity`, uses `Timestamps`, and gets both steps. The same trait used in a class **without** a parent would fail when `parent::touch()` runs, because there is no parent. A trait that calls `parent::` therefore quietly assumes something about every host class, which is worth documenting or avoiding. ## Inserted methods are ordinary overrides Once composed, a trait method is checked exactly like a method written in the class: - If the parent already has `touch()`, the trait's version must be **signature-compatible** with it, or the class fails with `Declaration of ... must be compatible with ...`. - If the class implements an interface that declares the method, the trait method must satisfy it. A `protected` trait method cannot implement a `public` interface method: `Access level to Foo::bad() must be public (as in class Baz)`. - Since **PHP 8.3**, `use Timestamps { touch as final; }` marks the inserted method `final`, so subclasses of the host cannot override it, while the host itself still could have declared its own. ## What changed in PHP 8.5 Before PHP 8.5, traits were bound **after** the parent class. For **methods** the visible result is the same either way. For **properties and constants** it was not: a trait declaring `public $status = 'draft'` in a class whose parent already declared `public $status = 'new'` produced a fatal incompatible-composition error. Since PHP 8.5 traits are bound **before** the parent, matching their copy-and-paste semantics, so the trait's definition takes precedence over the inherited one, as if written in the class. ## Properties and constants follow a different rule The class-beats-trait rule is about **methods**. For properties and constants there is no silent winner between the class and its trait: - If the class itself declares a property or constant that the trait also declares, the two definitions must be **compatible** (same visibility, type, readonly modifier and initial value for properties; visibility, value and finality for constants), or the class fails to compose. - Between a trait and the **parent**, PHP 8.5 lets the trait's definition take precedence, as described above. So a host cannot use its own declaration to override a trait property's default; it has to set a different value in its constructor instead. ## Practical guidance - Keep trait method names specific (`touchTimestamps()` rather than `save()`) to avoid silently shadowing a parent's method. - If the host needs to extend a trait method, alias the trait's version and call it from the host's own method; conflict tools cover that. - Treat `parent::` inside traits as a smell unless every intended host shares the same parent.
- Page wants the trait's touch() and then some extra work of its own. How can it call the trait version?Import the trait method under an alias and call it: `use Timestamps { touch as traitTouch; }`, then `public function touch(): string { return $this->traitTouch() . '+page'; }`. The alias keeps the trait's body reachable while the class's own `touch()` takes precedence.
- A trait declares public $status = 'draft' and the host's parent declares public $status = 'new'. What changed in PHP 8.5?Before PHP 8.5 traits were bound after the parent, and this composition was reported as a fatal incompatible definition. Since 8.5 traits are bound first, so the trait's declaration takes precedence as if it were written in the class, and objects of the host class start with `'draft'`.
saying these in an interview costs you the question
- A trait method always wins over a method declared in the class
- parent:: inside a trait method refers to the trait itself
- Inherited methods beat trait methods because the parent is bound first
- Trait methods skip signature compatibility checks against the parent
- PHP 8.5 changed which method runs when trait and parent collide