PHP, especially with its modern features like attributes, enums, and readonly properties in PHP, is well-suited for implementing design patterns. Here are five essential patterns every software engineer should know.
1. Singleton Pattern
Ensures a class has only one instance.
final class Config
{
private static ?Config $instance = null;
private function __construct(public readonly array $settings) {}
public static function getInstance(): Config
{
return self::$instance ??= new Config(['env' => 'production']);
}
}
// Usage
$config = Config::getInstance();
echo $config->settings['env']; // Output: production
2. Factory Pattern
Centralizes object creation logic.
class DatabaseFactory
{
public static function create(string $type): Database
{
return match ($type) {
'mysql' => new MySQLDatabase(),
'postgres' => new PostgresDatabase(),
default => throw new InvalidArgumentException("Unknown database type"),
};
}
}
interface Database { public function connect(): void; }
class MySQLDatabase implements Database { public function connect() { echo "MySQL connected"; } }
class PostgresDatabase implements Database { public function connect() { echo "Postgres connected"; } }
// Usage
$db = DatabaseFactory::create('mysql');
$db->connect(); // Output: MySQL connected
3. Observer Pattern
Notifies multiple objects about state changes.
class Event
{
private array $listeners = [];
public function attach(callable $listener): void { $this->listeners[] = $listener; }
public function trigger(string $data): void { foreach ($this->listeners as $listener) $listener($data); }
}
// Usage
$event = new Event();
$event->attach(fn($data) => print "Listener 1: $data\n");
$event->attach(fn($data) => print "Listener 2: $data\n");
$event->trigger("Event triggered");
// Output:
// Listener 1: Event triggered
// Listener 2: Event triggered
4. Decorator Pattern
Dynamically adds behavior to objects.
interface Text { public function render(): string; }
class PlainText implements Text
{
public function __construct(private string $text) {}
public function render(): string { return $this->text; }
}
class BoldText implements Text
{
public function __construct(private Text $text) {}
public function render(): string { return "<b>" . $this->text->render() . "</b>"; }
}
// Usage
$text = new BoldText(new PlainText("Hello, World!"));
echo $text->render(); // Output: <b>Hello, World!</b>
5. Strategy Pattern
Switches between algorithms at runtime.
interface PaymentStrategy { public function pay(float $amount): void; }
class CreditCardPayment implements PaymentStrategy
{
public function pay(float $amount): void { echo "Paid $amount with Credit Card\n"; }
}
class PayPalPayment implements PaymentStrategy
{
public function pay(float $amount): void { echo "Paid $amount via PayPal\n"; }
}
class PaymentProcessor
{
public function __construct(private PaymentStrategy $strategy) {}
public function execute(float $amount): void
{
$this->strategy->pay($amount);
}
}
// Usage
$processor = new PaymentProcessor(new PayPalPayment());
$processor->execute(100.00); // Output: Paid 100 via PayPal
These patterns solve real-world problems and are fundamental to writing maintainable and scalable applications.
Which pattern resonates with your current project? Let’s discuss in the comments! 🚀
Top comments (0)