PHP code example of farzai / breaker

1. Go to this page and download the library: Download farzai/breaker library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

farzai / breaker example snippets


use Farzai\Breaker\Breaker;

// Simple one-liner protection
$result = Breaker::protect('api-service', function () {
    return callExternalApi();
});

// With automatic fallback
$result = Breaker::protect(
    'api-service',
    fn() => callExternalApi(),
    fallback: fn($e) => getCachedData()
);

// With custom configuration
$result = Breaker::protect(
    'api-service',
    fn() => callExternalApi(),
    config: ['failure_threshold' => 3, 'timeout' => 60]
);

use Farzai\Breaker\Breaker;

// Configure different services with different thresholds
Breaker::configure('critical-service', [
    'failure_threshold' => 2,
    'timeout' => 120,
]);

Breaker::configure('non-critical-service', [
    'failure_threshold' => 10,
    'timeout' => 30,
]);

// Use pre-configured services
$result = Breaker::protect('critical-service', fn() => callCriticalApi());

// Get health for all services
$reports = Breaker::healthReport();
foreach ($reports as $service => $health) {
    echo "{$service}: {$health->status->value}\n";
}

// Get health for specific service
$health = Breaker::healthReport('api-service');
echo "Status: {$health->status->value}\n";
echo "Failures: {$health->failureCount}/{$health->failureThreshold}\n";

use Farzai\Breaker\BreakerManager;

class ApiClient
{
    public function __construct(private BreakerManager $breaker) {}

    public function fetchData(): array
    {
        return $this->breaker->protect(
            'api-service',
            fn() => $this->callApi(),
            fallback: fn($e) => $this->getCached()
        );
    }
}

// In your DI container
$manager = new BreakerManager();
$apiClient = new ApiClient($manager);

use Farzai\Breaker\Breaker;
use Farzai\Breaker\Events\CircuitStateChangedEvent;
use Farzai\Breaker\Events\CircuitOpenedEvent;
use Farzai\Breaker\Events\CallSucceededEvent;
use Farzai\Breaker\Events\CallFailedEvent;

// Get the managed instance to add listeners
$breaker = Breaker::instance('api-service');

// Listen to state transitions with typed event objects
$breaker->onStateChange(function (CircuitStateChangedEvent $event) {
    echo sprintf(
        "Circuit state changed from %s to %s for service %s",
        $event->getPreviousState(),
        $event->getNewState(),
        $event->getServiceKey()
    );
});

// Listen to specific state transitions
$breaker->onOpen(function (CircuitOpenedEvent $event) {
    // Circuit opened - notify administrators with rich context
    sendAlertToAdmin([
        'service' => $event->getServiceKey(),
        'failure_count' => $event->getFailureCount(),
        'failure_threshold' => $event->getFailureThreshold(),
        'timeout' => $event->getTimeout(),
        'half_open_at' => date('Y-m-d H:i:s', $event->getHalfOpenTimestamp()),
    ]);
});

$breaker->onHalfOpen(function (CircuitHalfOpenedEvent $event) {
    logEvent("Testing if service is back online", [
        'success_threshold' => $event->getSuccessThreshold(),
    ]);
});

$breaker->onClose(function (CircuitClosedEvent $event) {
    // Check if this is a recovery or initial close
    $message = $event->isRecovery()
        ? 'Service recovered successfully'
        : 'Service initialized';
    sendStatusUpdate($message);
});

// Listen to call success with execution metrics
$breaker->onSuccess(function (CallSucceededEvent $event) {
    incrementMetric('service.success', [
        'execution_time' => $event->getExecutionTime(), // milliseconds
        'state' => $event->getCurrentState(),
        'result' => $event->getResult(),
    ]);
});

// Listen to failures with detailed error info
$breaker->onFailure(function (CallFailedEvent $event) {
    if ($event->willTriggerOpen()) {
        sendCriticalAlert('Circuit about to open!');
    }

    logError($event->getExceptionMessage(), [
        'exception_class' => $event->getExceptionClass(),
        'execution_time' => $event->getExecutionTime(),
        'failure_count' => $event->getFailureCount(),
        'threshold' => $event->getFailureThreshold(),
    ]);
});

// Remove a listener when no longer needed
$listenerId = $breaker->onSuccess(function ($event) { /* ... */ });
$breaker->removeListener($listenerId);

// High priority listener executes first
$breaker->onFailure(function (CallFailedEvent $event) {
    criticalErrorHandler($event);
}, priority: 100);

// Normal priority listener
$breaker->onFailure(function (CallFailedEvent $event) {
    logError($event);
}, priority: 0);

// Low priority listener executes last
$breaker->onFailure(function (CallFailedEvent $event) {
    sendMetrics($event);
}, priority: -10);

$breaker->onFailure(function (CallFailedEvent $event) {
    if ($event->willTriggerOpen()) {
        handleCriticalFailure($event);

        // Stop other failure listeners from executing
        $event->stopPropagation();
    }
});

use Farzai\Breaker\Events\EventSubscriberInterface;
use Farzai\Breaker\Events\CallSucceededEvent;
use Farzai\Breaker\Events\CallFailedEvent;
use Farzai\Breaker\Events\CircuitOpenedEvent;

class MetricsCollectorSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            CallSucceededEvent::class => 'onSuccess',
            CallFailedEvent::class => ['method' => 'onFailure', 'priority' => 100],
            CircuitOpenedEvent::class => 'onOpen',
        ];
    }

    public function onSuccess(CallSucceededEvent $event): void
    {
        $this->metrics->increment('circuit.success', [
            'service' => $event->getServiceKey(),
            'execution_time' => $event->getExecutionTime(),
        ]);
    }

    public function onFailure(CallFailedEvent $event): void
    {
        $this->metrics->increment('circuit.failure', [
            'service' => $event->getServiceKey(),
            'exception' => $event->getExceptionClass(),
        ]);
    }

    public function onOpen(CircuitOpenedEvent $event): void
    {
        $this->metrics->increment('circuit.opened', [
            'service' => $event->getServiceKey(),
        ]);
    }
}

// Add the subscriber to your circuit breaker
$breaker->addSubscriber(new MetricsCollectorSubscriber($metrics));

use Farzai\Breaker\Events\Subscribers\LoggingSubscriber;
use Psr\Log\LogLevel;

// With default log level (INFO)
$breaker->addSubscriber(new LoggingSubscriber($logger));

// With custom log level
$breaker->addSubscriber(new LoggingSubscriber($logger, LogLevel::WARNING));

use Farzai\Breaker\Events\Subscribers\MetricsSubscriber;
use Farzai\Breaker\Metrics\InMemoryMetricsCollector;

$metricsCollector = new InMemoryMetricsCollector();
$breaker->addSubscriber(new MetricsSubscriber($metricsCollector));

// Later, retrieve collected metrics
$metrics = $metricsCollector->getMetrics();
echo "Success rate: {$metrics->getSuccessRate()}%";

use Farzai\Breaker\Breaker;
use Farzai\Breaker\Storage\StorageFactory;

// Create file storage repository
$repository = StorageFactory::createRepository(
    StorageFactory::file(__DIR__ . '/storage/circuit-breaker')
);

// Set as default for all circuit breakers
Breaker::setDefaultRepository($repository);

// Now all protected calls use persistent storage
$result = Breaker::protect('api-service', fn() => callApi());

$repository = StorageFactory::createRepository(
    StorageFactory::file('/tmp/circuit-breaker')
);

$repository = StorageFactory::createRepository(
    StorageFactory::memory()
);

use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Psr16Cache;

$redis = RedisAdapter::createConnection('redis://localhost');
$cache = new Psr16Cache(new RedisAdapter($redis));

$repository = StorageFactory::createRepository(
    StorageFactory::psr16($cache, defaultTtl: 3600)
);

use Psr\Log\NullLogger;

// Use fluent builder for complex configurations
$storage = StorageFactory::builder('file', ['path' => '/tmp'])
    ->withLogging(new NullLogger())      // Add logging
    ->withRetry(maxAttempts: 3)          // Add retry logic
    ->build();

$repository = StorageFactory::createRepository($storage);

// Create fallback chain: Redis → File → Memory
$storage = StorageFactory::fallback([
    StorageFactory::psr16($redisCache),     // Primary: Redis
    StorageFactory::file('/tmp'),            // Secondary: File
    StorageFactory::memory(),                // Tertiary: Memory
]);

$repository = StorageFactory::createRepository($storage);

use Farzai\Breaker\Breaker;

// Configure each service based on criticality
Breaker::configure('user-service', [
    'failure_threshold' => 5,
    'timeout' => 30,
]);

Breaker::configure('payment-service', [
    'failure_threshold' => 2,  // Critical - fail fast
    'timeout' => 60,
]);

Breaker::configure('notification-service', [
    'failure_threshold' => 10,  // Non-critical - more lenient
    'timeout' => 15,
]);

// Critical service with stricter thresholds
Breaker::configure('critical-service', [
    'failure_threshold' => 2,      // Open after just 2 failures
    'timeout' => 60,               // Wait longer before testing again
    'success_threshold' => 3,      // Require more successes to restore
]);

// Non-critical service with more lenient settings
Breaker::configure('non-critical-service', [
    'failure_threshold' => 10,     // Allow more failures
    'timeout' => 15,               // Test again quickly
    'success_threshold' => 1,      // Close after first success
]);

use Farzai\Breaker\Breaker;

$result = Breaker::protect(
    'primary-api',
    fn() => fetchDataFromPrimarySource(),
    fallback: fn($e) => $e instanceof CircuitOpenException
        ? fetchFromCache()
        : fetchFromBackupService()
);

use Farzai\Breaker\Events\Subscribers\LoggingSubscriber;

// Use built-in logging subscriber for automatic logging
$breaker->addSubscriber(new LoggingSubscriber($logger));

// Or create custom monitoring with event listeners
$breaker->onStateChange(function (CircuitStateChangedEvent $event) use ($logger) {
    $logger->info("Circuit '{$event->getServiceKey()}' changed", [
        'previous_state' => $event->getPreviousState(),
        'new_state' => $event->getNewState(),
        'timestamp' => $event->getTimestamp(),
    ]);
});

// Alert on circuit open with rich context
$breaker->onOpen(function (CircuitOpenedEvent $event) use ($alertService) {
    $alertService->sendAlert([
        'title' => "Circuit breaker opened",
        'service' => $event->getServiceKey(),
        'failures' => "{$event->getFailureCount()}/{$event->getFailureThreshold()}",
        'retry_at' => date('Y-m-d H:i:s', $event->getHalfOpenTimestamp()),
        'severity' => 'critical',
    ]);
});

// Track execution metrics
$breaker->onSuccess(function (CallSucceededEvent $event) use ($metrics) {
    $metrics->histogram('circuit.execution_time', $event->getExecutionTime(), [
        'service' => $event->getServiceKey(),
        'state' => $event->getCurrentState(),
    ]);
});

use Farzai\Breaker\BreakerManager;
use Farzai\Breaker\Events\CallSucceededEvent;
use Farzai\Breaker\Events\CircuitOpenedEvent;
use Farzai\Breaker\Events\Subscribers\LoggingSubscriber;

class ApiClient
{
    private HttpClientInterface $httpClient;
    private BreakerManager $breaker;
    private CacheInterface $cache;
    private LoggerInterface $logger;

    public function __construct(
        HttpClientInterface $httpClient,
        BreakerManager $breaker,
        CacheInterface $cache,
        LoggerInterface $logger
    ) {
        $this->httpClient = $httpClient;
        $this->breaker = $breaker;
        $this->cache = $cache;
        $this->logger = $logger;

        // Set up monitoring with subscribers
        $this->setupMonitoring();
    }

    public function fetchData($id)
    {
        return $this->breaker->protect(
            'api-client',
            function () use ($id) {
                $response = $this->httpClient->get("/data/{$id}");

                // Cache successful responses
                $this->cache->set("data_{$id}", $response, 3600);

                return $response;
            },
            fallback: fn($exception) => $this->cache->get("data_{$id}")
        );
    }

    private function setupMonitoring(): void
    {
        $circuitBreaker = $this->breaker->instance('api-client');

        // Add built-in logging subscriber
        $circuitBreaker->addSubscriber(
            new LoggingSubscriber($this->logger)
        );

        // Add custom event listeners for specific actions
        $circuitBreaker->onSuccess(function (CallSucceededEvent $event) {
            if ($event->getExecutionTime() > 1000) {
                $this->logger->warning('Slow API response detected', [
                    'execution_time' => $event->getExecutionTime(),
                    'threshold' => 1000,
                ]);
            }
        });

        $circuitBreaker->onOpen(function (CircuitOpenedEvent $event) {
            // Send alert to monitoring system
            $this->sendAlert('API Circuit Opened', [
                'failures' => $event->getFailureCount(),
                'retry_at' => date('Y-m-d H:i:s', $event->getHalfOpenTimestamp()),
            ]);
        });
    }

    private function sendAlert(string $title, array $context): void
    {
        // Implementation for sending alerts
    }
}

use Farzai\Breaker\Events\EventDispatcher;

$breaker->getEventDispatcher()->setErrorHandlingStrategy(
    EventDispatcher::ERROR_STRATEGY_SILENT  // Log errors and continue (default)
    // EventDispatcher::ERROR_STRATEGY_COLLECT  // Collect errors for later inspection
    // EventDispatcher::ERROR_STRATEGY_STOP     // Stop on first error
);

// Even if a listener fails, the circuit breaker continues working
$breaker->onSuccess(function ($event) {
    throw new \Exception('Listener error!');
    // This won't break the circuit breaker
});

$breaker->onSuccess(function (CallSucceededEvent $event) {
    $executionTime = $event->getExecutionTime(); // in milliseconds

    if ($executionTime > 500) {
        $this->logger->warning('Slow response detected', [
            'service' => $event->getServiceKey(),
            'execution_time' => $executionTime,
        ]);
    }
});

$breaker->onFailure(function (CallFailedEvent $event) {
    // Track how long it took to fail
    $this->metrics->timing('failure_time', $event->getExecutionTime());
});

$breaker->onSuccess(function (CallSucceededEvent $event) {
    $result = $event->getResult();        // ✓ Reading is allowed
    $time = $event->getExecutionTime();   // ✓ Reading is allowed

    // Event properties are readonly - cannot be modified
});

use Farzai\Breaker\CircuitBreaker;
use Farzai\Breaker\Exceptions\CircuitOpenException;
use Farzai\Breaker\Storage\StorageFactory;

// Create with custom configuration and storage
$repository = StorageFactory::createRepository(
    StorageFactory::file(__DIR__ . '/storage/circuit-breaker')
);

$breaker = new CircuitBreaker(
    serviceKey: 'my-service',
    config: [
        'failure_threshold' => 5,
        'timeout' => 30,
        'success_threshold' => 2,
    ],
    repository: $repository,
    logger: $logger,  // Optional PSR-3 logger
);

// Use the circuit breaker
try {
    $result = $breaker->call(function () {
        return callExternalService();
    });
} catch (CircuitOpenException $e) {
    return getBackupData();
}

// Or with fallback
$result = $breaker->callWithFallback(
    fn() => callExternalService(),
    fn($e, $breaker) => getBackupData()
);