Skip to content

Commit

Permalink
Merge 7fd0a5a into 5535fac
Browse files Browse the repository at this point in the history
  • Loading branch information
Xenonym committed Sep 16, 2021
2 parents 5535fac + 7fd0a5a commit fdb80cc
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 122 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,18 @@ $builder = UnleashBuilder::create()

Some optional parameters can be set, these include:

- http client implementation
- request factory implementation
- http client implementation ([PSR-18](https://packagist.org/providers/psr/http-client-implementation))
- request factory implementation ([PSR-17](https://packagist.org/providers/psr/http-factory-implementation))
- cache implementation ([PSR-16](https://packagist.org/providers/psr/simple-cache-implementation))
- cache ttl
- available strategies
- http headers

If you use [guzzlehttp/guzzle](https://packagist.org/packages/guzzlehttp/guzzle) or
The builder will attempt to load http client and request factory implementations automatically. Most implementations,
such as [guzzlehttp/guzzle](https://packagist.org/packages/guzzlehttp/guzzle) or
[symfony/http-client](https://packagist.org/packages/symfony/http-client) (in combination with
[nyholm/psr7](https://packagist.org/packages/nyholm/psr7)), the http client and request factory will be created
automatically, otherwise you need to provide an implementation on your own.
[nyholm/psr7](https://packagist.org/packages/nyholm/psr7)), will be loaded automatically. If the builder is unable to
locate a http client or request factory implementation, you will need to provide some implementation on your own.

If you use [symfony/cache](https://packagist.org/packages/symfony/cache) or
[cache/filesystem-adapter](https://packagist.org/packages/cache/filesystem-adapter) as your cache implementation, the
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"psr/simple-cache": "^1.0",
"psr/simple-cache-implementation": "^1.0",
"lastguest/murmurhash": "^2.1",
"psr/http-message": "^1.0"
"psr/http-message": "^1.0",
"php-http/discovery": "^1.14"
},
"autoload": {
"psr-4": {
Expand Down
80 changes: 17 additions & 63 deletions src/Helper/DefaultImplementationLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace Unleash\Client\Helper;

use Cache\Adapter\Filesystem\FilesystemCachePool;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use Http\Discovery\Exception\NotFoundException;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use LogicException;
Expand All @@ -13,7 +14,6 @@
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\HttpClient\Psr18Client;

/**
* @internal
Expand All @@ -24,14 +24,6 @@ final class DefaultImplementationLocator
* @var array<string,string[]>
*/
private array $supportedPackages = [
'client' => [
'guzzlehttp/guzzle',
'symfony/http-client',
],
'factory' => [
'guzzlehttp/guzzle',
'symfony/http-client',
],
'cache' => [
'symfony/cache',
'cache/filesystem-adapter',
Expand All @@ -42,14 +34,6 @@ final class DefaultImplementationLocator
* @var array<string,array<string,array>>
*/
private array $defaultImplementations = [
'client' => [
Client::class => [],
Psr18Client::class => [],
],
'factory' => [
HttpFactory::class => [],
Psr18Client::class => [],
],
'cache' => [
FilesystemCachePool::class => [
Filesystem::class => [
Expand All @@ -68,40 +52,26 @@ final class DefaultImplementationLocator

public function findHttpClient(): ?ClientInterface
{
foreach ($this->defaultImplementations['client'] as $class => $config) {
if (class_exists($class)) {
$result = $this->constructObject($class, $config);
if (!$result instanceof ClientInterface) {
// @codeCoverageIgnoreStart
throw new LogicException('The resulting object is not an instance of ' . ClientInterface::class);
// @codeCoverageIgnoreEnd
}

return $result;
}
try {
/**
* Discovery triggers an error if symfony/http-client is installed
* and php-http/httplug is not, even if the intention is to find a
* PSR-18 client. Since the discovery will otherwise be successful,
* let's silence the error.
*/
return @Psr18ClientDiscovery::find();
} catch (NotFoundException $e) {
return null;
}

return null;
}

public function findRequestFactory(): ?RequestFactoryInterface
{
foreach ($this->defaultImplementations['factory'] as $class => $config) {
if (class_exists($class)) {
$result = $this->constructObject($class, $config);
if (!$result instanceof RequestFactoryInterface) {
// @codeCoverageIgnoreStart
throw new LogicException(
'The resulting object is not an instance of ' . RequestFactoryInterface::class
);
// @codeCoverageIgnoreEnd
}

return $result;
}
try {
return Psr17FactoryDiscovery::findRequestFactory();
} catch (NotFoundException $e) {
return null;
}

return null;
}

public function findCache(): ?CacheInterface
Expand All @@ -124,22 +94,6 @@ public function findCache(): ?CacheInterface
return null;
}

/**
* @return string[]
*/
public function getHttpClientPackages(): array
{
return $this->supportedPackages['client'];
}

/**
* @return string[]
*/
public function getRequestFactoryPackages(): array
{
return $this->supportedPackages['factory'];
}

/**
* @return string[]
*/
Expand Down
10 changes: 2 additions & 8 deletions src/UnleashBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,7 @@ public function build(): Unleash
$httpClient = $this->defaultImplementationLocator->findHttpClient();
if ($httpClient === null) {
throw new InvalidValueException(
sprintf(
"No http client provided, please use 'withHttpClient()' method or install one of officially supported clients: '%s'",
implode("', '", $this->defaultImplementationLocator->getHttpClientPackages())
)
"No http client provided, please use 'withHttpClient()' method or install a package providing 'psr/http-client-implementation'.",
);
}
}
Expand All @@ -288,10 +285,7 @@ public function build(): Unleash
$requestFactory = $this->defaultImplementationLocator->findRequestFactory();
if ($requestFactory === null) {
throw new InvalidValueException(
sprintf(
"No request factory provided, please use 'withHttpClient()' method or install one of officially supported clients: '%s'",
implode("', '", $this->defaultImplementationLocator->getRequestFactoryPackages())
)
"No request factory provided, please use 'withRequestFactory()' method or install a package providing 'psr/http-factory-implementation'.",
);
}
}
Expand Down
77 changes: 32 additions & 45 deletions tests/UnleashBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
namespace Unleash\Client\Tests;

use Cache\Adapter\Filesystem\FilesystemCachePool;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use PHPUnit\Framework\TestCase;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use ReflectionObject;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\HttpClient\Psr18Client;
use Unleash\Client\Client\DefaultRegistrationService;
use Unleash\Client\Configuration\Context;
use Unleash\Client\Configuration\UnleashConfiguration;
Expand Down Expand Up @@ -42,7 +43,7 @@ public function testWithInstanceId()

public function testWithHttpClient()
{
self::assertNotSame($this->instance, $this->instance->withHttpClient(new Client()));
self::assertNotSame($this->instance, $this->instance->withHttpClient($this->newHttpClient()));
}

public function testWithStrategies()
Expand Down Expand Up @@ -89,7 +90,7 @@ public function testWithCacheHandler()

public function testWithRequestFactory()
{
self::assertNotSame($this->instance, $this->instance->withRequestFactory(new HttpFactory()));
self::assertNotSame($this->instance, $this->instance->withRequestFactory($this->newRequestFactory()));
}

public function testBuild()
Expand Down Expand Up @@ -143,8 +144,8 @@ public function testBuild()
self::assertIsInt($configuration->getTtl());
self::assertCount(8, $strategies);

$requestFactory = new HttpFactory();
$httpClient = new Client();
$requestFactory = $this->newRequestFactory();
$httpClient = $this->newHttpClient();

$instance = $this->instance
->withAppUrl('https://example.com')
Expand Down Expand Up @@ -248,8 +249,8 @@ public function testWithHeader()
public function testWithRegistrationService()
{
self::assertNotSame($this->instance, $this->instance->withRegistrationService(new DefaultRegistrationService(
new Client(),
new HttpFactory(),
$this->newHttpClient(),
$this->newRequestFactory(),
new UnleashConfiguration('', '', '')
)));
}
Expand Down Expand Up @@ -292,10 +293,6 @@ public function testWithoutDefaultClients()
$locatorProperty->setAccessible(true);
$locator = $locatorProperty->getValue($instance);

$defaultImplementationsProperty = (new ReflectionObject($locator))->getProperty('defaultImplementations');
$defaultImplementationsProperty->setAccessible(true);
$defaultImplementations = $defaultImplementationsProperty->getValue($locator);

$repositoryProperty = (new ReflectionObject($unleash))->getProperty('repository');
$repositoryProperty->setAccessible(true);
$repository = $repositoryProperty->getValue($unleash);
Expand All @@ -304,30 +301,18 @@ public function testWithoutDefaultClients()
$httpClientProperty->setAccessible(true);
$httpClient = $httpClientProperty->getValue($repository);

self::assertInstanceOf(Client::class, $httpClient);

$defaultImplementations['client'][Client::class . 2] = [];
unset($defaultImplementations['client'][Client::class]);
$defaultImplementationsProperty->setValue($locator, $defaultImplementations);

$unleash = $instance->build();
$repository = $repositoryProperty->getValue($unleash);
$httpClient = $httpClientProperty->getValue($repository);

self::assertInstanceOf(Psr18Client::class, $httpClient);
self::assertInstanceOf(ClientInterface::class, $httpClient);

$defaultImplementations['client'][Psr18Client::class . 2] = [];
unset($defaultImplementations['client'][Psr18Client::class]);
$defaultImplementationsProperty->setValue($locator, $defaultImplementations);
$psr18DiscoveryStrategies = Psr18ClientDiscovery::getStrategies();
Psr18ClientDiscovery::setStrategies([]);

try {
$instance->build();
$this->fail('No default http client is available, expected exception');
} catch (InvalidValueException $e) {
}

$defaultImplementations['client'][Psr18Client::class] = [];
$defaultImplementationsProperty->setValue($locator, $defaultImplementations);
Psr18ClientDiscovery::setStrategies($psr18DiscoveryStrategies);

$requestFactoryProperty = (new ReflectionObject($repository))->getProperty('requestFactory');
$requestFactoryProperty->setAccessible(true);
Expand All @@ -336,30 +321,18 @@ public function testWithoutDefaultClients()
$repository = $repositoryProperty->getValue($unleash);
$requestFactory = $requestFactoryProperty->getValue($repository);

self::assertInstanceOf(HttpFactory::class, $requestFactory);

$defaultImplementations['factory'][HttpFactory::class . 2] = [];
unset($defaultImplementations['factory'][HttpFactory::class]);
$defaultImplementationsProperty->setValue($locator, $defaultImplementations);

$unleash = $instance->build();
$repository = $repositoryProperty->getValue($unleash);
$requestFactory = $requestFactoryProperty->getValue($repository);

self::assertInstanceOf(Psr18Client::class, $requestFactory);
self::assertInstanceOf(RequestFactoryInterface::class, $requestFactory);

$defaultImplementations['factory'][Psr18Client::class . 2] = [];
unset($defaultImplementations['factory'][Psr18Client::class]);
$defaultImplementationsProperty->setValue($locator, $defaultImplementations);
$psr17DiscoveryStrategies = Psr17FactoryDiscovery::getStrategies();
Psr17FactoryDiscovery::setStrategies([]);

try {
$instance->build();
$this->fail('No default request factory is available, expected exception');
} catch (InvalidValueException $e) {
}

$defaultImplementations['factory'][Psr18Client::class] = [];
$defaultImplementationsProperty->setValue($locator, $defaultImplementations);
Psr17FactoryDiscovery::setStrategies($psr17DiscoveryStrategies);

$configurationProperty = (new ReflectionObject($repository))->getProperty('configuration');
$configurationProperty->setAccessible(true);
Expand All @@ -375,6 +348,10 @@ public function testWithoutDefaultClients()

self::assertInstanceOf(FilesystemCachePool::class, $cache);

$defaultImplementationsProperty = (new ReflectionObject($locator))->getProperty('defaultImplementations');
$defaultImplementationsProperty->setAccessible(true);
$defaultImplementations = $defaultImplementationsProperty->getValue($locator);

$defaultImplementations['cache'][FilesystemCachePool::class . 2] = [];
unset($defaultImplementations['cache'][FilesystemCachePool::class]);
$defaultImplementationsProperty->setValue($locator, $defaultImplementations);
Expand Down Expand Up @@ -478,4 +455,14 @@ public function testWithContextProvider()
assert($provider instanceof DefaultUnleashContextProvider);
self::assertEquals('456', $provider->getContext()->getCurrentUserId());
}

private function newHttpClient(): ClientInterface
{
return $this->createMock(ClientInterface::class);
}

private function newRequestFactory(): RequestFactoryInterface
{
return $this->createMock(RequestFactoryInterface::class);
}
}

0 comments on commit fdb80cc

Please sign in to comment.