← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Readonly Properties (PHP 8.1)

php PHP 8.1+ Intermediate
debt(d2/e1/b3/t5)
d2 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'caught instantly' (d1) but +1 because misuse (writing after init) throws a runtime Error rather than compile error; phpstan/psalm/rector flag readonly violations statically.

e1 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch' (e1) — quick_fix is literally adding the readonly keyword to a property declaration.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3) — readonly applies per-property/per-class (value objects, DTOs); it shapes those classes but doesn't impose system-wide weight. Wither-method workaround adds some local friction.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5) — misconception field states devs assume it's like a private setter, but it forbids reassignment even inside the class; also the cloning/wither gotcha is a documented surprise most devs learn.

About DEBT scoring →

Also Known As

PHP readonly readonly property PHP 8.1 readonly

TL;DR

Properties declared with readonly can only be initialised once and then never modified, enforcing immutability at the language level.

Explanation

Readonly properties (PHP 8.1) must be typed and can only be written once — either in the constructor or at the point of declaration. Any subsequent write throws an Error. This is ideal for value objects and DTOs where properties should never change after construction. PHP 8.2 added readonly classes, where all promoted and explicitly declared properties become readonly automatically. Readonly properties cannot have a default value in their declaration (only in the constructor).

Common Misconception

Readonly properties are just properties with private setters. Readonly properties can only be initialised once — attempting to assign after initialisation throws an Error even from within the class itself. This guarantees true immutability, not just access control.

Why It Matters

PHP 8.1 readonly properties can only be initialised once — they enforce immutability at the language level, making value objects and DTOs safe against accidental mutation.

Common Mistakes

  • Trying to reassign a readonly property even in the same class — any write after initialisation throws an Error.
  • Not using constructor promotion with readonly — the combination is the primary intended usage.
  • Using readonly on properties that need cloning with modifications — use a wither method returning a new instance.
  • Declaring readonly on static properties — not supported; readonly applies to instance properties only.

Code Examples

✗ Vulnerable
// Mutable value object — can be corrupted after construction:
class Money {
    public float $amount;
    public string $currency;
}
$price = new Money();
$price->amount = 9.99;
$price->amount = -999; // Nothing prevents this mutation

// Readonly — immutable by language enforcement:
class Money {
    public function __construct(
        public readonly float $amount,
        public readonly string $currency,
    ) {}
}
✓ Fixed
// PHP 8.1 — individual readonly properties
class User {
    public function __construct(
        public readonly int    $id,
        public readonly string $email,
    ) {}
    // $user->id = 99; → Error: Cannot modify readonly property
}

// PHP 8.2 — readonly class (all promoted properties are readonly)
readonly class Money {
    public function __construct(
        public int    $amount,
        public string $currency,
    ) {}

    // Methods can still return new instances
    public function add(self $other): self {
        return new self($this->amount + $other->amount, $this->currency);
    }
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 40
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 2 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 0 pings F 2 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 3 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Perplexity 10 Amazonbot 9 Ahrefs 6 ChatGPT 3 Unknown AI 2 Google 1
crawler 30 crawler_json 1
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Low
⚡ Quick Fix
Add readonly to any property that should only be set once (in the constructor) — PHP enforces this at runtime
📦 Applies To
PHP 8.1+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Property assigned only in constructor with no setter — candidate for readonly
Auto-detectable: ✓ Yes rector phpstan psalm
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: Medium ✓ Auto-fixable Fix: Low Context: Class

✓ schema.org compliant