PHP Advanced Patterns
A zero-dependency PHP library implementing 6 advanced software patterns with 50+ methods: Pipeline, Result, EventEmitter, Container, Retry, and CircuitBreaker.
Download
curl -OL https://ahmedhabib.com/libraries/download/php-advanced-patterns
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.
- Download PhpAdvancedPatterns.php
- Include or autoload it in your project
- 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
$result = Pipeline::of(
fn($s) => trim($s),
fn($s) => strtolower($s),
fn($s) => str_replace(' ', '-', $s),
)->process(' Hello World ');
// "hello-world"$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)$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 = 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")
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 = 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([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
$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
$emitter->once('db.connected', function() { echo 'Connected!'; }); $emitter->emit('db.connected'); // "Connected!" $emitter->emit('db.connected'); // nothing (removed)
$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
$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
$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]
$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
$result = Retry::of()
->attempts(3)
->delay(200) // 200ms initial delay
->run(function($attempt) {
return callExternalApi('/data');
});
$result->unwrapOr([]);
// API response or empty fallbackRetry::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)// 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
$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]);$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)
$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