OOP Complete Tutorial: Object-Oriented Programming (65 Topics with Code) — a practical guide to OOP tutorial with clear examples you can reuse in real projects.
This complete Object-Oriented Programming (OOP) tutorial covers 65 topics — from classes and inheritance to SOLID, design patterns, MVC, and OOP in PHP, WordPress, and Laravel. Beginner-friendly with practical PHP code.
Course roadmap
- Introduction to OOP
- What is Object-Oriented Programming?
- Procedural Programming vs OOP
- Advantages of OOP
- Classes and Objects
- Creating a Class
- Creating Objects
- Properties and Attributes
- Methods and Functions
- Constructors in OOP
- Destructors in OOP
- The $this Keyword
- Access Modifiers in OOP
- Public, Private and Protected
- Encapsulation in OOP
- Inheritance in OOP
- Types of Inheritance
- Single Inheritance
- Multilevel Inheritance
- Hierarchical Inheritance
- Multiple Inheritance
- Polymorphism in OOP
- Method Overloading
- Method Overriding
- Abstraction in OOP
- Abstract Classes
- Interfaces in OOP
- Traits in PHP OOP
- Static Properties and Methods
- Constants in Classes
- Final Classes and Methods
- Parent and Child Classes
- The parent Keyword
- The self Keyword
- instanceof in PHP
- Getters and Setters
- Dependency Injection
- Composition vs Inheritance
- Association, Aggregation and Composition
- Object Cloning
- Magic Methods in PHP OOP
- Namespaces in PHP
- Autoloading in PHP
- PSR Standards
- Exception Handling in OOP
- SOLID Principles
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
- Design Patterns Introduction
- Factory Pattern
- Singleton Pattern
- Repository Pattern
- MVC Architecture
- OOP with Database
- OOP with MySQL
- OOP in PHP
- OOP in WordPress Plugin Development
- OOP in WordPress Theme Development
- OOP in Laravel
- OOP Interview Questions
- OOP Practical Examples
- Final Project – Complete OOP-Based PHP Application
1. Introduction to OOP
OOP helps you model software as interacting objects. This series uses PHP examples and connects OOP to WordPress plugins/themes and Laravel.
- Learn classes, objects, and encapsulation first.
- Practice inheritance and interfaces.
- Apply SOLID and build the final PHP project.
Learning path
Classes/objects → encapsulation/inheritance
Polymorphism/abstraction → SOLID
Patterns/MVC → PHP apps, WP, Laravel
Final OOP PHP project
2. What is Object-Oriented Programming?
In OOP, you define classes (blueprints) and create objects (instances) that hold state (properties) and behavior (methods).
Core idea
class User {
public string $name;
public function greet(): string {
return 'Hi ' . $this->name;
}
}
$u = new User();
$u->name = 'Asha';
echo $u->greet();
3. Procedural Programming vs OOP
Procedural code focuses on functions and data separately. OOP groups related data and behavior, which scales better for larger apps.
Same task, two styles
// Procedural
function user_greet(string $name): string {
return 'Hi ' . $name;
}
// OOP
class Greeter {
public function __construct(private string $name) {}
public function greet(): string { return 'Hi ' . $this->name; }
}
4. Advantages of OOP
OOP improves code organization through encapsulation, reuse via inheritance/composition, and flexible designs through polymorphism.
Key advantages
- Modularity
- Reusability
- Easier maintenance
- Better domain modeling
- Testable units (classes/services)
5. Classes and Objects
A class defines structure. An object is a live instance created with `new` that uses that structure.
Class vs object
class Product {}
$a = new Product(); // object
$b = new Product(); // another object
6. Creating a Class
Use clear class names (StudlyCase). Keep one main responsibility per class when possible.
Create a class
class Invoice {
public string $number;
public float $total = 0.0;
public function add(float $amount): void {
$this->total += $amount;
}
}
7. Creating Objects
Each object has its own property values even if they share the same class.
Instantiate
$inv = new Invoice();
$inv->number = 'INV-1001';
$inv->add(250);
echo $inv->total;
8. Properties and Attributes
Properties hold data. Prefer typed properties in modern PHP and initialize them safely.
Typed properties
class Article {
public string $title;
public bool $published = false;
}
9. Methods and Functions
Methods can read/update properties and return values. Keep methods focused and named clearly.
Instance method
class Counter {
private int $count = 0;
public function increment(): int {
return ++$this->count;
}
}
10. Constructors in OOP
Constructors set required state. In modern PHP, constructor property promotion keeps code short.
Constructor promotion
class User {
public function __construct(
public string $name,
public string $email,
) {}
}
$u = new User('Ravi', 'ravi@example.com');
11. Destructors in OOP
Destructors are less common in PHP web apps (request lifecycle is short), but useful for closing resources in long-running CLI processes.
Destructor
class FileLogger {
public function __destruct() {
// close handles / flush buffers
}
}
12. The $this Keyword
Inside an instance method, `$this` refers to the object the method was called on.
$this usage
class Account {
private float $balance = 0;
public function deposit(float $amount): void {
$this->balance += $amount;
}
public function getBalance(): float {
return $this->balance;
}
}
13. Access Modifiers in OOP
Access modifiers are a core encapsulation tool — they hide internal details and expose a safe API.
Visibility overview
public → accessible everywhere
protected → class + subclasses
private → only inside the same class
14. Public, Private and Protected
Default to private/protected for state; expose only what callers need via public methods.
Visibility example
class BankAccount {
private float $balance = 0;
public function deposit(float $amount): void {
if ($amount <= 0) throw new InvalidArgumentException('Invalid amount');
$this->balance += $amount;
}
}
15. Encapsulation in OOP
Encapsulation protects invariants (rules). Callers use methods instead of changing properties directly.
Encapsulated API
class Temperature {
public function __construct(private float $celsius) {}
public function toFahrenheit(): float {
return ($this->celsius * 9 / 5) + 32;
}
}
16. Inheritance in OOP
Inheritance models “is-a” relationships (Dog is an Animal). Prefer composition when “has-a” fits better.
extends
class Animal {
public function speak(): string { return '...'; }
}
class Dog extends Animal {
public function speak(): string { return 'Woof'; }
}
17. Types of Inheritance
PHP supports single class inheritance and multiple interfaces/traits to cover many multiple-inheritance use cases.
Inheritance types
Single → one parent
Multilevel → chain of parents
Hierarchical → many children from one parent
Multiple → many parents (via interfaces/traits in PHP)
18. Single Inheritance
A class may extend only one parent class in PHP. Use interfaces/traits for additional reuse.
Single parent
class Vehicle {}
class Car extends Vehicle {}
19. Multilevel Inheritance
Keep chains short. Deep hierarchies become hard to understand and change.
Multilevel
class Employee {}
class Manager extends Employee {}
class Director extends Manager {}
20. Hierarchical Inheritance
Hierarchical inheritance is common for payment gateways, notifiers, and shape hierarchies.
One parent, many children
class Shape {}
class Circle extends Shape {}
class Square extends Shape {}
21. Multiple Inheritance
PHP does not allow extending multiple classes. Implement multiple interfaces and use traits for shared code.
Interfaces + trait
interface Loggable { public function log(string $msg): void; }
interface Cacheable { public function cacheKey(): string; }
trait WritesLog {
public function log(string $msg): void { echo $msg; }
}
class Report implements Loggable, Cacheable {
use WritesLog;
public function cacheKey(): string { return 'report'; }
}
22. Polymorphism in OOP
Polymorphism lets you write code against interfaces/base types while concrete classes decide the behavior.
Polymorphic loop
interface Notifier { public function send(string $to): void; }
class EmailNotifier implements Notifier {
public function send(string $to): void { /* email */ }
}
class SmsNotifier implements Notifier {
public function send(string $to): void { /* sms */ }
}
/** @param Notifier[] $notifiers */
function notifyAll(array $notifiers, string $to): void {
foreach ($notifiers as $n) { $n->send($to); }
}
23. Method Overloading
Classic overloading (same method name, different signatures) is not native in PHP. Use default args, variadics, or separate methods instead.
PHP-style flexibility
class Math {
public function sum(int ...$nums): int {
return array_sum($nums);
}
}
echo (new Math())->sum(1, 2, 3);
24. Method Overriding
Overriding customizes inherited behavior. Call `parent::method()` when you need to extend rather than fully replace.
Override
class Controller {
public function handle(): string { return 'base'; }
}
class ApiController extends Controller {
public function handle(): string { return 'json'; }
}
25. Abstraction in OOP
Abstraction focuses on “what” an object does. Abstract classes and interfaces help define clean contracts.
Abstraction idea
Caller: $gateway->charge(100)
Hidden: HTTP, signatures, retries, logging
26. Abstract Classes
Abstract classes can include shared code plus abstract methods children must implement.
Abstract class
abstract class PaymentGateway {
abstract public function charge(int $amount): bool;
public function currency(): string { return 'INR'; }
}
class StripeGateway extends PaymentGateway {
public function charge(int $amount): bool { return true; }
}
27. Interfaces in OOP
Interfaces are ideal for polymorphism and dependency injection. A class can implement many interfaces.
Interface
interface Logger {
public function info(string $message): void;
}
class FileLogger implements Logger {
public function info(string $message): void { file_put_contents('app.log', $message.PHP_EOL, FILE_APPEND); }
}
28. Traits in PHP OOP
Traits solve code reuse without multiple class inheritance. Watch for conflict resolution with `insteadof`/`as`.
Trait usage
trait HasTimestamps {
public function touch(): void { $this->updatedAt = new DateTimeImmutable(); }
}
class Post {
use HasTimestamps;
public ?DateTimeImmutable $updatedAt = null;
}
29. Static Properties and Methods
Static members belong to the class, not an instance. Overuse can hurt testability — prefer dependency injection for services.
Static example
class IdGenerator {
private static int $last = 0;
public static function next(): int {
return ++self::$last;
}
}
echo IdGenerator::next();
30. Constants in Classes
Class constants are useful for statuses, config keys, and fixed options.
Class const
class OrderStatus {
public const PENDING = 'pending';
public const PAID = 'paid';
}
echo OrderStatus::PAID;
31. Final Classes and Methods
Use `final` to protect critical classes/methods from unsafe extension.
final examples
final class Money {
public function __construct(public int $cents) {}
}
class Service {
final public function boot(): void {}
}
32. Parent and Child Classes
Children inherit members and can add/override behavior while staying substitutable for the parent type.
Parent/child
class User { public function role(): string { return 'user'; } }
class Admin extends User { public function role(): string { return 'admin'; } }
33. The parent Keyword
`parent::` accesses the overridden implementation in the parent class.
parent:: usage
class Model {
public function __construct(public array $attrs = []) {}
}
class Post extends Model {
public function __construct(array $attrs = []) {
parent::__construct($attrs);
}
}
34. The self Keyword
`self` refers to the class where it is written. For late static binding (inheritance-aware), use `static::`.
self vs static
class Base {
public static function who(): string { return self::class; }
public static function whoLate(): string { return static::class; }
}
class Child extends Base {}
echo Child::who(); // Base
echo Child::whoLate(); // Child
35. instanceof in PHP
`instanceof` works with classes and interfaces. Prefer type hints when possible instead of frequent runtime checks.
instanceof
function dump($value): void {
if ($value instanceof DateTimeInterface) {
echo $value->format('c');
}
}
36. Getters and Setters
Getters/setters validate changes and preserve encapsulation. In modern PHP, consider readonly properties when mutation is unnecessary.
Getter/setter
class Profile {
public function __construct(private string $email) {}
public function getEmail(): string { return $this->email; }
public function setEmail(string $email): void {
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email');
}
$this->email = $email;
}
}
37. Dependency Injection
Dependency Injection (DI) improves testing and flexibility. Inject interfaces, not concrete classes, when useful.
Constructor injection
interface Mailer { public function send(string $to, string $body): void; }
class WelcomeService {
public function __construct(private Mailer $mailer) {}
public function welcome(string $email): void {
$this->mailer->send($email, 'Welcome!');
}
}
38. Composition vs Inheritance
Composition builds behavior by combining objects. It usually reduces fragile base-class problems.
Composition
class Engine { public function start(): string { return 'vroom'; } }
class Car {
public function __construct(private Engine $engine) {}
public function start(): string { return $this->engine->start(); }
}
39. Association, Aggregation and Composition
Association links objects. Aggregation is a weak “has-a”. Composition is a strong “owns-a” lifecycle relationship.
Relationship summary
Association → uses
Aggregation → has (parts can live independently)
Composition → owns (parts die with the whole)
40. Object Cloning
Default cloning is shallow. Implement `__clone` for deep copies of nested objects.
clone + __clone
class Cart {
public function __construct(public array $items) {}
public function __clone(): void {
// adjust cloned state if needed
}
}
$a = new Cart(['book']);
$b = clone $a;
41. Magic Methods in PHP OOP
Magic methods are powerful but can hide logic. Use them deliberately and document behavior.
__toString and __invoke
class Money {
public function __construct(private int $cents) {}
public function __toString(): string { return ($this->cents / 100) . ' INR'; }
}
echo new Money(1999);
42. Namespaces in PHP
Namespaces map well to folder structures and Composer PSR-4 autoloading.
Namespace example
namespace AppBilling;
class Invoice {}
// elsewhere:
use AppBillingInvoice;
$invoice = new Invoice();
43. Autoloading in PHP
Autoloading removes endless `require` statements and is standard in modern PHP apps.
Composer PSR-4
{
"autoload": {
"psr-4": {
"App": "src/"
}
}
}
44. PSR Standards
Key PSRs: PSR-1/PSR-12 coding style, PSR-4 autoloading, PSR-3 logging, PSR-7 HTTP messages, PSR-11 containers.
Useful PSRs
PSR-12 → style
PSR-4 → autoloading
PSR-3 → logger interface
PSR-7/15 → HTTP & middleware
PSR-11 → container
45. Exception Handling in OOP
Create domain exceptions, throw early, catch at boundaries, and avoid empty catch blocks.
Custom exception
class InsufficientFunds extends RuntimeException {}
class Wallet {
public function __construct(private int $cents) {}
public function pay(int $amount): void {
if ($amount > $this->cents) throw new InsufficientFunds('Not enough funds');
$this->cents -= $amount;
}
}
46. SOLID Principles
SOLID helps keep classes focused, extensible, and loosely coupled — especially important as projects grow.
SOLID map
S — Single Responsibility
O — Open/Closed
L — Liskov Substitution
I — Interface Segregation
D — Dependency Inversion
47. Single Responsibility Principle
SRP reduces tangled code. Split reporting, persistence, and emailing into separate classes.
SRP example
class OrderRepository { public function save(Order $order): void {} }
class OrderMailer { public function sendConfirmation(Order $order): void {} }
48. Open/Closed Principle
Add new behavior via new classes implementing interfaces, instead of editing giant switch statements repeatedly.
OCP sketch
interface Discount { public function apply(float $total): float; }
class PercentageDiscount implements Discount {
public function __construct(private float $percent) {}
public function apply(float $total): float { return $total * (1 - $this->percent / 100); }
}
49. Liskov Substitution Principle
If code expects a `Bird`, a subclass should not surprise it (e.g., by throwing on `fly()` when flying was assumed).
LSP tip
Subtypes must honor parent contracts
Don't strengthen preconditions unexpectedly
Prefer separate interfaces when behaviors diverge
50. Interface Segregation Principle
Many specific interfaces are better than one fat interface.
ISP example
interface CanPrint { public function print(): void; }
interface CanScan { public function scan(): void; }
class SimplePrinter implements CanPrint {
public function print(): void {}
}
51. Dependency Inversion Principle
DIP is the principle behind good dependency injection and testable architecture.
DIP
interface UserRepository { public function find(int $id): ?User; }
class UserService {
public function __construct(private UserRepository $users) {}
}
52. Design Patterns Introduction
Patterns are tools, not goals. Learn a few deeply (Factory, Strategy, Repository, Adapter) before collecting many.
Pattern categories
Creational → Factory, Singleton
Structural → Adapter, Decorator
Behavioral → Strategy, Observer
53. Factory Pattern
Factories help when creation logic is non-trivial or when you want to return interface types.
Simple factory
class NotifierFactory {
public static function make(string $channel): Notifier {
return match ($channel) {
'email' => new EmailNotifier(),
'sms' => new SmsNotifier(),
default => throw new InvalidArgumentException('Unknown channel'),
};
}
}
54. Singleton Pattern
Singleton restricts a class to one instance. It can hide dependencies and complicate testing — use sparingly.
Singleton sketch
final class Config {
private static ?self $instance = null;
private function __construct() {}
public static function getInstance(): self {
return self::$instance ??= new self();
}
}
55. Repository Pattern
Repositories make services database-agnostic and easier to fake in tests.
Repository interface
interface PostRepository {
public function find(int $id): ?Post;
public function save(Post $post): void;
}
class MysqlPostRepository implements PostRepository {
public function find(int $id): ?Post { /* query */ return null; }
public function save(Post $post): void { /* insert/update */ }
}
56. MVC Architecture
MVC keeps UI, request handling, and domain/data logic separated — used by Laravel, many PHP apps, and inspired WP patterns.
MVC roles
Model → data & business rules
View → presentation
Controller → request flow / input mapping
57. OOP with Database
Use connection services, repositories/models, and entities/DTOs to keep SQL organized.
DB wrapper sketch
class Database {
public function __construct(private PDO $pdo) {}
public function fetchAll(string $sql, array $params = []): array {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
58. OOP with MySQL
PDO is object-oriented and supports safe parameterized queries for MySQL.
PDO connection
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4',
'root',
'',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([1]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
59. OOP in PHP
PHP’s OOP model is mature — use typed properties, return types, interfaces, and Composer autoloading as defaults.
Modern PHP OOP
enum Status: string { case Draft = 'draft'; case Published = 'published'; }
final class Post {
public function __construct(
public readonly string $title,
public Status $status = Status::Draft,
) {}
}
60. OOP in WordPress Plugin Development
OOP plugins are easier to maintain: one class for admin, one for REST, one for CPT registration, bootstrapped from the main plugin file.
Plugin class bootstrap
namespace AcmeTools;
final class Plugin {
public function boot(): void {
add_action('init', [$this, 'register']);
}
public function register(): void {
// CPT, shortcodes, etc.
}
}
(new Plugin())->boot();
61. OOP in WordPress Theme Development
Even classic themes benefit from OOP helpers and setup classes instead of a giant functions.php.
Theme setup class
final class ThemeSetup {
public function register(): void {
add_action('after_setup_theme', [$this, 'supports']);
add_action('wp_enqueue_scripts', [$this, 'assets']);
}
public function supports(): void {
add_theme_support('title-tag');
}
public function assets(): void {
wp_enqueue_style('theme', get_stylesheet_uri(), [], '1.0.0');
}
}
62. OOP in Laravel
Laravel is built around dependency injection and interfaces (contracts). Your app code should follow the same OOP style.
Laravel-style injection
// app/Services/InvoiceService.php
namespace AppServices;
class InvoiceService {
public function __construct(private InvoiceRepository $invoices) {}
public function dueSoon(): array {
return $this->invoices->dueWithinDays(7);
}
}
63. OOP Interview Questions
Be ready to explain encapsulation, inheritance vs composition, interfaces vs abstract classes, and SOLID with examples.
Sample Q&A
Q: Interface vs abstract class?
A: Interface = contract only (mostly); abstract class can share code.
Q: Overloading vs overriding?
A: Overloading = same name different signatures (limited in PHP); overriding = redefine parent method.
Q: Why DI?
A: Loose coupling + easier testing.
Q: Composition vs inheritance?
A: Prefer has-a composition for flexibility.
64. OOP Practical Examples
Practical examples connect theory to code you can reuse in apps and interviews.
Mini cart example
class CartItem {
public function __construct(public string $name, public int $priceCents, public int $qty = 1) {}
public function lineTotal(): int { return $this->priceCents * $this->qty; }
}
class Cart {
/** @var CartItem[] */
private array $items = [];
public function add(CartItem $item): void { $this->items[] = $item; }
public function total(): int {
return array_sum(array_map(fn (CartItem $i) => $i->lineTotal(), $this->items));
}
}
65. Final Project – Complete OOP-Based PHP Application
Create a small application (task manager or blog admin) using namespaces, Composer autoload, PDO repositories, services, and simple controllers/views.
- Define the domain (tasks/posts/users).
- Implement repositories and services.
- Wire a front controller with simple routing.
- Add validation, auth, and README.
Final project scope
1. Composer + PSR-4 namespaces
2. PDO Database class
3. Entities + Repository interfaces
4. Services with constructor DI
5. Controllers + simple PHP views (MVC)
6. Exceptions + validation objects
7. Auth session service
8. README + UML/class list
Suggested structure
public/index.php
src/Controller
src/Domain
src/Repository
src/Service
src/Support
views/
composer.json
Conclusion
You now have a complete OOP foundation: classes, encapsulation, inheritance, polymorphism, SOLID, and practical patterns. Apply it in PHP applications, WordPress plugins/themes, and Laravel — then finish the final OOP PHP project.