This capstone integrates all 14 previous labs into a cohesive production PHP platform. You'll build a complete system with: Fiber-based async scheduling, Event Sourcing, Circuit Breaker, libsodium request signing, OpenTelemetry tracing, a custom stream wrapper for config, a Reflection-based DI container, and full PHPUnit 10 test coverage — all verified end-to-end in Docker.
<?php
// src/Container.php
namespace Platform;
class Container {
private array $bindings = [];
private array $instances = [];
public function bind(string $abstract, callable $factory): void {
$this->bindings[$abstract] = $factory;
}
public function singleton(string $abstract, callable $factory): void {
$this->bindings[$abstract] = function() use ($abstract, $factory) {
return $this->instances[$abstract] ??= $factory($this);
};
}
public function make(string $class): object {
if (isset($this->bindings[$class])) {
return ($this->bindings[$class])($this);
}
return $this->autowire($class);
}
private function autowire(string $class): object {
$rc = new \ReflectionClass($class);
if (!$rc->isInstantiable()) {
throw new \RuntimeException("Cannot instantiate {$class}");
}
$ctor = $rc->getConstructor();
if (!$ctor || $ctor->getNumberOfParameters() === 0) {
return new $class();
}
$deps = array_map(function(\ReflectionParameter $param) use ($class) {
$type = $param->getType();
if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
return $this->make($type->getName());
}
if ($param->isDefaultValueAvailable()) {
return $param->getDefaultValue();
}
throw new \RuntimeException(
"Cannot resolve \${$param->getName()} for {$class}"
);
}, $ctor->getParameters());
return $rc->newInstanceArgs($deps);
}
}
<?php
// src/EventSourcing/DomainEvent.php
namespace Platform\EventSourcing;
abstract class DomainEvent {
public readonly string $eventId;
public readonly float $occurredAt;
public function __construct(
public readonly string $aggregateId,
public readonly int $version
) {
$this->eventId = bin2hex(random_bytes(8));
$this->occurredAt = microtime(true);
}
abstract public function toPayload(): array;
public function eventType(): string { return (new \ReflectionClass($this))->getShortName(); }
}
// src/EventSourcing/EventStore.php
namespace Platform\EventSourcing;
class EventStore {
private \PDO $pdo;
public function __construct(string $dsn = 'sqlite::memory:') {
$this->pdo = new \PDO($dsn);
$this->pdo->exec('CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_id TEXT UNIQUE,
event_type TEXT,
aggregate_id TEXT,
version INTEGER,
payload TEXT,
occurred_at REAL,
UNIQUE(aggregate_id, version)
)');
}
public function append(DomainEvent $event): void {
$this->pdo->prepare('INSERT INTO events
(event_id, event_type, aggregate_id, version, payload, occurred_at)
VALUES (?,?,?,?,?,?)')
->execute([
$event->eventId,
$event->eventType(),
$event->aggregateId,
$event->version,
json_encode($event->toPayload()),
$event->occurredAt,
]);
}
public function getEvents(string $aggregateId): array {
$stmt = $this->pdo->prepare('SELECT * FROM events WHERE aggregate_id=? ORDER BY version');
$stmt->execute([$aggregateId]);
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
public function count(): int {
return (int)$this->pdo->query('SELECT COUNT(*) FROM events')->fetchColumn();
}
}
<?php
// src/Resilience/CircuitBreaker.php
namespace Platform\Resilience;
enum CircuitState: string {
case Closed = 'closed';
case Open = 'open';
case HalfOpen = 'half-open';
}
class CircuitBreaker {
private CircuitState $state = CircuitState::Closed;
private int $failCount = 0;
private float $openedAt = 0.0;
public function __construct(
private readonly int $threshold = 5,
private readonly float $openDuration = 30.0
) {}
public function call(callable $fn): mixed {
if ($this->state === CircuitState::Open) {
if (microtime(true) - $this->openedAt > $this->openDuration) {
$this->state = CircuitState::HalfOpen;
} else {
throw new CircuitOpenException("Circuit is OPEN");
}
}
try {
$result = $fn();
if ($this->state === CircuitState::HalfOpen) {
$this->state = CircuitState::Closed;
$this->failCount = 0;
}
return $result;
} catch (\Throwable $e) {
$this->failCount++;
if ($this->failCount >= $this->threshold) {
$this->state = CircuitState::Open;
$this->openedAt = microtime(true);
}
throw $e;
}
}
public function getState(): CircuitState { return $this->state; }
public function getFailCount(): int { return $this->failCount; }
public function reset(): void { $this->state = CircuitState::Closed; $this->failCount = 0; }
}
class CircuitOpenException extends \RuntimeException {}
// src/Resilience/RetryDecorator.php
namespace Platform\Resilience;
class RetryDecorator {
public function __construct(
private readonly int $maxAttempts = 3,
private readonly float $baseDelayMs = 100.0,
private readonly float $multiplier = 2.0
) {}
public function execute(callable $fn): mixed {
$lastEx = null;
for ($i = 1; $i <= $this->maxAttempts; $i++) {
try {
return $fn();
} catch (\Throwable $e) {
$lastEx = $e;
if ($i < $this->maxAttempts) {
$delay = $this->baseDelayMs * ($this->multiplier ** ($i - 1));
usleep((int)($delay * 1000));
}
}
}
throw new \RuntimeException("Max retries exceeded: " . $lastEx->getMessage(), 0, $lastEx);
}
}