Timeout Pattern Generator
Creates Timeout pattern infrastructure for execution time limits with fallback support.
When to Use
| Scenario | Example |
|---|---|
| External API calls | HTTP requests to third-party services |
| Database queries | Long-running or unoptimized queries |
| Queue consumers | Message processing time limits |
| File operations | Large file uploads/downloads |
| Distributed calls | gRPC, SOAP, or REST inter-service calls |
| Batch processing | Individual item timeout within batch |
Component Characteristics
TimeoutConfig
- Immutable value object
- Duration in seconds (float for sub-second precision)
- Optional fallback callable
- Optional shouldRetry flag
- Named presets (fast, standard, slow)
TimeoutInterface
- Domain layer contract
- execute(callable operation, TimeoutConfig config): mixed
- Single responsibility: enforce time limit
TimeoutExecutor
- pcntl_alarm + async signal based for CLI
- Catches SIGALRM to throw TimeoutException
- Restores previous signal handler after execution
- Thread-safe via execution context
StreamTimeoutExecutor
- stream_set_timeout for I/O operations
- Works in both CLI and FPM
- Socket and stream resource support
TimeoutException
- Extends RuntimeException
- Contains elapsed time and operation context
- Machine-readable properties for monitoring
TimeoutMiddleware
- PSR-15 compatible for HTTP clients
- Adds timeout to outgoing requests
- Configurable per-route timeouts
Generation Process
Step 1: Analyze Request
Determine:
- Target use case (API calls, DB queries, queue consumers)
- Timeout strategy (signal-based, stream-based, or both)
- Fallback behavior (exception, default value, cached result)
Step 2: Generate Core Components
Domain Layer (
src/Domain/Shared/Timeout/)TimeoutConfig.php— Configuration value objectTimeoutInterface.php— Execution contractTimeoutException.php— Timeout exceeded exception
Infrastructure Layer (
src/Infrastructure/Resilience/Timeout/)SignalTimeoutExecutor.php— pcntl_alarm based implementationStreamTimeoutExecutor.php— stream_set_timeout basedNullTimeoutExecutor.php— No-op for testingTimeoutExecutorFactory.php— Environment-aware factory
Presentation Layer (
src/Presentation/Middleware/)TimeoutMiddleware.php— PSR-15 HTTP middleware
Tests
TimeoutConfigTest.phpSignalTimeoutExecutorTest.phpTimeoutExceptionTest.php
File Placement
| Layer | Path |
|---|---|
| Domain Types | src/Domain/Shared/Timeout/ |
| Infrastructure | src/Infrastructure/Resilience/Timeout/ |
| Middleware | src/Presentation/Middleware/ |
| Unit Tests | tests/Unit/{Layer}/{Path}/ |
Key Principles
Signal-Based Timeout (CLI)
- Register SIGALRM handler
- Set pcntl_alarm with timeout duration
- Execute operation
- Cancel alarm on completion
- Restore previous handler
- If alarm fires → throw TimeoutException
Stream-Based Timeout (FPM/CLI)
- Set stream_set_timeout on resource
- Check stream_get_meta_data for timed_out flag
- Works for network I/O operations
Fallback Strategy
- On timeout, check if fallback provided
- Execute fallback within separate timeout (prevent cascading)
- If no fallback, throw TimeoutException
- Log timeout event with context
Naming Conventions
| Component | Pattern | Example |
|---|---|---|
| Config VO | TimeoutConfig |
TimeoutConfig |
| Interface | TimeoutInterface |
TimeoutInterface |
| Signal Impl | SignalTimeoutExecutor |
SignalTimeoutExecutor |
| Stream Impl | StreamTimeoutExecutor |
StreamTimeoutExecutor |
| Null Impl | NullTimeoutExecutor |
NullTimeoutExecutor |
| Exception | TimeoutException |
TimeoutException |
| Factory | TimeoutExecutorFactory |
TimeoutExecutorFactory |
| Middleware | TimeoutMiddleware |
TimeoutMiddleware |
| Test | {ClassName}Test |
SignalTimeoutExecutorTest |
Quick Template Reference
TimeoutConfig
final readonly class TimeoutConfig
{
public function __construct(
public float $durationSeconds,
public ?callable $fallback = null,
public bool $shouldRetry = false,
public string $operationName = 'unknown',
) {}
public static function fast(): self; // 3 seconds
public static function standard(): self; // 10 seconds
public static function slow(): self; // 30 seconds
public static function of(float $seconds): self;
}
TimeoutInterface
interface TimeoutInterface
{
/**
* @template T
* @param callable(): T $operation
* @return T
* @throws TimeoutException
*/
public function execute(callable $operation, TimeoutConfig $config): mixed;
}
TimeoutException
final class TimeoutException extends \RuntimeException
{
public function __construct(
public readonly float $elapsedSeconds,
public readonly float $timeoutSeconds,
public readonly string $operationName,
?\Throwable $previous = null,
) {
parent::__construct(
sprintf('Operation "%s" timed out after %.2fs (limit: %.2fs)', $operationName, $elapsedSeconds, $timeoutSeconds),
0,
$previous,
);
}
}
Usage Example
$timeout = $timeoutFactory->create();
try {
$result = $timeout->execute(
operation: fn() => $httpClient->request('GET', $url),
config: TimeoutConfig::of(5.0),
);
} catch (TimeoutException $e) {
$logger->warning('API call timed out', [
'operation' => $e->operationName,
'elapsed' => $e->elapsedSeconds,
'timeout' => $e->timeoutSeconds,
]);
return $cachedResult;
}
Composition with Circuit Breaker
$result = $circuitBreaker->execute(
operation: fn() => $timeout->execute(
operation: fn() => $api->call($request),
config: TimeoutConfig::of(5.0),
),
fallback: fn() => $cache->get($cacheKey),
);
Anti-patterns to Avoid
| Anti-pattern | Problem | Solution |
|---|---|---|
| No timeout | Indefinite blocking | Always set explicit timeout |
| Too aggressive | Fail on normal slow responses | Tune per-operation |
| Global timeout | One size doesn't fit all | Per-operation config |
| No fallback | Hard failure on timeout | Provide degraded response |
| Ignoring context | Can't debug timeouts | Include operation name |
| Nested timeouts | Outer timeout kills inner | Coordinate timeout budgets |
References
For complete PHP templates and examples, see:
references/templates.md— All component templatesreferences/examples.md— API call, DB query examples and tests
Converted and distributed by TomeVault — claim your Tome and manage your conversions.