From 0b80607cb45410231985189fc607a94b6f300c49 Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Thu, 18 May 2023 12:16:40 +0200 Subject: [PATCH 01/11] Upgrade HTTPlug from Legacy to Psr17/18 Discovery Factories --- composer.json | 14 +++++++------- src/Client.php | 4 ++-- src/HttpClient/HttpPluginClientBuilder.php | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 987103d..7faa254 100644 --- a/composer.json +++ b/composer.json @@ -12,20 +12,20 @@ ], "require": { "php": "^7.2 || ^8.0", - "psr/http-message": "^1.0", - "php-http/httplug": "^1.1 || ^2.0", + "ext-json": "*", + "php-http/client-common": "^1.9 || ^2.0", "php-http/discovery": "^1.0", "psr/http-client-implementation": "^1.0", - "php-http/client-common": "^1.9 || ^2.0", "php-http/message-factory": "^1.0", + "psr/http-message-implementation": "^1.0", "composer-runtime-api": "^2.0" }, "require-dev": { - "phpunit/phpunit": "^8.0 || ^9.0", - "guzzlehttp/guzzle": "^7", - "php-http/mock-client": "^1.0", "friendsofphp/php-cs-fixer": "^3.0", - "phpstan/phpstan": "^1.2" + "php-http/guzzle7-adapter": "^1.0", + "php-http/mock-client": "^1.0", + "phpstan/phpstan": "^1.2", + "phpunit/phpunit": "^8.0 || ^9.0" }, "autoload": { "psr-4": { "PrivatePackagist\\ApiClient\\": "src/" } diff --git a/src/Client.php b/src/Client.php index 84719c1..b902aac 100644 --- a/src/Client.php +++ b/src/Client.php @@ -10,7 +10,7 @@ namespace PrivatePackagist\ApiClient; use Http\Client\Common\Plugin; -use Http\Discovery\UriFactoryDiscovery; +use Http\Discovery\Psr17FactoryDiscovery; use PrivatePackagist\ApiClient\HttpClient\HttpPluginClientBuilder; use PrivatePackagist\ApiClient\HttpClient\Message\ResponseMediator; use PrivatePackagist\ApiClient\HttpClient\Plugin\ExceptionThrower; @@ -31,7 +31,7 @@ public function __construct(HttpPluginClientBuilder $httpClientBuilder = null, $ $privatePackagistUrl = $privatePackagistUrl ? : 'https://packagist.com'; $this->responseMediator = $responseMediator ? : new ResponseMediator(); - $builder->addPlugin(new Plugin\AddHostPlugin(UriFactoryDiscovery::find()->createUri($privatePackagistUrl))); + $builder->addPlugin(new Plugin\AddHostPlugin(Psr17FactoryDiscovery::findUriFactory()->createUri($privatePackagistUrl))); $builder->addPlugin(new PathPrepend('/api')); $builder->addPlugin(new Plugin\RedirectPlugin()); $headers = [ diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index 6b10c92..e754b44 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -12,10 +12,10 @@ use Http\Client\Common\HttpMethodsClient; use Http\Client\Common\Plugin; use Http\Client\Common\PluginClient; -use Http\Discovery\HttpClientDiscovery; -use Http\Discovery\MessageFactoryDiscovery; -use Http\Message\RequestFactory; +use Http\Discovery\Psr17FactoryDiscovery; +use Http\Discovery\Psr18ClientDiscovery; use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; class HttpPluginClientBuilder { @@ -23,15 +23,15 @@ class HttpPluginClientBuilder private $httpClient; /** @var HttpMethodsClient|null */ private $pluginClient; - /** @var RequestFactory */ + /** @var RequestFactoryInterface */ private $requestFactory; /** @var Plugin[] */ private $plugins = []; - public function __construct(ClientInterface $httpClient = null, RequestFactory $requestFactory = null) + public function __construct(ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null) { - $this->httpClient = $httpClient ?: HttpClientDiscovery::find(); - $this->requestFactory = $requestFactory ?: MessageFactoryDiscovery::find(); + $this->httpClient = $httpClient ?: Psr18ClientDiscovery::find(); + $this->requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory(); } public function addPlugin(Plugin $plugin) From 9a9e04913e99d8b7e0722c332d9316c285cb781b Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Thu, 18 May 2023 17:55:32 +0200 Subject: [PATCH 02/11] Support Both HTTPlug's RequestFactory and PSR's RequestFactoryInterface --- composer.json | 3 +- src/HttpClient/HttpPluginClientBuilder.php | 28 ++++++- .../HttpPluginClientBuilderTest.php | 80 +++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 tests/HttpClient/HttpPluginClientBuilderTest.php diff --git a/composer.json b/composer.json index 7faa254..d06ad05 100644 --- a/composer.json +++ b/composer.json @@ -13,12 +13,13 @@ "require": { "php": "^7.2 || ^8.0", "ext-json": "*", + "composer-runtime-api": "^2.0", "php-http/client-common": "^1.9 || ^2.0", "php-http/discovery": "^1.0", "psr/http-client-implementation": "^1.0", "php-http/message-factory": "^1.0", "psr/http-message-implementation": "^1.0", - "composer-runtime-api": "^2.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.0", diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index e754b44..45ccc12 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -14,6 +14,7 @@ use Http\Client\Common\PluginClient; use Http\Discovery\Psr17FactoryDiscovery; use Http\Discovery\Psr18ClientDiscovery; +use Http\Message\RequestFactory; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; @@ -28,10 +29,33 @@ class HttpPluginClientBuilder /** @var Plugin[] */ private $plugins = []; - public function __construct(ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null) + /** + * @param ClientInterface|null $httpClient + * @param RequestFactory|RequestFactoryInterface|null $requestFactory + */ + public function __construct(ClientInterface $httpClient = null, $requestFactory = null) { + $requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory(); + if ($requestFactory instanceof RequestFactory) { + trigger_deprecation( + 'private-packagist/api-client', + '1.35.0', + '', + RequestFactory::class, + RequestFactoryInterface::class + ); + } elseif (!$requestFactory instanceof RequestFactoryInterface) { + throw new \TypeError(sprintf( + '%s::__construct(): Argument #2 ($requestFactory) must be of type %s|%s, %s given', + self::class, + RequestFactory::class, + RequestFactoryInterface::class, + is_object($requestFactory) ? get_class($requestFactory) : gettype($requestFactory) + )); + } + $this->httpClient = $httpClient ?: Psr18ClientDiscovery::find(); - $this->requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory(); + $this->requestFactory = $requestFactory; } public function addPlugin(Plugin $plugin) diff --git a/tests/HttpClient/HttpPluginClientBuilderTest.php b/tests/HttpClient/HttpPluginClientBuilderTest.php new file mode 100644 index 0000000..84e8427 --- /dev/null +++ b/tests/HttpClient/HttpPluginClientBuilderTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PrivatePackagist\ApiClient\HttpClient\Plugin; + +use GuzzleHttp\Psr7\HttpFactory; +use GuzzleHttp\Psr7\Response; +use Http\Client\Common\HttpMethodsClientInterface; +use Http\Message\MessageFactory\GuzzleMessageFactory; +use Http\Message\RequestMatcher as RequestMatcherInterface; +use Http\Mock\Client as MockClient; +use PHPUnit\Framework\TestCase; +use PrivatePackagist\ApiClient\HttpClient\HttpPluginClientBuilder; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +class HttpPluginClientBuilderTest extends TestCase +{ + /** @dataProvider provideRequestFactories */ + public function testRequestFactory(?object $factory, ?string $expectedException): void + { + if ($expectedException !== null) { + $this->expectException($expectedException); + } + + $mockHttp = new MockClient; + $mockHttp->setDefaultException(new \Exception('Mock HTTP client did not match request.')); + $mockHttp->on($this->matchRequestIncludingHeaders(), new Response(307, ['Location' => '/kittens.jpg'])); + + $builder = new HttpPluginClientBuilder($mockHttp, $factory); + // Make sure that the RequestFactory passed is acceptable for the client. + $client = $builder->getHttpClient(); + $this->assertInstanceOf(HttpMethodsClientInterface::class, $client); + + // Ensure that the Request Factory correctly generates a request object (including headers + // as RequestFactory and RequestFactoryInterface set headers differently). + $response = $client->get('https://example.com/puppies.jpg', ['Accept' => 'image/vnd.cute+jpeg']); + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertSame(307, $response->getStatusCode()); + $locationHeaders = $response->getHeader('Location'); + $this->assertCount(1, $locationHeaders); + $this->assertSame('/kittens.jpg', reset($locationHeaders)); + } + + /** + * The concrete implementation of the RequestMatcher interface does not allow matching on + * headers, which we need to test to ensure both legacy and PSR17 implementations work. + */ + protected function matchRequestIncludingHeaders(): RequestMatcherInterface + { + return new class implements RequestMatcherInterface { + public function matches(RequestInterface $request): bool + { + $acceptHeaders = $request->getHeader('Accept'); + return $request->getUri()->getPath() === '/puppies.jpg' + && count($acceptHeaders) === 1 + && reset($acceptHeaders) === 'image/vnd.cute+jpeg'; + } + }; + } + + /** @return iterable{object|null, class-string|null} */ + private static function provideRequestFactories(): iterable + { + // Fallback + yield [null, null]; + // Legacy + yield [new GuzzleMessageFactory, null]; + // PSR17 + yield [new HttpFactory, null]; + // Invalid + yield [new \stdClass, \TypeError::class]; + } +} From bcdebc182e7cda3b207a8899e23ae073c4b93314 Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Fri, 19 May 2023 10:08:24 +0200 Subject: [PATCH 03/11] Finish Deprecation Notices in HttpPluginClientBuilder --- composer.json | 3 +-- src/HttpClient/HttpPluginClientBuilder.php | 11 ++++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index d06ad05..8b8ca3f 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,7 @@ "php-http/discovery": "^1.0", "psr/http-client-implementation": "^1.0", "php-http/message-factory": "^1.0", - "psr/http-message-implementation": "^1.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0" + "psr/http-message-implementation": "^1.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.0", diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index 45ccc12..226704a 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -30,21 +30,22 @@ class HttpPluginClientBuilder private $plugins = []; /** - * @param ClientInterface|null $httpClient * @param RequestFactory|RequestFactoryInterface|null $requestFactory */ - public function __construct(ClientInterface $httpClient = null, $requestFactory = null) + public function __construct(?ClientInterface $httpClient = null, $requestFactory = null) { $requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory(); if ($requestFactory instanceof RequestFactory) { - trigger_deprecation( + // Use same format as symfony/deprecation-contracts. + @trigger_error(sprintf( + 'Since %s %s: %s is deprecated, use %s instead.', 'private-packagist/api-client', '1.35.0', - '', RequestFactory::class, RequestFactoryInterface::class - ); + ), \E_USER_DEPRECATED); } elseif (!$requestFactory instanceof RequestFactoryInterface) { + /** @var mixed $requestFactory value unknown; set to mixed, prevent PHPStan complaining about guard clauses */ throw new \TypeError(sprintf( '%s::__construct(): Argument #2 ($requestFactory) must be of type %s|%s, %s given', self::class, From 824caea68907c94f9d8ed8ff8bbf607a61eb430d Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Fri, 19 May 2023 10:10:28 +0200 Subject: [PATCH 04/11] Make PHPUnit Data Provider Public --- tests/HttpClient/HttpPluginClientBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/HttpClient/HttpPluginClientBuilderTest.php b/tests/HttpClient/HttpPluginClientBuilderTest.php index 84e8427..d61fac1 100644 --- a/tests/HttpClient/HttpPluginClientBuilderTest.php +++ b/tests/HttpClient/HttpPluginClientBuilderTest.php @@ -66,7 +66,7 @@ public function matches(RequestInterface $request): bool } /** @return iterable{object|null, class-string|null} */ - private static function provideRequestFactories(): iterable + public static function provideRequestFactories(): iterable { // Fallback yield [null, null]; From af6fd31145bd6a251d10a37e83f5ea5a899844f6 Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Fri, 19 May 2023 10:17:14 +0200 Subject: [PATCH 05/11] Update Class Property PHPDoc --- src/HttpClient/HttpPluginClientBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index 226704a..ec0e801 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -24,7 +24,7 @@ class HttpPluginClientBuilder private $httpClient; /** @var HttpMethodsClient|null */ private $pluginClient; - /** @var RequestFactoryInterface */ + /** @var RequestFactory|RequestFactoryInterface */ private $requestFactory; /** @var Plugin[] */ private $plugins = []; From 456ea0de4ce9ccf12ee967dc6fbd15bd540dd8f8 Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Fri, 19 May 2023 14:03:25 +0200 Subject: [PATCH 06/11] Do not allow falsey values to mean the same as null. --- src/HttpClient/HttpPluginClientBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index ec0e801..51152ab 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -34,7 +34,7 @@ class HttpPluginClientBuilder */ public function __construct(?ClientInterface $httpClient = null, $requestFactory = null) { - $requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory(); + $requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory(); if ($requestFactory instanceof RequestFactory) { // Use same format as symfony/deprecation-contracts. @trigger_error(sprintf( @@ -55,7 +55,7 @@ public function __construct(?ClientInterface $httpClient = null, $requestFactory )); } - $this->httpClient = $httpClient ?: Psr18ClientDiscovery::find(); + $this->httpClient = $httpClient ?? Psr18ClientDiscovery::find(); $this->requestFactory = $requestFactory; } From a9d461671eb4876c9c99f7d1d2b860d9d04fdf68 Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Fri, 19 May 2023 16:00:14 +0200 Subject: [PATCH 07/11] Revert Back to using Guzzle Directly --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8b8ca3f..8fa214e 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.0", - "php-http/guzzle7-adapter": "^1.0", + "guzzlehttp/guzzle": "^7.0", "php-http/mock-client": "^1.0", "phpstan/phpstan": "^1.2", "phpunit/phpunit": "^8.0 || ^9.0" From 899dec8e7f9fed714bd8fb7ce17cd3319114503e Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Fri, 19 May 2023 16:00:58 +0200 Subject: [PATCH 08/11] Split up HttpPluginClientBuilderTest into exception throwing and successful tests --- .../HttpPluginClientBuilderTest.php | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tests/HttpClient/HttpPluginClientBuilderTest.php b/tests/HttpClient/HttpPluginClientBuilderTest.php index d61fac1..26c9e9c 100644 --- a/tests/HttpClient/HttpPluginClientBuilderTest.php +++ b/tests/HttpClient/HttpPluginClientBuilderTest.php @@ -22,13 +22,16 @@ class HttpPluginClientBuilderTest extends TestCase { - /** @dataProvider provideRequestFactories */ - public function testRequestFactory(?object $factory, ?string $expectedException): void + public function testInvalidRequestFactory(): void { - if ($expectedException !== null) { - $this->expectException($expectedException); - } + $this->expectException(\TypeError::class); + $definitelyNotARequestFactory = new \stdClass; + new HttpPluginClientBuilder(new MockClient, $definitelyNotARequestFactory); + } + /** @dataProvider provideRequestFactories */ + public function testRequestFactory(?object $factory): void + { $mockHttp = new MockClient; $mockHttp->setDefaultException(new \Exception('Mock HTTP client did not match request.')); $mockHttp->on($this->matchRequestIncludingHeaders(), new Response(307, ['Location' => '/kittens.jpg'])); @@ -52,7 +55,7 @@ public function testRequestFactory(?object $factory, ?string $expectedException) * The concrete implementation of the RequestMatcher interface does not allow matching on * headers, which we need to test to ensure both legacy and PSR17 implementations work. */ - protected function matchRequestIncludingHeaders(): RequestMatcherInterface + private function matchRequestIncludingHeaders(): RequestMatcherInterface { return new class implements RequestMatcherInterface { public function matches(RequestInterface $request): bool @@ -65,16 +68,11 @@ public function matches(RequestInterface $request): bool }; } - /** @return iterable{object|null, class-string|null} */ + /** @return iterable{object|null} */ public static function provideRequestFactories(): iterable { - // Fallback - yield [null, null]; - // Legacy - yield [new GuzzleMessageFactory, null]; - // PSR17 - yield [new HttpFactory, null]; - // Invalid - yield [new \stdClass, \TypeError::class]; + yield [null]; + yield [new GuzzleMessageFactory]; + yield [new HttpFactory]; } } From a8e6b7087f01fdcbdd786a16f94bb5b99207f82e Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Tue, 10 Oct 2023 11:06:38 +0200 Subject: [PATCH 09/11] Update Version in Deprecation Message --- src/HttpClient/HttpPluginClientBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index 51152ab..e3c7d6d 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -40,7 +40,7 @@ public function __construct(?ClientInterface $httpClient = null, $requestFactory @trigger_error(sprintf( 'Since %s %s: %s is deprecated, use %s instead.', 'private-packagist/api-client', - '1.35.0', + '1.36.0', RequestFactory::class, RequestFactoryInterface::class ), \E_USER_DEPRECATED); From 3f8000edccb07583ab69157643b3e3a7bc64e502 Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Tue, 10 Oct 2023 11:07:11 +0200 Subject: [PATCH 10/11] Add Support for Stream Factory Autodiscovery --- src/HttpClient/HttpPluginClientBuilder.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index e3c7d6d..de6928e 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -17,6 +17,7 @@ use Http\Message\RequestFactory; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; +use Psr\Http\Message\StreamFactoryInterface; class HttpPluginClientBuilder { @@ -26,14 +27,19 @@ class HttpPluginClientBuilder private $pluginClient; /** @var RequestFactory|RequestFactoryInterface */ private $requestFactory; + private $streamFactory; /** @var Plugin[] */ private $plugins = []; /** * @param RequestFactory|RequestFactoryInterface|null $requestFactory + * @param StreamFactoryInterface|null $streamFactory */ - public function __construct(?ClientInterface $httpClient = null, $requestFactory = null) - { + public function __construct( + ?ClientInterface $httpClient = null, + $requestFactory = null, + ?StreamFactoryInterface $streamFactory= null + ) { $requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory(); if ($requestFactory instanceof RequestFactory) { // Use same format as symfony/deprecation-contracts. @@ -57,6 +63,7 @@ public function __construct(?ClientInterface $httpClient = null, $requestFactory $this->httpClient = $httpClient ?? Psr18ClientDiscovery::find(); $this->requestFactory = $requestFactory; + $this->streamFactory = $streamFactory ?? Psr17FactoryDiscovery::findStreamFactory(); } public function addPlugin(Plugin $plugin) @@ -83,7 +90,8 @@ public function getHttpClient() if (!$this->pluginClient) { $this->pluginClient = new HttpMethodsClient( new PluginClient($this->httpClient, $this->plugins), - $this->requestFactory + $this->requestFactory, + $this->streamFactory ); } From 9427e98a2747647b43dcd93c5b446234ac508a41 Mon Sep 17 00:00:00 2001 From: Zan Baldwin Date: Tue, 10 Oct 2023 11:27:49 +0200 Subject: [PATCH 11/11] PHPStan: Add DocComments and ignore lines that purposefully fail static analysis --- src/HttpClient/HttpPluginClientBuilder.php | 3 ++- tests/HttpClient/HttpPluginClientBuilderTest.php | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/HttpClient/HttpPluginClientBuilder.php b/src/HttpClient/HttpPluginClientBuilder.php index de6928e..331e5c2 100644 --- a/src/HttpClient/HttpPluginClientBuilder.php +++ b/src/HttpClient/HttpPluginClientBuilder.php @@ -27,6 +27,7 @@ class HttpPluginClientBuilder private $pluginClient; /** @var RequestFactory|RequestFactoryInterface */ private $requestFactory; + /** @var StreamFactoryInterface */ private $streamFactory; /** @var Plugin[] */ private $plugins = []; @@ -73,7 +74,7 @@ public function addPlugin(Plugin $plugin) } /** - * @param string $pluginClass + * @param class-string $pluginClass */ public function removePlugin($pluginClass) { diff --git a/tests/HttpClient/HttpPluginClientBuilderTest.php b/tests/HttpClient/HttpPluginClientBuilderTest.php index 26c9e9c..8149421 100644 --- a/tests/HttpClient/HttpPluginClientBuilderTest.php +++ b/tests/HttpClient/HttpPluginClientBuilderTest.php @@ -26,6 +26,7 @@ public function testInvalidRequestFactory(): void { $this->expectException(\TypeError::class); $definitelyNotARequestFactory = new \stdClass; + /** @phpstan-ignore-next-line We are passing in an invalid type on purpose. */ new HttpPluginClientBuilder(new MockClient, $definitelyNotARequestFactory); } @@ -72,7 +73,9 @@ public function matches(RequestInterface $request): bool public static function provideRequestFactories(): iterable { yield [null]; + // Http\Message\RequestFactory yield [new GuzzleMessageFactory]; + // Psr\Http\Message\RequestFactoryInterface yield [new HttpFactory]; } }