Asymmetric Visibility — public private(set)
PHP 8.4 lets you set different visibility for reading and writing a property. The most common pattern: publicly readable, privately writable. No more writing getters just to expose a value you don't want externally modified.
class BankAccount {
public function __construct(
public readonly string $holder,
public private(set) float $balance = 0, // Read: public. Write: private.
) {}
public function deposit(float $amount): void {
if ($amount <= 0) throw new \InvalidArgumentException('Amount must be positive');
$this->balance += $amount; // Private write — OK
}
}
$account = new BankAccount('Alice', 100);
echo $account->balance; // 100.0 — public read
$account->balance = 0; // Error! — private write
$account->deposit(50); // OK — internal write
echo $account->balance; // 150.0
// Works with protected too
class Entity {
public protected(set) int $id; // Subclasses can write, outside can only read
public private(set) string $createdAt; // Only this class can write
}
Asymmetric visibility combines beautifully with property hooks and constructor promotion. Together, they give PHP one of the most expressive property systems in any language — read visibility, write visibility, get logic, set logic, and immutability, all declared inline.
Significance: Encapsulation
The getter-for-a-readable-property pattern was PHP's most common boilerplate. Asymmetric visibility eliminates it entirely:
public private(set) says "anyone can read, only I can write" — exactly the intent behind most getter methods — in a single declaration.