Skip to content

PHP Tutorial Advanced OOP

Encapsulation

Lesson 217 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

Encapsulation means an object keeps its data private and offers a small set of methods to work with it. Outside code cannot put the object into an invalid state.

You already know the tools: private properties and public methods. Here is how to use them well.

The problem: open data

index.php
PHP
<?php
class BadAccount
{
    public float $balance = 0;
}

$a = new BadAccount();
$a->balance = -99999;   // nothing stops this

echo $a->balance;
Output
OUTPUT
-99999

The solution: protect the rules

index.php
PHP
<?php
class Account
{
    private float $balance = 0;

    public function deposit(float $amount): void
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException("Deposit must be positive.");
        }

        $this->balance += $amount;
    }

    public function withdraw(float $amount): void
    {
        if ($amount > $this->balance) {
            throw new RuntimeException("Insufficient funds.");
        }

        $this->balance -= $amount;
    }

    public function balance(): float
    {
        return $this->balance;
    }
}

$account = new Account();
$account->deposit(100);

try {
    $account->withdraw(500);
} catch (RuntimeException $e) {
    echo $e->getMessage() . "\n";
}

echo $account->balance();
Output
OUTPUT
Insufficient funds.
100

The rule “the balance can never go negative” lives in one place, the class. No code anywhere can break it.

Invariants

A condition that is always true for an object is an invariant. A Percentage is always between 0 and 100. An Email is always a valid address. Encapsulation protects invariants.

Immutable value objects

A value object represents a value (money, a date range, a coordinate). It is often immutable: instead of changing it, you create a new one.

index.php
PHP
<?php
final class Money
{
    public function __construct(
        public readonly int $cents,
        public readonly string $currency,
    ) {}

    public function add(Money $other): static
    {
        if ($other->currency !== $this->currency) {
            throw new InvalidArgumentException("Currency mismatch.");
        }

        return new static($this->cents + $other->cents, $this->currency);
    }
}

$a = new Money(500, "INR");
$b = $a->add(new Money(250, "INR"));

echo $a->cents . " and " . $b->cents;
Output
OUTPUT
500 and 750

$a did not change. Immutable objects cannot be changed by accident, and you can share them freely.

Tell, don’t ask

Instead of asking an object for its data and deciding yourself, tell it what to do.

index.php
PHP
// Ask: the caller makes the decision and knows the internals
if ($account->getBalance() >= $amount) {
    $account->setBalance($account->getBalance() - $amount);
}

// Tell: the object decides
$account->withdraw($amount);

The second version cannot be forgotten or done wrongly. The rule is part of the class.

Do not add a getter and setter for everything

A setter for every property puts you back where you started: anyone can set anything. Add a method only when outside code needs it, and give it a meaningful name (withdraw, not setBalance).

Encapsulation vs security

Private properties protect your code from other code. They are not about hiding secrets from users. The data is still in memory, and a debugger or serialization can show it.

info
Why this matters in Laravel
Eloquent models guard against mass assignment with $fillable and $guarded, which is encapsulation applied to database fields: the model decides what can be set from outside.