What are design patterns and why are they needed in PHP?
Design patterns are proven solutions to typical problems that arise during software development. They are not ready-made code, but rather templates that can be adapted to a specific situation. For a PHP developer, knowledge of patterns helps write more flexible, maintainable, and scalable code.
In this article, we will cover four basic patterns from the GoF (Gang of Four) catalog that are most commonly used in real PHP projects. Each pattern will be accompanied by a practical example and explanations.
1. Singleton
The Singleton pattern guarantees that a class has only one instance and provides a global access point to it. This is useful for managing database connections, loggers, or cache.
When to apply?
- Exactly one object is needed (e.g., a database connection).
- The object is used throughout the application, and creating duplicates is not allowed.
Implementation example:
<?phpclass DatabaseConnection{ private static ?self $instance = null; private PDO $pdo;
private function __construct() { $this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'); }
public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; }
public function getConnection(): PDO { return $this->pdo; }
// Prevent cloning and deserialization private function __clone() {} public function __wakeup() { throw new \\Exception("Cannot unserialize singleton"); }}
// Usage:$db = DatabaseConnection::getInstance();$pdo = $db->getConnection();?>Important: Singleton is often criticized for violating the single responsibility principle and complicating testing. Use it consciously and only where it is truly necessary.
2. Factory Method
The Factory Method pattern defines an interface for creating an object but allows subclasses to alter the type of object that will be created. This allows delegating object creation logic to child classes.
When to apply?
- It is not known in advance which specific object will need to be created.
- You want to hide complex object creation logic from the client.
Implementation example:
<?php// Interface for all notification typesinterface Notification{ public function send(string $message): void;}
// Concrete implementationsclass EmailNotification implements Notification{ public function send(string $message): void { echo "Sending email: {$message}"; }}
class SMSNotification implements Notification{ public function send(string $message): void { echo "Sending SMS: {$message}"; }}
// Abstract creatorabstract class NotificationFactory{ abstract public function createNotification(): Notification;
public function notify(string $message): void { $notification = $this->createNotification(); $notification->send($message); }}
// Concrete factoriesclass EmailFactory extends NotificationFactory{ public function createNotification(): Notification { return new EmailNotification(); }}
class SMSFactory extends NotificationFactory{ public function createNotification(): Notification { return new SMSNotification(); }}
// Usage:$factory = new EmailFactory();$factory->notify("Your order has been confirmed!");
$factory = new SMSFactory();$factory->notify("Confirmation code: 1234");