Constructor Promotion + readonly Together
debt(d1/e1/b2/t5)
Closest to 'caught instantly' (d1), attempting to modify a readonly property outside the constructor produces an immediate Error at runtime, and phpstan/rector (per detection_hints) flag misuse statically.
Closest to 'one-line patch' (e1), per quick_fix: simply combine `public readonly Type` in constructor parameters — a single signature change per property.
Closest to 'minimal commitment' (b1), slightly higher because choosing readonly value objects propagates immutability expectations to callers (no setters, must clone-with), but applies_to is localised to the DTO/value-object classes themselves.
Closest to 'notable trap' (t5), grounded in misconception: developers expect clone to allow modification of readonly props, but pre-8.3 it cannot, and post-8.3 only inside __clone() — a documented gotcha most devs eventually learn.
TL;DR
Explanation
Constructor promotion (PHP 8.0) and readonly (PHP 8.1) combine elegantly. public readonly string $name in the constructor parameter list declares, promotes, and marks the property immutable in one line. PHP 8.2 adds readonly classes — mark the entire class and all promoted properties become readonly without individual annotation. Readonly properties can only be initialised once (in the constructor) and cannot be modified thereafter — attempting to set them throws an Error. Cloning: PHP 8.3 allows readonly property modification in __clone().
Common Misconception
Why It Matters
Common Mistakes
- Trying to set a readonly property outside the constructor.
- Not using readonly classes (PHP 8.2) when all properties should be immutable.
- Confusing readonly (write-once) with const (compile-time constant).
Code Examples
class Money {
public readonly int $amount;
public readonly string $currency;
public function __construct(int $amount, string $currency) {
$this->amount = $amount;
$this->currency = $currency;
}
}
// PHP 8.1+ — combined promotion + readonly:
class Money {
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {}
}
// PHP 8.2 — readonly class:
readonly class Money {
public function __construct(
public int $amount,
public string $currency,
) {}
}