Files
2025-06-24 14:14:35 +02:00
..
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00
2025-06-24 14:14:35 +02:00

Circuit Breaker, an implementation for resilient PHP applications

codecov PHPStan Psalm Build Status

Main principles

circuit breaker

This library is compatible with PHP 5.6+.

Installation

composer require prestashop/circuit-breaker

Use

Simple Circuit Breaker

You can use the factory to create a simple circuit breaker.

By default, you need to define 3 parameters for the circuit breaker:

  • the failures: define how many times we try to access the service;
  • the timeout: define how much time we wait before consider the service unreachable;
  • the threshold: define how much time we wait before trying to access again the service (once it is considered unreachable);

The fallback callback will be used if the distant service is unreachable when the Circuit Breaker is Open (means "is used" if the service is unreachable).

You'd better return the same type of response expected from your distant call.

use PrestaShop\CircuitBreaker\SimpleCircuitBreakerFactory;
use PrestaShop\CircuitBreaker\FactorySettings;

$circuitBreakerFactory = new SimpleCircuitBreakerFactory();
$circuitBreaker = $circuitBreakerFactory->create(new FactorySettings(2, 0.1, 10));

$fallbackResponse = function () {
    return '{}';
};

$response = $circuitBreaker->call('https://api.domain.com', [], $fallbackResponse);

If you don't specify any fallback, by default the circuit breaker will return an empty string.

use PrestaShop\CircuitBreaker\SimpleCircuitBreakerFactory;
use PrestaShop\CircuitBreaker\FactorySettings;

$circuitBreakerFactory = new SimpleCircuitBreakerFactory();
$circuitBreaker = $circuitBreakerFactory->create(new FactorySettings(2, 0.1, 10));
$response = $circuitBreaker->call('https://unreacheable.api.domain.com', []); // $response == ''

You can also define the client options (or even set your own client if you prefer).

use PrestaShop\CircuitBreaker\SimpleCircuitBreakerFactory;
use PrestaShop\CircuitBreaker\FactorySettings;

$circuitBreakerFactory = new SimpleCircuitBreakerFactory();
$settings = new FactorySettings(2, 0.1, 10);
$settings->setClientOptions(['method' => 'POST']);
$circuitBreaker = $circuitBreakerFactory->create($settings);
$response = $circuitBreaker->call('https://api.domain.com/create/user', ['body' => ['firstname' => 'John', 'lastname' => 'Doe']]);

For the Guzzle implementation, the Client options are described in the HttpGuzzle documentation.

Advanced Circuit Breaker

If you need more control on your circuit breaker, you should use the AdvancedCircuitBreaker which manages more features:

  • the stripped failures: define how many times we try to access the service when the circuit breaker is Half Open (when it retires to reach the service after it was unreachable);
  • the stripped timeout: define how much time we wait before consider the service unreachable (again in Half open state);
  • the storage: used to store the circuit breaker states and transitions. By default it's an SimpleArray so if you want to "cache" the fact that your service is unreachable you should use a persistent storage;
  • the transition dispatcher: used if you need to subscribe to transition events (ex: a dispatcher based on Symfony EventDispatcher is available)

Storage

use Doctrine\Common\Cache\FilesystemCache;
use PrestaShop\CircuitBreaker\AdvancedCircuitBreakerFactory;
use PrestaShop\CircuitBreaker\FactorySettings;
use PrestaShop\CircuitBreaker\Storage\DoctrineCache;

$circuitBreakerFactory = new AdvancedCircuitBreakerFactory();
$settings = new FactorySettings(2, 0.1, 60); //60 seconds threshold

//Once the circuit breaker is open, the fallback response will be returned instantly during the next 60 seconds
//Since the state is persisted even other requests/processes will be aware that the circuit breaker is open
$doctrineCache = new FilesystemCache(_PS_CACHE_DIR_ . '/addons_category');
$storage = new DoctrineCache($doctrineCache);
$settings->setStorage($storage);

$circuitBreaker = $circuitBreakerFactory->create($settings);
$response = $circuitBreaker->call('https://unreachable.api.domain.com/create/user', []);

Guzzle Cache

Besides caching the circuit breaker state, it may be interesting to cache the successful responses. The circuit breaker library doesn't manage the cache itself, however you can use Guzzle Subscriber to manage it.

use Doctrine\Common\Cache\FilesystemCache;
use GuzzleHttp\Subscriber\Cache\CacheStorage;
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
use PrestaShop\CircuitBreaker\FactorySettings;
use PrestaShop\CircuitBreaker\SimpleCircuitBreakerFactory;

$circuitBreakerFactory = new SimpleCircuitBreakerFactory();
$settings = new FactorySettings(2, 0.1, 60); //60 seconds threshold

//Guzzle subsriber is also compatible with doctrine cache, which is why we proposed a storage based on it, this allows you to use for storage and cache
$doctrineCache = new FilesystemCache(_PS_CACHE_DIR_ . '/addons_category');
//By default the ttl is defined by the response headers but you can set a default one
$cacheStorage = new CacheStorage($doctrineCache, null, 60);
$cacheSubscriber = new CacheSubscriber($cacheStorage, function (Request $request) { return true; });

$settings->setClientOptions(['subscribers' => [$cacheSubscriber]]);

$circuitBreaker = $circuitBreakerFactory->create($settings);
$response = $circuitBreaker->call('https://api.domain.com/create/user', []);

Tests

composer test

Code quality

composer cs-fix && composer phpcb && composer psalm && composer phpcs

We also use PHPQA to check the Code quality during the CI management of the contributions.

If you want to use it (using Docker):

docker run --rm -u $UID -v $(pwd):/app eko3alpha/docker-phpqa --report --ignoredDirs vendor,tests

If you want to use it (using Composer):

composer global require edgedesign/phpqa=v1.20.0 --update-no-dev
phpqa --report --ignoredDirs vendor,tests