first commit

This commit is contained in:
2024-12-17 13:43:22 +01:00
commit 8e6cd8b410
21292 changed files with 3514826 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\SystemInterface;
use PrestaShop\CircuitBreaker\Contract\TransitionDispatcherInterface;
use PrestaShop\CircuitBreaker\Exception\UnavailableServiceException;
/**
* This implementation of the CircuitBreaker is a bit more advanced than the SimpleCircuitBreaker,
* it allows you to setup your client, system, storage and dispatcher.
*/
class AdvancedCircuitBreaker extends PartialCircuitBreaker
{
/** @var TransitionDispatcherInterface */
protected $dispatcher;
/** @var callable|null */
protected $defaultFallback;
/**
* @param SystemInterface $system
* @param ClientInterface $client
* @param StorageInterface $storage
* @param TransitionDispatcherInterface $dispatcher
*/
public function __construct(
SystemInterface $system,
ClientInterface $client,
StorageInterface $storage,
TransitionDispatcherInterface $dispatcher
) {
parent::__construct($system, $client, $storage);
$this->dispatcher = $dispatcher;
}
/**
* {@inheritdoc}
*/
public function call(
$service,
array $serviceParameters = [],
callable $fallback = null
) {
$transaction = $this->initTransaction($service);
try {
if ($this->isOpened()) {
if (!$this->canAccessService($transaction)) {
return $this->callFallback($fallback);
}
$this->moveStateTo(State::HALF_OPEN_STATE, $service);
$this->dispatchTransition(
Transition::CHECKING_AVAILABILITY_TRANSITION,
$service,
$serviceParameters
);
}
$response = $this->request($service, $serviceParameters);
$this->moveStateTo(State::CLOSED_STATE, $service);
$this->dispatchTransition(
Transition::CLOSING_TRANSITION,
$service,
$serviceParameters
);
return $response;
} catch (UnavailableServiceException $exception) {
$transaction->incrementFailures();
$this->storage->saveTransaction($service, $transaction);
if (!$this->isAllowedToRetry($transaction)) {
$this->moveStateTo(State::OPEN_STATE, $service);
$transition = $this->isHalfOpened() ? Transition::REOPENING_TRANSITION : Transition::OPENING_TRANSITION;
$this->dispatchTransition($transition, $service, $serviceParameters);
return $this->callFallback($fallback);
}
return $this->call(
$service,
$serviceParameters,
$fallback
);
}
}
/**
* @return callable|null
*/
public function getDefaultFallback()
{
return $this->defaultFallback;
}
/**
* @param callable $defaultFallback|null
*
* @return AdvancedCircuitBreaker
*/
public function setDefaultFallback(callable $defaultFallback = null)
{
$this->defaultFallback = $defaultFallback;
return $this;
}
/**
* {@inheritdoc}
*/
protected function callFallback(callable $fallback = null)
{
return parent::callFallback(null !== $fallback ? $fallback : $this->defaultFallback);
}
/**
* @param string $transition
* @param string $service
* @param array $serviceParameters
*
* @return void
*/
protected function dispatchTransition($transition, $service, array $serviceParameters)
{
$this->dispatcher->dispatchTransition($transition, $service, $serviceParameters);
}
/**
* {@inheritdoc}
*/
protected function initTransaction($service)
{
if (!$this->storage->hasTransaction($service)) {
$this->dispatchTransition(Transition::INITIATING_TRANSITION, $service, []);
}
return parent::initTransaction($service);
}
/**
* {@inheritdoc}
*/
protected function request($service, array $parameters = [])
{
$this->dispatchTransition(Transition::TRIAL_TRANSITION, $service, $parameters);
return parent::request($service, $parameters);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\FactoryInterface;
use PrestaShop\CircuitBreaker\Contract\FactorySettingsInterface;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\TransitionDispatcherInterface;
use PrestaShop\CircuitBreaker\Place\ClosedPlace;
use PrestaShop\CircuitBreaker\Place\HalfOpenPlace;
use PrestaShop\CircuitBreaker\Place\OpenPlace;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Storage\SimpleArray;
use PrestaShop\CircuitBreaker\System\MainSystem;
use PrestaShop\CircuitBreaker\Transition\NullDispatcher;
/**
* Advanced implementation of Circuit Breaker Factory
* Used to create an AdvancedCircuitBreaker instance.
*/
final class AdvancedCircuitBreakerFactory implements FactoryInterface
{
/**
* {@inheritdoc}
*/
public function create(FactorySettingsInterface $settings)
{
$closedPlace = new ClosedPlace($settings->getFailures(), $settings->getTimeout(), 0);
$openPlace = new OpenPlace(0, 0, $settings->getThreshold());
$halfOpenPlace = new HalfOpenPlace($settings->getFailures(), $settings->getStrippedTimeout(), 0);
$system = new MainSystem($closedPlace, $halfOpenPlace, $openPlace);
/** @var ClientInterface $client */
$client = $settings->getClient() ?: new GuzzleClient($settings->getClientOptions());
/** @var StorageInterface $storage */
$storage = $settings->getStorage() ?: new SimpleArray();
/** @var TransitionDispatcherInterface $dispatcher */
$dispatcher = $settings->getDispatcher() ?: new NullDispatcher();
$circuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$storage,
$dispatcher
);
if (null !== $settings->getDefaultFallback()) {
$circuitBreaker->setDefaultFallback($settings->getDefaultFallback());
}
return $circuitBreaker;
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace PrestaShop\CircuitBreaker\Client;
use Exception;
use GuzzleHttp\Client as OriginalGuzzleClient;
use GuzzleHttp\Subscriber\Mock;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Exception\UnavailableServiceException;
use PrestaShop\CircuitBreaker\Exception\UnsupportedMethodException;
/**
* Guzzle implementation of client.
* The possibility of extending this client is intended.
*/
class GuzzleClient implements ClientInterface
{
/**
* @var string by default, calls are sent using GET method
*/
const DEFAULT_METHOD = 'GET';
/**
* Supported HTTP methods
*/
const SUPPORTED_METHODS = [
'GET' => true,
'HEAD' => true,
'POST' => true,
'PUT' => true,
'DELETE' => true,
'OPTIONS' => true,
];
/**
* @var array the Client default options
*/
private $defaultOptions;
public function __construct(array $defaultOptions = [])
{
$this->defaultOptions = $defaultOptions;
}
/**
* {@inheritdoc}
*
* @throws UnavailableServiceException
*/
public function request($resource, array $options)
{
try {
$options = array_merge($this->defaultOptions, $options);
$client = $this->buildClient($options);
$method = $this->getHttpMethod($options);
$options['exceptions'] = true;
// prevents unhandled method errors in Guzzle 5
unset($options['method'], $options['mock']);
$request = $client->createRequest($method, $resource, $options);
return (string) $client->send($request)->getBody();
} catch (Exception $e) {
throw new UnavailableServiceException($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* @param array $options the list of options
*
* @return string the method
*
* @throws UnsupportedMethodException
*/
private function getHttpMethod(array $options)
{
if (isset($options['method'])) {
if (!array_key_exists($options['method'], self::SUPPORTED_METHODS)) {
throw UnsupportedMethodException::unsupportedMethod($options['method']);
}
return $options['method'];
}
return self::DEFAULT_METHOD;
}
/**
* @param array $options
*
* @return OriginalGuzzleClient
*/
private function buildClient(array $options)
{
if (isset($options['mock']) && $options['mock'] instanceof Mock) {
return $this->buildMockClient($options);
}
return new OriginalGuzzleClient($options);
}
/**
* Builds a client with a mock
*
* @param array $options
*
* @return OriginalGuzzleClient
*/
private function buildMockClient(array $options)
{
/** @var Mock $mock */
$mock = $options['mock'];
unset($options['mock']);
$client = new OriginalGuzzleClient($options);
$client->getEmitter()->attach($mock);
return $client;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace PrestaShop\CircuitBreaker\Contract;
/**
* A circuit breaker is used to provide
* an alternative response when a tiers service
* is unreachable.
*/
interface CircuitBreakerInterface
{
/**
* @return string the circuit breaker state
*/
public function getState();
/**
* The function that execute the service.
*
* @param string $service the service to call
* @param array $parameters the parameters for the request
* @param callable|null $fallback if the service is unavailable, rely on the fallback
*
* @return string
*/
public function call($service, array $parameters = [], callable $fallback = null);
/**
* @return bool checks if the circuit breaker is open
*/
public function isOpened();
/**
* @return bool checks if the circuit breaker is half open
*/
public function isHalfOpened();
/**
* @return bool checks if the circuit breaker is closed
*/
public function isClosed();
}

View File

@@ -0,0 +1,18 @@
<?php
namespace PrestaShop\CircuitBreaker\Contract;
/**
* In charge of calling the resource and return a response.
* Must throw UnavailableService exception if not reachable.
*/
interface ClientInterface
{
/**
* @param string $resource the URI of the service to be reached
* @param array $options the options if needed
*
* @return string
*/
public function request($resource, array $options);
}

View File

@@ -0,0 +1,16 @@
<?php
namespace PrestaShop\CircuitBreaker\Contract;
/**
* Ease the creation of the Circuit Breaker.
*/
interface FactoryInterface
{
/**
* @param FactorySettingsInterface $settings the settings for the Place
*
* @return CircuitBreakerInterface
*/
public function create(FactorySettingsInterface $settings);
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\CircuitBreaker\Contract;
/**
* Interface FactorySettingsInterface contains the settings used by the Factory
*/
interface FactorySettingsInterface
{
/**
* @param FactorySettingsInterface $settingsA
* @param FactorySettingsInterface $settingsB
*
* @return FactorySettingsInterface
*/
public static function merge(FactorySettingsInterface $settingsA, FactorySettingsInterface $settingsB);
/**
* @return int
*/
public function getFailures();
/**
* @return float
*/
public function getTimeout();
/**
* @return int
*/
public function getThreshold();
/**
* @return float
*/
public function getStrippedTimeout();
/**
* @return int
*/
public function getStrippedFailures();
/**
* @return StorageInterface|null
*/
public function getStorage();
/**
* @return TransitionDispatcherInterface|null
*/
public function getDispatcher();
/**
* @return array
*/
public function getClientOptions();
/**
* @return ClientInterface|null
*/
public function getClient();
/**
* @return callable|null
*/
public function getDefaultFallback();
}

View File

@@ -0,0 +1,33 @@
<?php
namespace PrestaShop\CircuitBreaker\Contract;
/**
* A circuit breaker can be in 3 places:
* closed, half open or open. Each place have its
* own properties and behaviors.
*/
interface PlaceInterface
{
/**
* Return the current state of the Circuit Breaker.
*
* @return string
*/
public function getState();
/**
* @return int the number of failures
*/
public function getFailures();
/**
* @return int the allowed number of trials
*/
public function getThreshold();
/**
* @return float the allowed timeout
*/
public function getTimeout();
}

View File

@@ -0,0 +1,49 @@
<?php
namespace PrestaShop\CircuitBreaker\Contract;
use PrestaShop\CircuitBreaker\Exception\TransactionNotFoundException;
/**
* Store the transaction between the Circuit Breaker
* and the tiers service.
*/
interface StorageInterface
{
/**
* Save the CircuitBreaker transaction.
*
* @param string $service The service name
* @param TransactionInterface $transaction the transaction
*
* @return bool
*/
public function saveTransaction($service, TransactionInterface $transaction);
/**
* Retrieve the CircuitBreaker transaction for a specific service.
*
* @param string $service the service name
*
* @return TransactionInterface
*
* @throws TransactionNotFoundException
*/
public function getTransaction($service);
/**
* Checks if the transaction exists.
*
* @param string $service the service name
*
* @return bool
*/
public function hasTransaction($service);
/**
* Clear the Circuit Breaker storage.
*
* @return bool
*/
public function clear();
}

View File

@@ -0,0 +1,20 @@
<?php
namespace PrestaShop\CircuitBreaker\Contract;
/**
* The System define the places available
* for the Circuit Breaker and the initial Place.
*/
interface SystemInterface
{
/**
* @return PlaceInterface[] the list of places of the system
*/
public function getPlaces();
/**
* @return PlaceInterface the initial place of the system
*/
public function getInitialPlace();
}

View File

@@ -0,0 +1,40 @@
<?php
namespace PrestaShop\CircuitBreaker\Contract;
use DateTime;
/**
* Once the circuit breaker call a service,
* a transaction is initialized and stored.
*/
interface TransactionInterface
{
/**
* @return string the service name
*/
public function getService();
/**
* @return int the number of failures to call the service
*/
public function getFailures();
/**
* @return string the current state of the Circuit Breaker
*/
public function getState();
/**
* @return DateTime the time when the circuit breaker move
* from open to half open state
*/
public function getThresholdDateTime();
/**
* Everytime the service call fails, increment the number of failures.
*
* @return bool
*/
public function incrementFailures();
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\CircuitBreaker\Contract;
/**
* This interface is used for a circuit breaker to dispatch its state transitions.
*/
interface TransitionDispatcherInterface
{
/**
* @param string $transition the transition name
* @param string $service the Service URI
* @param array $serviceParameters the Service parameters
*
* @return void
*/
public function dispatchTransition($transition, $service, array $serviceParameters);
}

View File

@@ -0,0 +1,59 @@
<?php
namespace PrestaShop\CircuitBreaker\Event;
use Symfony\Component\EventDispatcher\Event;
class TransitionEvent extends Event
{
/**
* @var string the Transition name
*/
private $eventName;
/**
* @var string the Service URI
*/
private $service;
/**
* @var array the Service parameters
*/
private $parameters;
/**
* @param string $eventName the transition name
* @param string $service the Service URI
* @param array $parameters the Service parameters
*/
public function __construct($eventName, $service, array $parameters)
{
$this->eventName = $eventName;
$this->service = $service;
$this->parameters = $parameters;
}
/**
* @return string the Transition name
*/
public function getEvent()
{
return $this->eventName;
}
/**
* @return string the Service URI
*/
public function getService()
{
return $this->service;
}
/**
* @return array the Service parameters
*/
public function getParameters()
{
return $this->parameters;
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace PrestaShop\CircuitBreaker\Exception;
use Exception;
/**
* Base exception for Circuit Breaker exceptions
*/
class CircuitBreakerException extends Exception
{
}

View File

@@ -0,0 +1,26 @@
<?php
namespace PrestaShop\CircuitBreaker\Exception;
use PrestaShop\CircuitBreaker\Util\ErrorFormatter;
final class InvalidPlaceException extends CircuitBreakerException
{
/**
* @param mixed $failures the failures
* @param mixed $timeout the timeout
* @param mixed $threshold the threshold
*
* @return self
*/
public static function invalidSettings($failures, $timeout, $threshold)
{
$exceptionMessage = 'Invalid settings for Place' . PHP_EOL .
ErrorFormatter::format('failures', $failures, 'isPositiveInteger', 'a positive integer') .
ErrorFormatter::format('timeout', $timeout, 'isPositiveValue', 'a float') .
ErrorFormatter::format('threshold', $threshold, 'isPositiveInteger', 'a positive integer')
;
return new self($exceptionMessage);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace PrestaShop\CircuitBreaker\Exception;
use PrestaShop\CircuitBreaker\Util\ErrorFormatter;
final class InvalidTransactionException extends CircuitBreakerException
{
/**
* @param mixed $service the service URI
* @param mixed $failures the failures
* @param mixed $state the Circuit Breaker
* @param mixed $threshold the threshold
*
* @return self
*/
public static function invalidParameters($service, $failures, $state, $threshold)
{
$exceptionMessage = 'Invalid parameters for Transaction' . PHP_EOL .
ErrorFormatter::format('service', $service, 'isURI', 'an URI') .
ErrorFormatter::format('failures', $failures, 'isPositiveInteger', 'a positive integer') .
ErrorFormatter::format('state', $state, 'isString', 'a string') .
ErrorFormatter::format('threshold', $threshold, 'isPositiveInteger', 'a positive integer')
;
return new self($exceptionMessage);
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace PrestaShop\CircuitBreaker\Exception;
final class TransactionNotFoundException extends CircuitBreakerException
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace PrestaShop\CircuitBreaker\Exception;
final class UnavailableServiceException extends CircuitBreakerException
{
}

View File

@@ -0,0 +1,19 @@
<?php
namespace PrestaShop\CircuitBreaker\Exception;
/**
* Used when trying to use an unsupported HTTP method
*/
class UnsupportedMethodException extends CircuitBreakerException
{
/**
* @param string $methodName
*
* @return UnsupportedMethodException
*/
public static function unsupportedMethod($methodName)
{
return new static(sprintf('Unsupported method: "%s"', $methodName));
}
}

View File

@@ -0,0 +1,313 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\FactorySettingsInterface;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\TransitionDispatcherInterface;
/**
* Class FactorySettings is a simple implementation of FactorySettingsInterface, it is mainly
* a settings container and can be used with any Factory class.
*/
class FactorySettings implements FactorySettingsInterface
{
/** @var int */
private $failures;
/** @var float */
private $timeout;
/** @var int */
private $threshold;
/** @var float */
private $strippedTimeout;
/** @var int */
private $strippedFailures;
/** @var StorageInterface|null */
private $storage;
/** @var TransitionDispatcherInterface|null */
private $dispatcher;
/** @var array */
private $clientOptions = [];
/** @var ClientInterface|null */
private $client;
/** @var callable|null */
private $defaultFallback;
/**
* @param int $failures
* @param float $timeout
* @param int $threshold
*/
public function __construct(
$failures,
$timeout,
$threshold
) {
$this->failures = $this->strippedFailures = $failures;
$this->timeout = $this->strippedTimeout = $timeout;
$this->threshold = $threshold;
}
/**
* {@inheritdoc}
*/
public static function merge(FactorySettingsInterface $settingsA, FactorySettingsInterface $settingsB)
{
$mergedSettings = new FactorySettings(
$settingsB->getFailures(),
$settingsB->getTimeout(),
$settingsB->getThreshold()
);
$mergedSettings
->setStrippedFailures($settingsB->getStrippedFailures())
->setStrippedTimeout($settingsB->getStrippedTimeout())
;
$mergedSettings->setClientOptions(array_merge(
$settingsA->getClientOptions(),
$settingsB->getClientOptions()
));
if (null !== $settingsB->getClient()) {
$mergedSettings->setClient($settingsB->getClient());
} elseif (null !== $settingsA->getClient()) {
$mergedSettings->setClient($settingsA->getClient());
}
return $mergedSettings;
}
/**
* {@inheritdoc}
*/
public function getFailures()
{
return $this->failures;
}
/**
* @param int $failures
*
* @return FactorySettings
*/
public function setFailures($failures)
{
$this->failures = $failures;
return $this;
}
/**
* {@inheritdoc}
*/
public function getTimeout()
{
return $this->timeout;
}
/**
* @param float $timeout
*
* @return FactorySettings
*/
public function setTimeout($timeout)
{
$this->timeout = $timeout;
return $this;
}
/**
* {@inheritdoc}
*/
public function getThreshold()
{
return $this->threshold;
}
/**
* @param int $threshold
*
* @return FactorySettings
*/
public function setThreshold($threshold)
{
$this->threshold = $threshold;
return $this;
}
/**
* {@inheritdoc}
*/
public function getStrippedTimeout()
{
return $this->strippedTimeout;
}
/**
* @param float $strippedTimeout
*
* @return FactorySettings
*/
public function setStrippedTimeout($strippedTimeout)
{
$this->strippedTimeout = $strippedTimeout;
return $this;
}
/**
* @return int
*/
public function getStrippedFailures()
{
return $this->strippedFailures;
}
/**
* @param int $strippedFailures
*
* @return FactorySettings
*/
public function setStrippedFailures($strippedFailures)
{
$this->strippedFailures = $strippedFailures;
return $this;
}
/**
* {@inheritdoc}
*/
public function getStorage()
{
return $this->storage;
}
/**
* @param StorageInterface $storage
*
* @return FactorySettings
*/
public function setStorage(StorageInterface $storage)
{
$this->storage = $storage;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDispatcher()
{
return $this->dispatcher;
}
/**
* @param TransitionDispatcherInterface $dispatcher
*
* @return FactorySettings
*/
public function setDispatcher(TransitionDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
return $this;
}
/**
* {@inheritdoc}
*/
public function getClientOptions()
{
return $this->clientOptions;
}
/**
* @param array $clientOptions
*
* @return FactorySettings
*/
public function setClientOptions(array $clientOptions)
{
$this->clientOptions = $clientOptions;
return $this;
}
/**
* {@inheritdoc}
*/
public function getClient()
{
return $this->client;
}
/**
* @param ClientInterface|null $client
*
* @return FactorySettings
*/
public function setClient(ClientInterface $client = null)
{
$this->client = $client;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDefaultFallback()
{
return $this->defaultFallback;
}
/**
* @param callable $defaultFallback
*
* @return FactorySettings
*/
public function setDefaultFallback(callable $defaultFallback)
{
$this->defaultFallback = $defaultFallback;
return $this;
}
}

View File

@@ -0,0 +1,183 @@
<?php
namespace PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\Transaction\SimpleTransaction;
use PrestaShop\CircuitBreaker\Contract\CircuitBreakerInterface;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\SystemInterface;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
use DateTime;
abstract class PartialCircuitBreaker implements CircuitBreakerInterface
{
/**
* @param SystemInterface $system
* @param ClientInterface $client
* @param StorageInterface $storage
*/
public function __construct(
SystemInterface $system,
ClientInterface $client,
StorageInterface $storage
) {
$this->currentPlace = $system->getInitialPlace();
$this->places = $system->getPlaces();
$this->client = $client;
$this->storage = $storage;
}
/**
* @var ClientInterface the Client that consumes the service URI
*/
protected $client;
/**
* @var PlaceInterface the current Place of the Circuit Breaker
*/
protected $currentPlace;
/**
* @var PlaceInterface[] the Circuit Breaker places
*/
protected $places = [];
/**
* @var StorageInterface the Circuit Breaker storage
*/
protected $storage;
/**
* {@inheritdoc}
*/
abstract public function call($service, array $serviceParameters = [], callable $fallback = null);
/**
* {@inheritdoc}
*/
public function getState()
{
return $this->currentPlace->getState();
}
/**
* {@inheritdoc}
*/
public function isOpened()
{
return State::OPEN_STATE === $this->currentPlace->getState();
}
/**
* {@inheritdoc}
*/
public function isHalfOpened()
{
return State::HALF_OPEN_STATE === $this->currentPlace->getState();
}
/**
* {@inheritdoc}
*/
public function isClosed()
{
return State::CLOSED_STATE === $this->currentPlace->getState();
}
/**
* @param callable|null $fallback
*
* @return string
*/
protected function callFallback(callable $fallback = null)
{
if (null === $fallback) {
return '';
}
return call_user_func($fallback);
}
/**
* @param string $state the Place state
* @param string $service the service URI
*
* @return bool
*/
protected function moveStateTo($state, $service)
{
$this->currentPlace = $this->places[$state];
$transaction = SimpleTransaction::createFromPlace(
$this->currentPlace,
$service
);
return $this->storage->saveTransaction($service, $transaction);
}
/**
* @param string $service the service URI
*
* @return TransactionInterface
*/
protected function initTransaction($service)
{
if ($this->storage->hasTransaction($service)) {
$transaction = $this->storage->getTransaction($service);
// CircuitBreaker needs to be in the same state as its last transaction
if ($this->getState() !== $transaction->getState()) {
$this->currentPlace = $this->places[$transaction->getState()];
}
} else {
$transaction = SimpleTransaction::createFromPlace(
$this->currentPlace,
$service
);
$this->storage->saveTransaction($service, $transaction);
}
return $transaction;
}
/**
* @param TransactionInterface $transaction the Transaction
*
* @return bool
*/
protected function isAllowedToRetry(TransactionInterface $transaction)
{
return $transaction->getFailures() < $this->currentPlace->getFailures();
}
/**
* @param TransactionInterface $transaction the Transaction
*
* @return bool
*/
protected function canAccessService(TransactionInterface $transaction)
{
return $transaction->getThresholdDateTime() < new DateTime();
}
/**
* Calls the client with the right information.
*
* @param string $service the service URI
* @param array $parameters the service URI parameters
*
* @return string
*/
protected function request($service, array $parameters = [])
{
return $this->client->request(
$service,
array_merge($parameters, [
'connect_timeout' => $this->currentPlace->getTimeout(),
'timeout' => $this->currentPlace->getTimeout(),
])
);
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace PrestaShop\CircuitBreaker\Place;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
use PrestaShop\CircuitBreaker\Exception\InvalidPlaceException;
use PrestaShop\CircuitBreaker\Util\Assert;
abstract class AbstractPlace implements PlaceInterface
{
/**
* @var int the Place failures
*/
private $failures;
/**
* @var float the Place timeout
*/
private $timeout;
/**
* @var int the Place threshold
*/
private $threshold;
/**
* @param int $failures the Place failures
* @param float $timeout the Place timeout
* @param int $threshold the Place threshold
*
* @throws InvalidPlaceException
*/
public function __construct($failures, $timeout, $threshold)
{
$this->validate($failures, $timeout, $threshold);
$this->failures = $failures;
$this->timeout = $timeout;
$this->threshold = $threshold;
}
/**
* {@inheritdoc}
*/
abstract public function getState();
/**
* {@inheritdoc}
*/
public function getFailures()
{
return $this->failures;
}
/**
* {@inheritdoc}
*/
public function getTimeout()
{
return $this->timeout;
}
/**
* {@inheritdoc}
*/
public function getThreshold()
{
return $this->threshold;
}
/**
* Ensure the place is valid (PHP5 is permissive).
*
* @param int $failures the failures should be a positive value
* @param float $timeout the timeout should be a positive value
* @param int $threshold the threshold should be a positive value
*
* @return bool true if valid
*
* @throws InvalidPlaceException
*/
private function validate($failures, $timeout, $threshold)
{
$assertionsAreValid = Assert::isPositiveInteger($failures)
&& Assert::isPositiveValue($timeout)
&& Assert::isPositiveInteger($threshold)
;
if ($assertionsAreValid) {
return true;
}
throw InvalidPlaceException::invalidSettings($failures, $timeout, $threshold);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace PrestaShop\CircuitBreaker\Place;
use PrestaShop\CircuitBreaker\State;
final class ClosedPlace extends AbstractPlace
{
/**
* {@inheritdoc}
*/
public function getState()
{
return State::CLOSED_STATE;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace PrestaShop\CircuitBreaker\Place;
use PrestaShop\CircuitBreaker\State;
final class HalfOpenPlace extends AbstractPlace
{
/**
* {@inheritdoc}
*/
public function getState()
{
return State::HALF_OPEN_STATE;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace PrestaShop\CircuitBreaker\Place;
use PrestaShop\CircuitBreaker\State;
final class OpenPlace extends AbstractPlace
{
/**
* {@inheritdoc}
*/
public function getState()
{
return State::OPEN_STATE;
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\System\MainSystem;
use PrestaShop\CircuitBreaker\Storage\SimpleArray;
use PrestaShop\CircuitBreaker\Exception\UnavailableServiceException;
/**
* Main implementation of Circuit Breaker.
*/
final class SimpleCircuitBreaker extends PartialCircuitBreaker
{
public function __construct(
PlaceInterface $openPlace,
PlaceInterface $halfOpenPlace,
PlaceInterface $closedPlace,
ClientInterface $client
) {
$system = new MainSystem($closedPlace, $halfOpenPlace, $openPlace);
parent::__construct($system, $client, new SimpleArray());
}
/**
* {@inheritdoc}
*/
public function call(
$service,
array $serviceParameters = [],
callable $fallback = null
) {
$transaction = $this->initTransaction($service);
try {
if ($this->isOpened()) {
if (!$this->canAccessService($transaction)) {
return $this->callFallback($fallback);
}
$this->moveStateTo(State::HALF_OPEN_STATE, $service);
}
$response = $this->request($service, $serviceParameters);
$this->moveStateTo(State::CLOSED_STATE, $service);
return $response;
} catch (UnavailableServiceException $exception) {
$transaction->incrementFailures();
$this->storage->saveTransaction($service, $transaction);
if (!$this->isAllowedToRetry($transaction)) {
$this->moveStateTo(State::OPEN_STATE, $service);
return $this->callFallback($fallback);
}
return $this->call($service, $serviceParameters, $fallback);
}
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\FactoryInterface;
use PrestaShop\CircuitBreaker\Contract\FactorySettingsInterface;
use PrestaShop\CircuitBreaker\Place\ClosedPlace;
use PrestaShop\CircuitBreaker\Place\HalfOpenPlace;
use PrestaShop\CircuitBreaker\Place\OpenPlace;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
/**
* Main implementation of Circuit Breaker Factory
* Used to create a SimpleCircuitBreaker instance.
*/
final class SimpleCircuitBreakerFactory implements FactoryInterface
{
/**
* {@inheritdoc}
*/
public function create(FactorySettingsInterface $settings)
{
$closedPlace = new ClosedPlace($settings->getFailures(), $settings->getTimeout(), 0);
$openPlace = new OpenPlace(0, 0, $settings->getThreshold());
$halfOpenPlace = new HalfOpenPlace($settings->getFailures(), $settings->getStrippedTimeout(), 0);
/** @var ClientInterface $client */
$client = $settings->getClient() ?: new GuzzleClient($settings->getClientOptions());
return new SimpleCircuitBreaker(
$openPlace,
$halfOpenPlace,
$closedPlace,
$client
);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace PrestaShop\CircuitBreaker;
/**
* Define the available states of the Circuit Breaker;.
*/
final class State
{
/**
* Once opened, a circuit breaker doesn't do any call
* to third-party services. Only the alternative call is done.
*/
const OPEN_STATE = 'OPEN';
/**
* After some conditions are valid, the circuit breaker
* try to access the third-party service. If the service is valid,
* the circuit breaker go to CLOSED state. If it's not, the circuit breaker
* go to OPEN state.
*/
const HALF_OPEN_STATE = 'HALF OPEN';
/**
* On the first call of the service, or if the service is valid
* the circuit breaker is in CLOSED state. This means that the callable
* to evaluate is done and not the alternative call.
*/
const CLOSED_STATE = 'CLOSED';
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\CircuitBreaker\Storage;
use Doctrine\Common\Cache\CacheProvider;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Exception\TransactionNotFoundException;
/**
* Implementation of Storage using the Doctrine Cache.
*/
class DoctrineCache implements StorageInterface
{
/** @var CacheProvider */
private $cacheProvider;
/**
* @param CacheProvider $cacheProvider
*/
public function __construct(CacheProvider $cacheProvider)
{
$this->cacheProvider = $cacheProvider;
}
/**
* {@inheritdoc}
*/
public function saveTransaction($service, TransactionInterface $transaction)
{
$key = $this->getKey($service);
return $this->cacheProvider->save($key, $transaction);
}
/**
* {@inheritdoc}
*/
public function getTransaction($service)
{
$key = $this->getKey($service);
if ($this->hasTransaction($service)) {
return $this->cacheProvider->fetch($key);
}
throw new TransactionNotFoundException();
}
/**
* {@inheritdoc}
*/
public function hasTransaction($service)
{
$key = $this->getKey($service);
return $this->cacheProvider->contains($key);
}
/**
* {@inheritdoc}
*/
public function clear()
{
return $this->cacheProvider->deleteAll();
}
/**
* Helper method to properly store the transaction.
*
* @param string $service the service URI
*
* @return string the transaction unique identifier
*/
private function getKey($service)
{
return md5($service);
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace PrestaShop\CircuitBreaker\Storage;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Exception\TransactionNotFoundException;
/**
* Very simple implementation of Storage using a simple PHP array.
*/
final class SimpleArray implements StorageInterface
{
/**
* @var array the circuit breaker transactions
*/
public static $transactions = [];
/**
* {@inheritdoc}
*/
public function saveTransaction($service, TransactionInterface $transaction)
{
$key = $this->getKey($service);
self::$transactions[$key] = $transaction;
return true;
}
/**
* {@inheritdoc}
*/
public function getTransaction($service)
{
$key = $this->getKey($service);
if ($this->hasTransaction($service)) {
return self::$transactions[$key];
}
throw new TransactionNotFoundException();
}
/**
* {@inheritdoc}
*/
public function hasTransaction($service)
{
$key = $this->getKey($service);
return array_key_exists($key, self::$transactions);
}
/**
* {@inheritdoc}
*/
public function clear()
{
self::$transactions = [];
return true;
}
/**
* Helper method to properly store the transaction.
*
* @param string $service the service URI
*
* @return string the transaction unique identifier
*/
private function getKey($service)
{
return md5($service);
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace PrestaShop\CircuitBreaker\Storage;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Exception\TransactionNotFoundException;
use Psr\SimpleCache\CacheInterface;
/**
* Implementation of Storage using the Symfony Cache Component.
*/
final class SymfonyCache implements StorageInterface
{
/**
* @var CacheInterface the Symfony Cache
*/
private $symfonyCache;
public function __construct(CacheInterface $symfonyCache)
{
$this->symfonyCache = $symfonyCache;
}
/**
* {@inheritdoc}
*/
public function saveTransaction($service, TransactionInterface $transaction)
{
$key = $this->getKey($service);
return $this->symfonyCache->set($key, $transaction);
}
/**
* {@inheritdoc}
*/
public function getTransaction($service)
{
$key = $this->getKey($service);
if ($this->hasTransaction($service)) {
return $this->symfonyCache->get($key);
}
throw new TransactionNotFoundException();
}
/**
* {@inheritdoc}
*/
public function hasTransaction($service)
{
$key = $this->getKey($service);
return $this->symfonyCache->has($key);
}
/**
* {@inheritdoc}
*/
public function clear()
{
return $this->symfonyCache->clear();
}
/**
* Helper method to properly store the transaction.
*
* @param string $service the service URI
*
* @return string the transaction unique identifier
*/
private function getKey($service)
{
return md5($service);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\SystemInterface;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Transition\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Symfony implementation of Circuit Breaker.
*/
final class SymfonyCircuitBreaker extends AdvancedCircuitBreaker
{
/**
* @var EventDispatcherInterface the Symfony Event Dispatcher
*/
private $eventDispatcher;
/**
* @param SystemInterface $system
* @param ClientInterface $client
* @param StorageInterface $storage
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(
SystemInterface $system,
ClientInterface $client,
StorageInterface $storage,
EventDispatcherInterface $eventDispatcher
) {
$this->eventDispatcher = $eventDispatcher;
parent::__construct($system, $client, $storage, new EventDispatcher($eventDispatcher));
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace PrestaShop\CircuitBreaker\System;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
use PrestaShop\CircuitBreaker\Contract\SystemInterface;
use PrestaShop\CircuitBreaker\State;
/**
* Implement the system described by the documentation.
* The main system is built with 3 places:
* - A Closed place
* - A Half Open Place
* - An Open Place
*/
final class MainSystem implements SystemInterface
{
/**
* @var PlaceInterface[]
*/
private $places;
public function __construct(
PlaceInterface $closedPlace,
PlaceInterface $halfOpenPlace,
PlaceInterface $openPlace
) {
$this->places = [
$closedPlace->getState() => $closedPlace,
$halfOpenPlace->getState() => $halfOpenPlace,
$openPlace->getState() => $openPlace,
];
}
/**
* {@inheritdoc}
*/
public function getInitialPlace()
{
return $this->places[State::CLOSED_STATE];
}
/**
* {@inheritdoc}
*/
public function getPlaces()
{
return $this->places;
}
}

View File

@@ -0,0 +1,155 @@
<?php
namespace PrestaShop\CircuitBreaker\Transaction;
use DateTime;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Exception\InvalidTransactionException;
use PrestaShop\CircuitBreaker\Util\Assert;
/**
* Main implementation of Circuit Breaker transaction.
*/
final class SimpleTransaction implements TransactionInterface
{
/**
* @var string the URI of the service
*/
private $service;
/**
* @var int the failures when we call the service
*/
private $failures;
/**
* @var string the Circuit Breaker state
*/
private $state;
/**
* @var DateTime the Transaction threshold datetime
*/
private $thresholdDateTime;
/**
* @param string $service the service URI
* @param int $failures the allowed failures
* @param string $state the circuit breaker state/place
* @param int $threshold the place threshold
*/
public function __construct($service, $failures, $state, $threshold)
{
$this->validate($service, $failures, $state, $threshold);
$this->service = $service;
$this->failures = $failures;
$this->state = $state;
$this->initThresholdDateTime($threshold);
}
/**
* {@inheritdoc}
*/
public function getService()
{
return $this->service;
}
/**
* {@inheritdoc}
*/
public function getFailures()
{
return $this->failures;
}
/**
* {@inheritdoc}
*/
public function getState()
{
return $this->state;
}
/**
* {@inheritdoc}
*/
public function getThresholdDateTime()
{
return $this->thresholdDateTime;
}
/**
* {@inheritdoc}
*/
public function incrementFailures()
{
++$this->failures;
return true;
}
/**
* Helper to create a transaction from the Place.
*
* @param PlaceInterface $place the Circuit Breaker place
* @param string $service the service URI
*
* @return self
*/
public static function createFromPlace(PlaceInterface $place, $service)
{
$threshold = $place->getThreshold();
return new self(
$service,
0,
$place->getState(),
$threshold
);
}
/**
* Set the right DateTime from the threshold value.
*
* @param int $threshold the Transaction threshold
*
* @return void
*/
private function initThresholdDateTime($threshold)
{
$thresholdDateTime = new DateTime();
$thresholdDateTime->modify("+$threshold second");
$this->thresholdDateTime = $thresholdDateTime;
}
/**
* Ensure the transaction is valid (PHP5 is permissive).
*
* @param string $service the service URI
* @param int $failures the failures should be a positive value
* @param string $state the Circuit Breaker state
* @param int $threshold the threshold should be a positive value
*
* @return bool true if valid
*
* @throws InvalidTransactionException
*/
private function validate($service, $failures, $state, $threshold)
{
$assertionsAreValid = Assert::isURI($service)
&& Assert::isPositiveInteger($failures)
&& Assert::isString($state)
&& Assert::isPositiveInteger($threshold)
;
if ($assertionsAreValid) {
return true;
}
throw InvalidTransactionException::invalidParameters($service, $failures, $state, $threshold);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace PrestaShop\CircuitBreaker;
/**
* Define the available transitions of the Circuit Breaker;.
*/
final class Transition
{
/**
* Happened only once when calling the Circuit Breaker.
*/
const INITIATING_TRANSITION = 'INITIATING';
/**
* Happened when we open the Circuit Breaker.
* This means once the Circuit Breaker is in failure.
*/
const OPENING_TRANSITION = 'OPENING';
/**
* Happened once the conditions of retry are met
* in OPEN state to move to HALF_OPEN state in the
* Circuit Breaker.
*/
const CHECKING_AVAILABILITY_TRANSITION = 'CHECKING AVAILABILITY';
/**
* Happened when we come back to OPEN state
* in the Circuit Breaker from the HALF_OPEN state.
*/
const REOPENING_TRANSITION = 'REOPENING';
/**
* Happened if the service is available again.
*/
const CLOSING_TRANSITION = 'CLOSING';
/**
* Happened on each try to call the service.
*/
const TRIAL_TRANSITION = 'TRIAL';
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\CircuitBreaker\Transition;
use PrestaShop\CircuitBreaker\Contract\TransitionDispatcherInterface;
use PrestaShop\CircuitBreaker\Event\TransitionEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Class EventDispatcher implements the TransitionDispatcher using the Symfony EventDispatcherInterface
*/
class EventDispatcher implements TransitionDispatcherInterface
{
/**
* @var EventDispatcherInterface the Symfony Event Dispatcher
*/
private $eventDispatcher;
public function __construct(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
/**
* {@inheritdoc}
*/
public function dispatchTransition($transition, $service, array $serviceParameters)
{
$event = new TransitionEvent($transition, $service, $serviceParameters);
$this->eventDispatcher->dispatch($transition, $event);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\CircuitBreaker\Transition;
use PrestaShop\CircuitBreaker\Contract\TransitionDispatcherInterface;
/**
* Class NullDispatcher is used when you have no TransitionDispatcher to inject
* because you don't need it.
*/
class NullDispatcher implements TransitionDispatcherInterface
{
/**
* {@inheritdoc}
*/
public function dispatchTransition($transition, $service, array $serviceParameters)
{
// Simply does nothing
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace PrestaShop\CircuitBreaker\Util;
/**
* Util class to handle object validation
* Should be deprecated for most parts once
* the library will drop PHP5 support.
*/
final class Assert
{
/**
* @param mixed $value the value to evaluate
*
* @return bool
*/
public static function isPositiveValue($value)
{
return !is_string($value) && is_numeric($value) && $value >= 0;
}
/**
* @param mixed $value the value to evaluate
*
* @return bool
*/
public static function isPositiveInteger($value)
{
return self::isPositiveValue($value) && is_int($value);
}
/**
* @param mixed $value the value to evaluate
*
* @return bool
*/
public static function isURI($value)
{
return null !== $value
&& !is_numeric($value)
&& !is_bool($value)
&& false !== filter_var($value, FILTER_SANITIZE_URL)
;
}
/**
* @param mixed $value the value to evaluate
*
* @return bool
*/
public static function isString($value)
{
return !empty($value) && is_string($value);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace PrestaShop\CircuitBreaker\Util;
/**
* Helper to provide complete and easy to read
* error messages.
* Mostly used to build Exception messages.
*/
final class ErrorFormatter
{
/**
* Format error message.
*
* @param string $parameter the parameter to evaluate
* @param mixed $value the value to format
* @param string $function the validation function
* @param string $expectedType the expected type
*
* @return string
*/
public static function format($parameter, $value, $function, $expectedType)
{
$errorMessage = '';
$isValid = Assert::$function($value);
$type = gettype($value);
$hasStringValue = in_array($type, ['integer', 'float', 'string'], true);
if (!$isValid) {
$errorMessage = sprintf(
'Excepted %s to be %s, got %s',
$parameter,
$expectedType,
$type
);
if ($hasStringValue) {
$errorMessage .= sprintf(' (%s)', (string) $value);
}
$errorMessage .= PHP_EOL;
}
return $errorMessage;
}
}