📡 You're offline — showing cached content
New version available!
Quick Access
JavaScript Intermediate

PHP Design Patterns: Singleton, Factory, Observer, Strategy and More

Master the most important PHP design patterns — Singleton, Factory, Observer, Strategy, Decorator, and Repository with complete code examples.

EzyCoders Admin January 18, 2026 15 min read 1 views
PHP Design Patterns Complete Guide
Share: Twitter LinkedIn WhatsApp

Why Design Patterns?

Design patterns are reusable solutions to common problems. They give developers a shared vocabulary and prevent reinventing the wheel. Senior PHP developers are expected to know and apply them.

Singleton — One Instance Only

<?php
class Database {
    private static ?self $instance = null;
    private PDO $pdo;

    private function __construct() {
        $this->pdo = new PDO('mysql:host=localhost;dbname=app', 'user', 'pass');
    }
    private function __clone() {}

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function query(string $sql, array $params = []): array {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

$db    = Database::getInstance();
$users = $db->query('SELECT * FROM users WHERE active = ?', [1]);

Factory — Create Without new

<?php
interface Notifier {
    public function send(string $to, string $message): bool;
}

class EmailNotifier implements Notifier {
    public function send(string $to, string $message): bool {
        echo "Email to $to: $message\n";
        return true;
    }
}

class SMSNotifier implements Notifier {
    public function send(string $to, string $message): bool {
        echo "SMS to $to: $message\n";
        return true;
    }
}

class NotifierFactory {
    public static function create(string $type): Notifier {
        return match($type) {
            'email' => new EmailNotifier(),
            'sms'   => new SMSNotifier(),
            default => throw new InvalidArgumentException("Unknown: $type"),
        };
    }
}

$n = NotifierFactory::create(config('notify.default'));
$n->send('rahul@example.com', 'Welcome to EzyCoders!');

Observer — Event-Driven

<?php
interface Observer {
    public function update(string $event, mixed $data): void;
}

class EventBus {
    private array $listeners = [];

    public function on(string $event, Observer $obs): void {
        $this->listeners[$event][] = $obs;
    }

    public function emit(string $event, mixed $data = null): void {
        foreach ($this->listeners[$event] ?? [] as $obs) {
            $obs->update($event, $data);
        }
    }
}

class WelcomeEmailer implements Observer {
    public function update(string $event, mixed $data): void {
        echo "Sending welcome email to {$data['email']}\n";
    }
}

class ReputationGranter implements Observer {
    public function update(string $event, mixed $data): void {
        echo "Granting 50 points to user {$data['id']}\n";
    }
}

$bus = new EventBus();
$bus->on('user.registered', new WelcomeEmailer());
$bus->on('user.registered', new ReputationGranter());
$bus->emit('user.registered', ['id'=>1,'email'=>'r@example.com']);

Repository — Abstract Data Access

<?php
interface PostRepository {
    public function findById(int $id): ?array;
    public function findPublished(): array;
    public function save(array $post): int;
}

class MysqlPostRepository implements PostRepository {
    public function __construct(private PDO $db) {}
    
    public function findById(int $id): ?array {
        $s = $this->db->prepare('SELECT * FROM posts WHERE id=?');
        $s->execute([$id]);
        return $s->fetch(PDO::FETCH_ASSOC) ?: null;
    }
    
    public function findPublished(): array {
        return $this->db->query("SELECT * FROM posts WHERE status='published'")->fetchAll(PDO::FETCH_ASSOC);
    }
    
    public function save(array $post): int {
        $s = $this->db->prepare('INSERT INTO posts (title,content) VALUES (?,?)');
        $s->execute([$post['title'], $post['content']]);
        return (int) $this->db->lastInsertId();
    }
}

Q: What problem does the Repository pattern solve?

It decouples business logic from data access. Your controllers and services call repository methods without knowing whether the data comes from MySQL, MongoDB, an API, or in-memory test data. Swapping databases requires only implementing a new repository class.

EzyCoders Admin
Written by
EzyCoders Admin

Team Lead and Full-Stack Developer with experience in PHP, JavaScript, SQL, DSA, and System Design. Passionate about software engineering, scalable web technologies, and helping developers prepare for coding interviews and tech careers through practical tutorials and professional guidance.

Comments (0)

No comments yet. Be the first!

Leave a Comment