Built-in PHP Attributes
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). The core misuse — omitting #[Override] on intended override methods — is invisible until a typo silently creates a new method instead of overriding. PHPStan and Rector (listed in detection_hints.tools) can catch some cases, but only if the developer knows to configure them for this; it won't surface as a compiler error in missing-attribute scenarios. Slightly better than d9 because PHPStan can flag absent #[Override] with proper rules.
Closest to 'simple parameterised fix' (e3). The quick_fix describes adding #[Override], #[Deprecated], or #[SensitiveParameter] to method/parameter declarations. While adding a single attribute is near e1, the common_mistakes indicate the fix often spans multiple methods/parameters across the codebase (e.g., auditing all override methods or all password parameters), making it slightly more than a one-line patch.
Closest to 'localised tax' (b3). The applies_to scope covers web, cli, and queue-worker contexts in PHP 8+, but the burden is mostly felt at the point of declaration. Missing attributes don't impose a structural tax on unrelated code — the debt is localized to the specific classes/methods that omit them. Not reaching b5 because each omission is independently fixable.
Closest to 'serious trap' (t7). The misconception field directly states the trap: developers believe #[Override] is purely informational, when it is actually engine-enforced and will cause a fatal error if the method does not override a parent. This contradicts the common mental model of PHP attributes as decorators/metadata (similar to docblock annotations). The #[SensitiveParameter] omission trap (passwords leaking in stack traces) is a separate silent danger reinforcing t7.
Also Known As
TL;DR
Explanation
PHP 8.0 introduced attributes as a first-class language feature. Several built-in attributes ship with PHP itself. #[Deprecated] (PHP 8.4) marks a function, method, or class as deprecated with an optional message — triggers E_DEPRECATED without a manual trigger_error() call. #[Override] (PHP 8.3) asserts that a method overrides a parent — a fatal error is thrown if the parent method doesn't exist, catching rename bugs at startup. #[AllowDynamicProperties] (PHP 8.2) opts a class back into dynamic property creation (disabled by default in 8.2). #[SensitiveParameter] (PHP 8.2) redacts the marked parameter from stack traces — critical for passwords and API keys. Use #[Attribute] to define custom attributes consumed by frameworks or static analysis.
Common Misconception
Why It Matters
Common Mistakes
- Not using #[Override] on methods intended to override a parent — typos in the method name create a new method silently.
- Not using #[SensitiveParameter] on password/token parameters — they appear in exception stack traces unredacted.
- Applying #[Deprecated] without a message — the deprecation notice is useless without migration guidance.
- Not knowing which attributes are engine-enforced vs. framework-specific — engine attributes require no library.
Code Examples
// Missing #[Override] — typo creates a new method, parent not overridden:
class UserRepository extends BaseRepository {
public function findByid(int $id): User {} // Typo: 'findByid' not 'findById'
// #[Override] would make PHP throw an error here
}
// Missing #[SensitiveParameter]:
function login(string $user, string $password): void {} // $password in stack traces
class UserService {
#[SensitiveParameter]
public function login(string $password): void {}
#[Override]
public function __toString(): string { return 'UserService'; }
}