PHP OOP — Classes and Objects
Object-Oriented Programming (OOP) organizes code around classes (blueprints) and objects (instances built from those blueprints). Instead of loose functions and arrays floating around, related data and behavior are bundled together. This page covers classes, constructors, visibility, inheritance, interfaces, static members, and traits — with a real class hierarchy executing live on this page.
1) Defining a Class (Properties & Methods)
A class is a blueprint. Properties are variables that belong to the class,
and methods are functions that belong to the class.
<?php
class Car {
public string $brand;
public int $speed = 0;
public function accelerate(int $by) {
$this->speed += $by;
}
}
?>
2) Creating Objects with new
An object is a concrete instance of a class, created with the new keyword.
<?php
$car1 = new Car();
$car1->brand = 'Toyota';
$car1->accelerate(20);
echo $car1->speed; // 20
?>
3) The Constructor & Property Visibility
__construct() runs automatically when an object is created — the natural
place to set up required properties. Visibility keywords control where a property or method
can be accessed from:
| Visibility | Accessible from |
|---|---|
public | Anywhere — inside the class, subclasses, and outside code |
protected | Inside the class and any subclass, not from outside code |
private | Only inside the exact class that declared it |
<?php
class Animal {
protected string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function speak(): string {
return $this->name . ' makes a sound.';
}
}
?>
4) Inheritance (extends)
A class can extends another class to reuse its properties and methods, then
override the ones that need to behave differently. parent::__construct() calls
the parent class’s constructor from inside the child’s constructor.
<?php
class Dog extends Animal {
public function __construct(string $name) {
parent::__construct($name); // reuse the parent logic
}
public function speak(): string {
return $this->name . ' says: Woof!';
}
}
?>
5) Live Demo: Real Inheritance Executing on This Page
The Animal, Dog, and Cat classes shown above are
defined for real at the very top of this file, before the <!DOCTYPE html>
line. The two lines below are not typed-out text — they are the actual return values
of $rex->speak() and $whiskers->speak(), echoed live by PHP:
Rex says: Woof!Whiskers says: Meow!
Dog and Cat call the same
inherited constructor from Animal, but each overrides speak() with
its own behavior — that is polymorphism in action.6) Interfaces (interface / implements)
An interface defines a contract — a list of method names a class promises to implement, without providing the implementation itself. It lets unrelated classes be treated the same way as long as they honor the contract.
<?php
interface Speaks {
public function speak(): string;
}
class Robot implements Speaks {
public function speak(): string {
return 'BEEP BOOP.';
}
}
?>
7) Static Properties & Methods
static members belong to the class itself, not to any one object. They are
accessed with ClassName::member instead of $object->member, and
they keep their value across every call, shared by all instances.
<?php
class Counter {
private static int $count = 0;
public static function increment(): int {
self::$count++;
return self::$count;
}
}
Counter::increment();
Counter::increment();
echo Counter::increment(); // 3
?>
Counter class above ran at the top of this file and
was called three times. The current count is:
3.
8) Traits (Sharing Methods Across Unrelated Classes)
Sometimes two classes need the same helper method but don’t share a meaningful parent/child
relationship. A trait is a chunk of reusable methods that can be mixed into any
class with use TraitName;.
<?php
trait Loggable {
public function log(string $msg) {
echo '[LOG] ' . $msg;
}
}
class Invoice {
use Loggable; // Invoice now has a log() method for free
}
?>
9) Best Practices & Common Mistakes
| Issue | Problem | Fix |
|---|---|---|
Everything public | Any code can corrupt internal state | Default to private/protected, expose only what’s needed |
| God classes | One class doing everything is hard to test/maintain | Split responsibilities into smaller classes |
Forgetting parent::__construct() | Parent setup logic never runs | Call it explicitly in the child constructor when needed |
| Using inheritance where a trait fits better | Forces an artificial parent/child relationship | Use a trait to share behavior between unrelated classes |