Class vs object in 10 seconds
A class is a cookie cutter. Objects are the cookies. One cutter can make 100 cookies, each with its own sprinkles.
class Cookie // the cutter
{
public function __construct(
public string $sprinkles
) {}
}
$a = new Cookie('rainbow'); // a cookie
$b = new Cookie('chocolate'); // another cookie
Tap a pillar to jump to it
An ATM. You can deposit and withdraw using the buttons, but you can't open the machine and grab the cash. The machine also checks the rules: “not enough money? No cash for you.”
class BankAccount
{
private int $balance = 100; // 🔒 locked inside
public function deposit(int $amount): void
{
if ($amount <= 0) throw new Exception('Amount must be positive.');
$this->balance += $amount;
}
public function withdraw(int $amount): void
{
if ($amount > $this->balance) throw new Exception('Not enough money!');
$this->balance -= $amount;
}
public function getBalance(): int
{
return $this->balance;
}
}
The keywords are the locks: public means anyone can use it, protected means only this class and its children, and private means only this class. Data is usually private; the buttons (methods) are public.
Encapsulation = a capsule. The medicine (data) is sealed inside; you only interact with the outside.
A model's $fillable list decides which fields can be filled from outside, and $hidden keeps the password out of JSON. Same idea: protect the inside.
Every car can drive and honk. A sports car is a car, so it gets those for free, and it adds a turbo button. Nobody re-invents the wheel. Literally. 😄
class Car
{
public function drive() { return 'Vroom! Driving…'; }
public function honk() { return 'Beep beep!'; }
}
class SportsCar extends Car // 🧬 inherits drive() + honk()
{
public function turbo() { return '🔥 TURBO BOOST!'; }
}
The magic word is extends. The child (SportsCar) gets every public and protected method of the parent (Car). It can also override one by writing a method with the same name.
Ask: “Is a SportsCar a Car?” If the answer is “yes, it is a…”, inheritance fits.
class Post extends Model. That one word gives your tiny Post class hundreds of inherited powers: save(), delete(), find(), where()… you write none of them.
A TV remote has a few simple buttons. You press “Volume +” without knowing about infrared signals, chips or amplifiers. The complexity exists, it's just hidden.
interface Remote // 📋 the buttons you get
{
public function power(): void;
public function volumeUp(): void;
public function nextChannel(): void;
}
// Some class does the hard work behind these buttons.
// You only ever talk to the buttons.
In PHP, abstraction usually means an interface (a list of buttons, with no wiring) or an abstract class (some buttons wired, some left for the child to finish). Both say what can be done, not how.
Abstraction = the menu, not the kitchen. You order “pasta” without watching the chef cook.
Mail::to($user)->send(new Welcome) sends an email. You never touch SMTP servers or encoding. Cache::put() works the same whether the cache is a file, Redis or a database.
Say “speak!” to a dog, a cat and a cow. Same command, but you get “woof”, “meow” and “moo”. You don't need a different command for each animal.
interface PaymentMethod
{
public function pay(int $amount): string;
}
class CardPayment implements PaymentMethod {
public function pay(int $amount): string { return "Charged card {$amount}"; }
}
class PayPalPayment implements PaymentMethod {
public function pay(int $amount): string { return "Paid {$amount} via PayPal"; }
}
function checkout(PaymentMethod $method, int $amount) {
return $method->pay($amount); // 🎭 works for ANY method
}
The checkout() function doesn't care which payment it gets, only that it can pay(). Adding Apple Pay tomorrow? Write one new class. Checkout stays untouched.
Polymorphism = a universal remote. The same “play” button works on the TV, the speaker and the projector. Each device plays in its own way.
Change MAIL_MAILER=smtp to MAIL_MAILER=log in .env and your code keeps calling the same send(). A different “driver” class just handles it differently. That's polymorphism powering Laravel's drivers.
All four on one card
| Pillar | In one line | Real life | PHP keyword |
|---|---|---|---|
| 🔒 Encapsulation | Protect data; change it only through methods | ATM | private / public |
| 🧬 Inheritance | Reuse a parent's code and add extras | Car → Sports car | extends |
| 📺 Abstraction | Show simple buttons, hide the wiring | TV remote | interface / abstract |
| 🎭 Polymorphism | Same method name, different behaviour | “Speak!” to pets | implements + same method |
Want the deeper version of each? Visit Encapsulation, Inheritance, Abstraction and Polymorphism in Module 02.
Avoid this, do that
if ($type === 'card') { /* … */ }
elseif ($type === 'paypal') { /* … */ }
elseif ($type === 'crypto') { /* … */ }
Every new payment method means editing this growing if-chain, and it's easy to break.
$method->pay($amount);
Let each class handle itself (polymorphism). New method? New class. Nothing else changes.
- In the bank demo, try the hack button. What protects the balance?
- Write a
Dogand aCatclass that both implement anAnimalinterface withspeak(). - Create
ElectricCar extends Carand overridehonk()to return “Beep (quietly)”. - Open any model in a Laravel project. Can you spot inheritance (
extends) and encapsulation ($fillable,$hidden)?
Key takeaways
- Encapsulation: keep data private and change it through public methods that check the rules.
- Inheritance:
extendslets a child reuse everything a parent has. - Abstraction: interfaces show what you can do and hide how.
- Polymorphism: one method name, many behaviours, so your code works with any matching class.
- Laravel uses all four everywhere: models, drivers, facades and contracts.