PHP Strategy Design Pattern Behavioral Pattern

PHP Strategy Pattern

A short screencast: the Strategy pattern — you can swap algorithms at runtime without changing the client. Example with different payment methods.

Strategy Pattern — open the lesson page

Overview

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. The client selects the needed algorithm at runtime by passing it to the context. The PaymentStrategy interface defines the common payment method. Concrete strategies CreditCardPayment and PaypalPayment implement different payment ways. The context ShoppingCart takes a strategy through its constructor and delegates payment to the chosen strategy in checkout() — it does not know the implementation details.

UML diagram

classDiagram class PaymentStrategy { <<interface>> +pay(amount: int): void } class CreditCardPayment { -cardNumber: string +pay(amount: int): void } class PaypalPayment { -email: string +pay(amount: int): void } class ShoppingCart { -strategy: PaymentStrategy +checkout(amount: int): void } PaymentStrategy <|.. CreditCardPayment PaymentStrategy <|.. PaypalPayment ShoppingCart o-- PaymentStrategy

Code

PaymentStrategy.php

interface PaymentStrategy
{
    public function pay(int $amount): void;
}

CreditCardPayment.php

class CreditCardPayment implements PaymentStrategy
{
    public function __construct(private readonly string $cardNumber) {}

    public function pay(int $amount): void
    {
        echo "Paid {$amount} via Credit Card ({$this->cardNumber})\n";
    }
}

PaypalPayment.php

class PaypalPayment implements PaymentStrategy
{
    public function __construct(private readonly string $email) {}

    public function pay(int $amount): void
    {
        echo "Paid {$amount} via PayPal ({$this->email})\n";
    }
}

ShoppingCart.php

class ShoppingCart
{
    public function __construct(private readonly PaymentStrategy $strategy) {}

    public function checkout(int $amount): void
    {
        $this->strategy->pay($amount);
    }
}

demo.php

require_once 'PaymentStrategy.php';
require_once 'CreditCardPayment.php';
require_once 'PaypalPayment.php';
require_once 'ShoppingCart.php';

$cart = new ShoppingCart(new CreditCardPayment('1234-5678-9012-3456'));
$cart->checkout(100);

$cart = new ShoppingCart(new PaypalPayment('user@example.com'));
$cart->checkout(200);

Example output

Paid 100 via Credit Card (1234-5678-9012-3456)
Paid 200 via PayPal (user@example.com)

Advantages

  • Algorithms are encapsulated and easily interchangeable.
  • The client is decoupled from algorithm implementation details.
  • New strategies can be added without changing existing code.
  • Follows open/closed and dependency inversion principles.

When to use

  • When you need to choose from several algorithms dynamically.
  • When many classes differ only in behavior.
  • When you want to hide algorithm details from the client.
  • When behavior can vary without changing the context.

Usage in Laravel

In Laravel the Strategy pattern appears in driver selection: queue drivers (redis, database, sqs), different auth methods, or various payment strategies in packages. Illuminate\Support\Facades\Cache also lets you choose a cache driver at runtime. The context uses the selected strategy without knowing its implementation details.