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

Asymmetric Visibility (PHP 8.4)

php PHP 8.4+ Intermediate
debt(d5/e3/b3/t5)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list rector and phpstan as the tools, both specialist static-analysis tools rather than the default compiler or linter. The common mistakes (confusing with readonly, using static properties) are caught by phpstan type-checking or rector rules, not by a default linter or compile-time error.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is 'Use public private(set) or protected(set)' — essentially replacing a private property + public getter pair with an asymmetric visibility declaration. This is a small, localised refactor within one class/component, not a single-line swap but not cross-cutting either.

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

Closest to 'localised tax' (b3). Asymmetric visibility applies within the declaring class and its subclasses. It replaces a well-understood pattern (private property + public getter), so the structural burden is localised. The rest of the codebase is largely unaffected unless the property is inherited widely.

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

Closest to 'notable trap' (t5). The canonical misconception is conflating asymmetric visibility with readonly — the misconception field states exactly this. A competent PHP developer familiar with readonly (introduced in 8.1) will likely assume the two are equivalent, but asymmetric visibility allows internal mutation after construction. This is a documented gotcha (t5) but not so severe as to contradict an entire paradigm.

About DEBT scoring →

Also Known As

asymmetric visibility private(set) public get private set PHP 8.4

TL;DR

PHP 8.4 allows separate read and write visibility on properties — public(get) private(set) means anyone can read but only the class can write.

Explanation

Asymmetric visibility uses the syntax: public private(set) string $name — public read access, private write access. Shorthand: public private(set) (long form) or public(get) private(set). Available modifiers: public, protected, private for both get and set independently. This replaces the common pattern of private property + public getter + no setter (enforced readonly) or private property + public getter + private setter. Works naturally with constructor promotion.

Common Misconception

Asymmetric visibility is the same as readonly — readonly prevents all modification after construction; asymmetric visibility allows the owning class to mutate the property while keeping reads public.

Why It Matters

Asymmetric visibility eliminates the boilerplate of private property + public getter — the most common OOP pattern in PHP — while keeping the property mutable internally.

Common Mistakes

  • Confusing with readonly — readonly cannot be written after construction; asymmetric visibility allows internal mutation.
  • Not knowing that set visibility must be equal or more restrictive than get visibility.
  • Using the long form public(get) private(set) when public private(set) is the shorthand.
  • Asymmetric visibility on static properties — not supported in PHP 8.4.

Code Examples

✗ Vulnerable
// PHP 8.3 boilerplate — private + getter:
class User {
    private string $name;
    private int $loginCount = 0;

    public function __construct(string $name) {
        $this->name = $name;
    }

    public function getName(): string { return $this->name; }
    public function getLoginCount(): int { return $this->loginCount; }

    public function recordLogin(): void { $this->loginCount++; }
}
✓ Fixed
// PHP 8.4 asymmetric visibility:
class User {
    public private(set) int $loginCount = 0;

    public function __construct(
        public private(set) string $name,
    ) {}

    public function recordLogin(): void {
        $this->loginCount++;
    }
}

$user = new User('Alice');
echo $user->name;
echo $user->loginCount;
$user->name = 'Bob'; // Error

Added 16 Mar 2026
Edited 16 Apr 2026
Views 58
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 6 pings F 2 pings S 1 ping S 6 pings M 1 ping T 0 pings W 2 pings T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping 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
ChatGPT 27 Amazonbot 6 Perplexity 5 Google 4 Unknown AI 3 Ahrefs 1
crawler 42 crawler_json 3 pre-tracking 1
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Low
⚡ Quick Fix
Use public private(set) or protected(set) to expose read access while restricting writes — replaces getter-only patterns
📦 Applies To
PHP 8.4+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Private property with only a public getter and no setter — candidate for asymmetric visibility
Auto-detectable: ✓ Yes rector phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: Medium ✗ Manual fix Fix: Medium Context: Class

✓ schema.org compliant