⚙️

PHP Advanced Patterns

A zero-dependency PHP library implementing 6 advanced software patterns with 50+ methods: Pipeline, Result, EventEmitter, Container, Retry, and CircuitBreaker.

PHP Design Patterns · v1.0.0 · 0 downloads

Download

curl -OL https://ahmedhabib.com/libraries/download/php-advanced-patterns
6
Classes
50+
Methods
0
Dependencies
8.0+
PHP Version

PHP Advanced Patterns is a single-file, zero-dependency library for PHP 8.0+ that brings battle-tested software design patterns to your projects. Includes a composable Pipeline for middleware-style chaining, a monadic Result type (Ok/Err) for exception-free error handling, a full-featured EventEmitter for pub/sub architecture, a lightweight dependency injection Container with singleton support, a configurable Retry mechanism with exponential backoff, and a CircuitBreaker for fault-tolerant service calls. Drop it into any project and start building resilient, maintainable systems immediately.

Modern PHP applications need more than basic utilities — they need resilience patterns that keep systems running when things go wrong. PHP Advanced Patterns brings six battle-tested software design patterns into a single, zero-dependency file. Inspired by patterns from languages like Rust (Result types), Java (Circuit Breaker), and Node.js (EventEmitter), this library gives PHP developers production-grade tools for building fault-tolerant, maintainable architectures. Whether you're chaining middleware with Pipeline, replacing exception-heavy code with monadic Result types, decoupling components via events, or protecting external API calls with Circuit Breaker — these patterns work together seamlessly and drop into any PHP 8.0+ project without a single Composer dependency.

  • Pipeline 8 methods

    Composable data processing — create a pipeline from callables, add stages with pipe or multiple at once with through, process data through all stages sequentially, processSafe to catch exceptions as Err results, conditional stages with when, count stages, and static factories of/from.

  • Result / Ok / Err 12 methods

    Monadic error handling — Ok wraps success values, Err wraps errors, unwrap/unwrapOr/unwrapErr for value extraction, map transforms Ok values, mapErr transforms Err values, andThen for flat-mapping (chain operations returning Results), orElse for fallback chains, tap for side effects, toArray conversion, Result::try wraps callables catching exceptions, and Result::all collects multiple Results.

  • EventEmitter 8 methods

    Publish/subscribe events — register persistent listeners with on, one-time listeners with once (auto-removed after first call), emit events with arguments returning listener count, remove specific listeners with off, removeAll for one or all events, listenerCount, eventNames listing, and hasListeners check.

  • Container 9 methods

    Dependency injection — register factory bindings with bind (new instance each time), singleton for cached single-instance resolution, instance for pre-built values, alias to create alternative names, get to resolve by ID, has to check existence, forget to remove bindings, keys to list all registered IDs, and call to invoke callables with the container.

  • Retry 8 methods

    Configurable retry with backoff — set max attempts, initial delay in milliseconds, exponential backoff multiplier, max delay cap, conditional retryIf callback (receives exception and attempt number), onRetry hook for logging, run executes and returns a Result, and Retry::times static shorthand for quick retries with defaults.

  • CircuitBreaker 10 methods

    Fault tolerance — three-state machine (closed/open/half-open), call executes through the breaker returning a Result, configurable failure threshold to trip open, timeout before attempting recovery, success threshold to close again, state inspection with getState/isClosed/isOpen/isHalfOpen, getFailureCount, onStateChange callback, full stats array, and manual reset.

  1. Download PhpAdvancedPatterns.php
  2. Include or autoload it in your project
  3. Use the pattern classes: Pipeline, Result, Ok, Err, EventEmitter, Container, Retry, CircuitBreaker
require_once 'PhpAdvancedPatterns.php';

Pipeline (Composable Processing)

8 methods for chaining callables into elegant, reusable data processing flows

Pipeline::of / pipe / process
Create a pipeline, add stages, and process data through all of them.
$result = Pipeline::of(
    fn($s) => trim($s),
    fn($s) => strtolower($s),
    fn($s) => str_replace(' ', '-', $s),
)->process('  Hello World  ');
// "hello-world"
Pipeline::through / when
Add multiple stages at once, or conditionally include a stage.
$pipe = Pipeline::of()
    ->through(
        fn($n) => $n * 2,
        fn($n) => $n + 10,
    )
    ->when($addTax, fn($n) => $n * 1.2);

$pipe->process(5);
// 20 (no tax) or 24 (with tax)
Pipeline::processSafe
Process data and return a Result — exceptions become Err automatically.
$pipe = Pipeline::of(
    fn($x) => 100 / $x,
);

$pipe->processSafe(5);  // Ok(20)
$pipe->processSafe(0);  // Err("Division by zero")

Result / Ok / Err (Monadic Error Handling)

12 methods for exception-free error handling with chainable transformations

Ok::of / Err::of / Result::try
Create Ok/Err results directly or wrap a callable that might throw.
$ok  = Ok::of(42);
$err = Err::of('Not found');

$result = Result::try(fn() => json_decode($json, true,
    flags: JSON_THROW_ON_ERROR));
// Ok([...]) or Err("Syntax error")
Result::unwrap / unwrapOr / unwrapErr
Extract the value with a default fallback, or get the error message.
Ok::of(42)->unwrap();        // 42
Err::of('fail')->unwrapOr(0); // 0
Err::of('fail')->unwrapErr();  // "fail"

Ok::of(42)->isOk();   // true
Err::of('x')->isErr(); // true
Result::map / mapErr / andThen
Chain transformations — map runs on Ok, mapErr on Err, andThen for flat-mapping.
$result = Ok::of(5)
    ->map(fn($n) => $n * 2)        // Ok(10)
    ->andThen(fn($n) =>
        $n > 0 ? Ok::of($n) : Err::of('negative')
    );
// Ok(10)

Err::of('oops')
    ->mapErr(fn($e) => strtoupper($e));
// Err("OOPS")
Result::all / tap / toArray
Collect multiple Results, perform side effects, or convert to array.
Result::all([Ok::of(1), Ok::of(2), Ok::of(3)]);
// Ok([1, 2, 3])

Result::all([Ok::of(1), Err::of('bad')]);
// Err("bad")

Ok::of(42)->tap(fn($v) => log($v));
// Ok(42) — logged 42 as side effect

EventEmitter (Publish / Subscribe)

8 methods for decoupled event-driven communication between components

EventEmitter::on / emit
Register listeners for events and emit events with arguments.
$emitter = new EventEmitter();

$emitter->on('user.created', function($user) {
    sendWelcomeEmail($user);
});

$emitter->on('user.created', function($user) {
    logActivity('New user: ' . $user['name']);
});

$emitter->emit('user.created', $newUser);
// Both listeners called — returns 2
EventEmitter::once / off
One-time listeners auto-remove after first call. Remove specific listeners with off.
$emitter->once('db.connected', function() {
    echo 'Connected!';
});

$emitter->emit('db.connected'); // "Connected!"
$emitter->emit('db.connected'); // nothing (removed)
EventEmitter::listenerCount / eventNames / hasListeners
Inspect the emitter — count listeners, list events, check if events have handlers.
$emitter->on('click', $handler1);
$emitter->on('click', $handler2);
$emitter->on('hover', $handler3);

$emitter->listenerCount('click'); // 2
$emitter->eventNames();
// ["click", "hover"]

$emitter->hasListeners('click'); // true
$emitter->hasListeners('scroll'); // false

Container (Dependency Injection)

9 methods for managing service bindings, singletons, aliases, and resolution

Container::bind / get
Register a factory function and resolve it later. New instance each time.
$c = new Container();

$c->bind('logger', fn() => new FileLogger('/var/log'));

$log1 = $c->get('logger'); // new instance
$log2 = $c->get('logger'); // another new instance
// $log1 !== $log2
Container::singleton / instance
Singleton resolves once and caches. Instance registers a pre-built value.
$c->singleton('db', fn() => new PDO($dsn));
$db1 = $c->get('db');
$db2 = $c->get('db');
// $db1 === $db2 (same instance)

$c->instance('config', ['debug' => true]);
$c->get('config'); // ["debug" => true]
Container::alias / has / keys / call
Create aliases, check existence, list all bindings, and invoke with the container.
$c->alias('database', 'db');
$c->get('database'); // same as get('db')

$c->has('db');   // true
$c->keys();       // ["logger", "db", "config"]

$c->call(function($c) {
    return $c->get('db')->query('...');
});

Retry (Configurable Backoff)

8 methods for retrying failed operations with exponential backoff and conditions

Retry::of / attempts / delay / run
Configure retry attempts and initial delay, then run. Returns a Result.
$result = Retry::of()
    ->attempts(3)
    ->delay(200)  // 200ms initial delay
    ->run(function($attempt) {
        return callExternalApi('/data');
    });

$result->unwrapOr([]);
// API response or empty fallback
Retry::multiplier / maxDelay
Control exponential backoff: multiply delay each attempt, cap at a maximum.
Retry::of()
    ->attempts(5)
    ->delay(100)        // 100ms
    ->multiplier(2.0)   // 200ms, 400ms, 800ms...
    ->maxDelay(5000)    // capped at 5s
    ->run($operation);
// Delays: 100, 200, 400, 800, 1600ms
// (capped at 5000ms if exceeded)
Retry::retryIf / onRetry / times
Conditionally retry, hook into retry events, or use the quick static helper.
// Only retry on specific exceptions
Retry::of()->retryIf(
    fn($e) => $e instanceof TimeoutException
)->run($fn);

// Log each retry attempt
Retry::of()->onRetry(function($attempt, $e) {
    error_log("Retry #{$attempt}: {$e->getMessage()}");
})->run($fn);

// Quick helper
Retry::times(3, $fn); // 3 attempts, defaults

CircuitBreaker (Fault Tolerance)

10 methods for protecting external calls with automatic failure detection and recovery

CircuitBreaker::create / call
Create a breaker with thresholds and call through it. Returns a Result.
$breaker = CircuitBreaker::create(
    failureThreshold: 5,   // open after 5 failures
    timeoutSeconds: 60,     // wait 60s before retry
    successThreshold: 2,    // 2 successes to close
);

$result = $breaker->call(function() {
    return Http::get('https://api.example.com');
});

$result->unwrapOr(['fallback' => true]);
CircuitBreaker — State Machine
Three states: Closed (healthy), Open (tripped), Half-Open (testing recovery).
$breaker->getState();   // "closed"
$breaker->isClosed();   // true

// After 5 failures...
$breaker->isOpen();     // true
$breaker->call($fn);    // Err("Circuit breaker is open.")

// After timeout, state becomes half_open
// 2 successes → back to closed
$breaker->isHalfOpen(); // true (testing)
CircuitBreaker::onStateChange / stats / reset
Monitor state transitions, get full stats, or manually reset the breaker.
$breaker->onStateChange(function($from, $to) {
    alert("Circuit: {$from} → {$to}");
});

$breaker->stats();
// ["state" => "closed",
//  "failure_count" => 0,
//  "failure_threshold" => 5, ...]

$breaker->reset();      // force back to closed
$breaker->getFailureCount(); // 0
ESC