Visibility
Visibility determines where code may access a Property or Method. It helps define a Class’s public API and keeps implementation details behind a boundary.
Access levels
Section titled “Access levels”| Visibility | External callers | Declaring Class | Child Classes |
|---|---|---|---|
public |
Yes | Yes | Yes |
protected |
No | Yes | Yes |
private |
No | Yes | No direct access |
The same access rules apply to Properties and Methods.
Private means Class scope
Section titled “Private means Class scope”Private access is based on the Class containing the code, not the identity of the receiving Object. A Wallet Method may read a private Property of another Wallet instance:
final class Wallet{ public function __construct(private int $balance) {}
public function hasMoreThan(Wallet $other): bool { return $this->balance > $other->balance; }}
$first = new Wallet(100);$second = new Wallet(50);var_dump($first->hasMoreThan($second)); // trueExternal code and an unrelated WalletService cannot access $balance
directly. They must use an appropriate public operation or query.
Protected and inheritance
Section titled “Protected and inheritance”Protected Members are accessible from the declaring Class and its Child Classes, but not directly from external callers:
class Wallet{ protected int $balance = 100;}
class PremiumWallet extends Wallet{ public function balance(): int { return $this->balance; }}
$wallet = new PremiumWallet();echo $wallet->balance(); // 100// echo $wallet->balance; // Error: protected PropertyInheritance will be covered separately. Its existence alone does not justify protected State: a Child may use operations that preserve the Parent’s rules instead of editing its data directly.
A small public API
Section titled “A small public API”Keep the balance private, expose withdrawal and reading as public operations, and keep internal validation private:
<?php
declare(strict_types=1);
final class Wallet{ public function __construct(private int $balance) { if ($balance < 0) { throw new InvalidArgumentException('Negative initial balance.'); } }
public function withdraw(int $amount): void { $this->ensureCanWithdraw($amount); $this->balance -= $amount; }
private function ensureCanWithdraw(int $amount): void { if ($amount <= 0) { throw new InvalidArgumentException('Amount must be positive.'); }
if ($amount > $this->balance) { throw new LogicException('Insufficient balance.'); } }
public function balance(): int { return $this->balance; }}The helper either returns normally or throws; returning true adds no value
when the caller ignores it. Its name describes both positive-amount and
available-balance checks. Amounts represent a single currency’s smallest unit.
Extracting a helper organizes implementation. Making it private hides that
detail from callers. The same invariant could also be protected by checks
written directly inside withdraw().
Common mistakes and trade-offs
Section titled “Common mistakes and trade-offs”- Making every Member public by default expands the API callers depend on.
- Assuming private means only the current instance may access the Member.
- Making State protected only because a Child Class exists.
- Naming a helper after only one of several checks it performs.
- Treating visibility as a substitute for validating State transitions.
Use the narrowest access level that supports the intended collaboration. Public operations are necessary for callers; private details are easier to change without affecting them. Protected access creates a dependency for Child Classes, so introduce it deliberately.
Summary and knowledge check
Section titled “Summary and knowledge check”Public exposes a Member to callers. Protected also supports access through the inheritance family. Private restricts access to code in the declaring Class, including access to other instances of that Class.
- Why can
hasMoreThan()read another Wallet’s private balance? - Can an external caller invoke
ensureCanWithdraw()? - Why does a protected Property remain inaccessible from outside the Class?
- Why is a small public API easier to maintain?