Skip to content

Commit

Permalink
Merge pull request #45 from boherm/#32855-symfony-http-client
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieu-rolland committed Nov 20, 2023
2 parents 2f66ed4 + a38e635 commit 1ff44e3
Show file tree
Hide file tree
Showing 34 changed files with 1,374 additions and 32 deletions.
111 changes: 99 additions & 12 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,23 @@ name: PHP
on: [push, pull_request]

jobs:
build:
cs:
runs-on: ubuntu-latest
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1

- uses: actions/checkout@v2

- name: Install dependencies
run: composer install --no-interaction

- name: PHP CS Fixer
run: ./vendor/bin/php-cs-fixer fix --dry-run

tests-common:
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -17,25 +33,96 @@ jobs:

- uses: actions/checkout@v2

- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Install dependencies
run: composer install --no-interaction

- name: PHPUnit
run: ./vendor/bin/phpunit --group=common --coverage-clover build/clover.xml

- name: Cache dependencies
uses: actions/cache@v2
- name: Upload coverage results to Coveralls
if: matrix.php-versions == '7.4'
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.4.3/php-coveralls.phar
chmod +x php-coveralls.phar
php php-coveralls.phar --coverage_clover=build/clover.xml --json_path=build/coveralls-upload.json -vvv
tests-guzzle-client:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
php-version: ${{ matrix.php-versions }}
coverage: xdebug

- uses: actions/checkout@v2

- name: Install dependencies
run: composer install --no-interaction

- name: PHPUnit
run: ./vendor/bin/phpunit --coverage-clover build/clover.xml
run: ./vendor/bin/phpunit --group=guzzle-client --coverage-clover build/clover.xml

- name: PHP CS Fixer
run: PHP_CS_FIXER_IGNORE_ENV=1 ./vendor/bin/php-cs-fixer fix --dry-run
- name: Upload coverage results to Coveralls
if: matrix.php-versions == '7.4'
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.4.3/php-coveralls.phar
chmod +x php-coveralls.phar
php php-coveralls.phar --coverage_clover=build/clover.xml --json_path=build/coveralls-upload.json -vvv
tests-symfony-http-client:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
symfony-http-client-versions: ['5.4', '6.0', '6.1', '6.2']
exclude:
- php-versions: '7.2'
symfony-http-client-versions: '6.0'
- php-versions: '7.3'
symfony-http-client-versions: '6.0'
- php-versions: '7.4'
symfony-http-client-versions: '6.0'
- php-versions: '7.2'
symfony-http-client-versions: '6.1'
- php-versions: '7.3'
symfony-http-client-versions: '6.1'
- php-versions: '7.4'
symfony-http-client-versions: '6.1'
- php-versions: '8.0'
symfony-http-client-versions: '6.1'
- php-versions: '7.2'
symfony-http-client-versions: '6.2'
- php-versions: '7.3'
symfony-http-client-versions: '6.2'
- php-versions: '7.4'
symfony-http-client-versions: '6.2'
- php-versions: '8.0'
symfony-http-client-versions: '6.2'
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
coverage: xdebug

- uses: actions/checkout@v2

- name: Require Symfony Http Client version
run: composer require symfony/http-client:${{ matrix.symfony-http-client-versions }} --no-interaction --no-update

- name: Install dependencies
run: composer install --no-interaction

- name: PHPUnit
run: ./vendor/bin/phpunit --group=symfony-http-client --coverage-clover build/clover.xml

- name: Upload coverage results to Coveralls
if: matrix.php-versions == '7.4'
Expand Down
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,29 @@ composer require prestashop/circuit-breaker

## Use

### Symfony Http Client and Guzzle Client implementations

By default, Circuit Breaker use the Symfony Http Client library, and all the client options are described in the [official documentation](https://symfony.com/doc/current/http_client.html).

For retro-compatibility, we let you use Guzzle Client instead of Symfony Http Client. To use Guzzle, you need to set the Guzzle client with `setClient()` of the settings factory, like this example below:

```php
use PrestaShop\CircuitBreaker\SimpleCircuitBreakerFactory;
use PrestaShop\CircuitBreaker\FactorySettings;
use PrestaShop\CircuitBreaker\Client\GuzzleClient

$circuitBreakerFactory = new SimpleCircuitBreakerFactory();
$factorySettings = new FactorySettings(2, 0.1, 10);
$factorySettings->setClient(new GuzzleHttpClient());

$circuitBreaker = $circuitBreakerFactory->create($factorySettings);
```

Be aware, that the client options depend on the client implementation you choose!

> For the Guzzle implementation, the Client options are described
> in the [HttpGuzzle documentation](http://docs.guzzlephp.org/en/stable/index.html).
### Simple Circuit Breaker

You can use the factory to create a simple circuit breaker.
Expand Down Expand Up @@ -71,9 +94,6 @@ $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](http://docs.guzzlephp.org/en/stable/index.html).
### Advanced Circuit Breaker

If you need more control on your circuit breaker, you should use the `AdvancedCircuitBreaker` which manages more features:
Expand Down
17 changes: 13 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
}
],
"require": {
"guzzlehttp/guzzle": "^7.3"
"php": ">=7.2.5",
"symfony/http-client": "^5.4"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.3",
"phpunit/phpunit": "^8",
"doctrine/cache": "^1.10.2",
"symfony/cache": "^4.4",
Expand All @@ -27,7 +29,8 @@
"suggest": {
"symfony/cache": "Allows use of Symfony Cache adapters to store transactions",
"doctrine/cache": "Allows use of Doctrine Cache adapters to store transactions",
"ext-apcu": "Allows use of APCu adapter (performant) to store transactions"
"ext-apcu": "Allows use of APCu adapter (performant) to store transactions",
"guzzlehttp/guzzle": "Allows use of Guzzle to perform HTTP requests instead of Symfony HttpClient"
},
"autoload": {
"psr-4": {
Expand All @@ -41,10 +44,16 @@
},
"scripts": {
"cs-fix": "@php ./vendor/bin/php-cs-fixer fix",
"test": "@php ./vendor/bin/phpunit"
"test": "@php ./vendor/bin/phpunit",
"test-common": "@php ./vendor/bin/phpunit --group=common",
"test-guzzle-client": "@php ./vendor/bin/phpunit --group=guzzle-client",
"test-symfony-http-client": "@php ./vendor/bin/phpunit --group=symfony-http-client"
},
"scripts-descriptions": {
"cs-fix": "Check and fix coding styles using PHP CS Fixer",
"test": "Launch PHPUnit test suite"
"tests": "Launch PHPUnit test suite",
"tests-common": "Launch PHPUnit test suite for commons",
"tests-guzzle": "Launch PHPUnit test suite for Guzzle",
"tests-symfony-http-client": "Launch PHPUnit test suite for Symfony Http Client"
}
}
4 changes: 2 additions & 2 deletions src/AdvancedCircuitBreakerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

namespace PrestaShop\CircuitBreaker;

use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Client\SymfonyHttpClient;
use PrestaShop\CircuitBreaker\Contract\CircuitBreakerInterface;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\FactoryInterface;
Expand Down Expand Up @@ -59,7 +59,7 @@ public function create(FactorySettingsInterface $settings): CircuitBreakerInterf
$system = new MainSystem($closedPlace, $halfOpenPlace, $openPlace);

/** @var ClientInterface $client */
$client = $settings->getClient() ?: new GuzzleClient($settings->getClientOptions());
$client = $settings->getClient() ?: new SymfonyHttpClient($settings->getClientOptions());
/** @var StorageInterface $storage */
$storage = $settings->getStorage() ?: new SimpleArray();
/** @var TransitionDispatcherInterface $dispatcher */
Expand Down
120 changes: 120 additions & 0 deletions src/Client/SymfonyHttpClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* 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.md.
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/

declare(strict_types=1);

namespace PrestaShop\CircuitBreaker\Client;

use Exception;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Exception\UnavailableServiceException;
use PrestaShop\CircuitBreaker\Exception\UnsupportedMethodException;
use Symfony\Component\HttpClient\HttpClient as OriginalSymfonyHttpClient;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
* Symfony Http Client implementation.
* The possibility of extending this client is intended.
*/
class SymfonyHttpClient implements ClientInterface
{
/**
* @var string by default, calls are sent using GET method
*/
public const DEFAULT_METHOD = 'GET';

/**
* Supported HTTP methods
*/
public const SUPPORTED_METHODS = [
'GET',
'HEAD',
'POST',
'PUT',
'DELETE',
'OPTIONS',
];

/**
* @var array the Client default options
*/
private $defaultOptions;

/**
* @var HttpClientInterface|null
*/
private $client;

public function __construct(array $defaultOptions = [], HttpClientInterface $client = null)
{
$this->defaultOptions = $defaultOptions;
$this->client = $client;
}

/**
* {@inheritdoc}
*
* @throws UnavailableServiceException
*/
public function request(string $resource, array $options): string
{
try {
$options = array_merge($this->defaultOptions, $options);
$method = $this->getHttpMethod($options);
// Symfony Http Client not support "method" passed in options array.
unset($options['method']);
// If we haven't already injected a client, we create a new one.
if (!$this->client) {
$this->client = OriginalSymfonyHttpClient::create($options);
}

return (string) $this->client->request($method, $resource, $options)->getContent();
} catch (Exception|TransportExceptionInterface $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): string
{
if (isset($options['method'])) {
if (!in_array($options['method'], self::SUPPORTED_METHODS)) {
throw UnsupportedMethodException::unsupportedMethod($options['method']);
}

return $options['method'];
}

return self::DEFAULT_METHOD;
}
}
12 changes: 8 additions & 4 deletions src/PartialCircuitBreaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
namespace PrestaShop\CircuitBreaker;

use DateTime;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Contract\CircuitBreakerInterface;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
Expand Down Expand Up @@ -178,12 +179,15 @@ protected function canAccessService(TransactionInterface $transaction): bool
*/
protected function request(string $service, array $parameters = []): string
{
$forcedParameters = ['timeout' => $this->currentPlace->getTimeout()];

if ($this->client instanceof GuzzleClient) {
$forcedParameters['connect_timeout'] = $this->currentPlace->getTimeout();
}

return $this->client->request(
$service,
array_merge($parameters, [
'connect_timeout' => $this->currentPlace->getTimeout(),
'timeout' => $this->currentPlace->getTimeout(),
])
array_merge($parameters, $forcedParameters)
);
}
}
4 changes: 2 additions & 2 deletions src/SimpleCircuitBreakerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

namespace PrestaShop\CircuitBreaker;

use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Client\SymfonyHttpClient;
use PrestaShop\CircuitBreaker\Contract\CircuitBreakerInterface;
use PrestaShop\CircuitBreaker\Contract\ClientInterface;
use PrestaShop\CircuitBreaker\Contract\FactoryInterface;
Expand All @@ -53,7 +53,7 @@ public function create(FactorySettingsInterface $settings): CircuitBreakerInterf
$halfOpenPlace = new HalfOpenPlace($settings->getFailures(), $settings->getStrippedTimeout(), 0);

/** @var ClientInterface $client */
$client = $settings->getClient() ?: new GuzzleClient($settings->getClientOptions());
$client = $settings->getClient() ?: new SymfonyHttpClient($settings->getClientOptions());

return new SimpleCircuitBreaker(
$openPlace,
Expand Down

0 comments on commit 1ff44e3

Please sign in to comment.