Skip to content

[HttpClient] Favor php-http/discovery instead of nyholm/psr7 #50225

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
"monolog/monolog": "^1.25.1|^2",
"nyholm/psr7": "^1.0",
"pda/pheanstalk": "^4.0",
"php-http/discovery": "^1.15",
"php-http/httplug": "^1.0|^2.0",
"php-http/message-factory": "^1.0",
"phpdocumentor/reflection-docblock": "^5.2",
Expand Down Expand Up @@ -171,6 +172,7 @@
},
"config": {
"allow-plugins": {
"php-http/discovery": false,
"symfony/runtime": true
}
},
Expand Down
5 changes: 1 addition & 4 deletions src/Symfony/Component/ErrorHandler/DebugClassLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use Prophecy\Prophecy\ProphecySubjectInterface;
use ProxyManager\Proxy\ProxyInterface;
use Symfony\Component\ErrorHandler\Internal\TentativeTypes;
use Symfony\Component\HttpClient\HttplugClient;
use Symfony\Component\VarExporter\LazyObjectInterface;

/**
Expand Down Expand Up @@ -422,9 +421,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
if (!isset(self::$checkedClasses[$use])) {
$this->checkClass($use);
}
if (isset(self::$deprecated[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen) && !isset(self::$deprecated[$class])
&& !(HttplugClient::class === $class && \in_array($use, [\Http\Client\HttpClient::class, \Http\Message\RequestFactory::class, \Http\Message\StreamFactory::class, \Http\Message\UriFactory::class], true))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great that we don't need to do special cases here anymore

) {
if (isset(self::$deprecated[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen) && !isset(self::$deprecated[$class])) {
$type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait');
$verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');

Expand Down
64 changes: 27 additions & 37 deletions src/Symfony/Component/HttpClient/HttplugClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,9 @@
use Http\Client\Exception\NetworkException;
use Http\Client\Exception\RequestException;
use Http\Client\HttpAsyncClient;
use Http\Client\HttpClient as HttplugInterface;
use Http\Discovery\Exception\NotFoundException;
use Http\Discovery\Psr17Factory;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Message\RequestFactory;
use Http\Message\StreamFactory;
use Http\Message\UriFactory;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7\Factory\Psr17Factory as NyholmPsr17Factory;
use Nyholm\Psr7\Request;
use Nyholm\Psr7\Uri;
use Psr\Http\Client\ClientInterface;
Expand All @@ -36,37 +32,32 @@
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Symfony\Component\HttpClient\Internal\HttplugWaitLoop;
use Symfony\Component\HttpClient\Internal\LegacyHttplugInterface;
use Symfony\Component\HttpClient\Response\HttplugPromise;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Symfony\Contracts\Service\ResetInterface;

if (!interface_exists(HttplugInterface::class)) {
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/httplug" package is not installed. Try running "composer require php-http/httplug".');
}

if (!interface_exists(RequestFactory::class)) {
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/message-factory" package is not installed. Try running "composer require php-http/message-factory".');
if (!interface_exists(HttpAsyncClient::class)) {
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/httplug" package is not installed. Try running "php-http/discovery php-http/async-client-implementation:*".');
}

if (!interface_exists(RequestFactoryInterface::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as the "psr/http-factory" package is not installed. Try running "composer require nyholm/psr7".');
}

if (!interface_exists(ClientInterface::class)) {
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "psr/http-client" package is not installed. Try running "composer require psr/http-client".');
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as the "psr/http-factory" package is not installed. Try running "composer require php-http/discovery psr/http-factory-implementation:*".');
}

/**
* An adapter to turn a Symfony HttpClientInterface into an Httplug client.
*
* Run "composer require nyholm/psr7" to install an efficient implementation of response
* and stream factories with flex-provided autowiring aliases.
* In comparison to Psr18Client, this client supports asynchronous requests.
*
* Run "composer require php-http/discovery php-http/async-client-implementation:*"
* to get the required dependencies.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
final class HttplugClient implements ClientInterface, HttplugInterface, HttpAsyncClient, RequestFactoryInterface, StreamFactoryInterface, UriFactoryInterface, RequestFactory, StreamFactory, UriFactory, ResetInterface
final class HttplugClient implements ClientInterface, HttpAsyncClient, RequestFactoryInterface, StreamFactoryInterface, UriFactoryInterface, ResetInterface, LegacyHttplugInterface
{
private HttpClientInterface $client;
private ResponseFactoryInterface $responseFactory;
Expand All @@ -86,17 +77,16 @@ public function __construct(HttpClientInterface $client = null, ResponseFactoryI
$this->promisePool = class_exists(Utils::class) ? new \SplObjectStorage() : null;

if (null === $responseFactory || null === $streamFactory) {
if (!class_exists(Psr17Factory::class) && !class_exists(Psr17FactoryDiscovery::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been provided. Try running "composer require nyholm/psr7".');
if (class_exists(Psr17Factory::class)) {
$psr17Factory = new Psr17Factory();
} elseif (class_exists(NyholmPsr17Factory::class)) {
$psr17Factory = new NyholmPsr17Factory();
} else {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been provided. Try running "composer require php-http/discovery psr/http-factory-implementation:*".');
}

try {
$psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
$responseFactory ??= $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
$streamFactory ??= $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
} catch (NotFoundException $e) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been found. Try running "composer require nyholm/psr7".', 0, $e);
}
$responseFactory ??= $psr17Factory;
$streamFactory ??= $psr17Factory;
}

$this->responseFactory = $responseFactory;
Expand Down Expand Up @@ -170,12 +160,12 @@ public function createRequest($method, $uri, array $headers = [], $body = null,
}
if ($this->responseFactory instanceof RequestFactoryInterface) {
$request = $this->responseFactory->createRequest($method, $uri);
} elseif (class_exists(Request::class)) {
$request = new Request($method, $uri);
} elseif (class_exists(Psr17FactoryDiscovery::class)) {
$request = Psr17FactoryDiscovery::findRequestFactory()->createRequest($method, $uri);
} elseif (class_exists(Request::class)) {
$request = new Request($method, $uri);
} else {
throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
throw new \LogicException(sprintf('You cannot use "%s()" as no PSR-17 factories have been found. Try running "composer require php-http/discovery psr/http-factory-implementation:*".', __METHOD__));
}

$request = $request
Expand Down Expand Up @@ -245,15 +235,15 @@ public function createUri($uri = ''): UriInterface
return $this->responseFactory->createUri($uri);
}

if (class_exists(Uri::class)) {
return new Uri($uri);
}

if (class_exists(Psr17FactoryDiscovery::class)) {
return Psr17FactoryDiscovery::findUrlFactory()->createUri($uri);
}

throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
if (class_exists(Uri::class)) {
return new Uri($uri);
}

throw new \LogicException(sprintf('You cannot use "%s()" as no PSR-17 factories have been found. Try running "composer require php-http/discovery psr/http-factory-implementation:*".', __METHOD__));
}

public function __sleep(): array
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\HttpClient\Internal;

use Http\Client\HttpClient;
use Http\Message\RequestFactory;
use Http\Message\StreamFactory;
use Http\Message\UriFactory;

if (interface_exists(RequestFactory::class)) {
/**
* @internal
*
* @deprecated since Symfony 6.3
*/
interface LegacyHttplugInterface extends HttpClient, RequestFactory, StreamFactory, UriFactory
{
}
} else {
/**
* @internal
*
* @deprecated since Symfony 6.3
*/
interface LegacyHttplugInterface extends HttpClient
{
}
}
54 changes: 26 additions & 28 deletions src/Symfony/Component/HttpClient/Psr18Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

namespace Symfony\Component\HttpClient;

use Http\Discovery\Exception\NotFoundException;
use Http\Discovery\Psr17Factory;
use Http\Discovery\Psr17FactoryDiscovery;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7\Factory\Psr17Factory as NyholmPsr17Factory;
use Nyholm\Psr7\Request;
use Nyholm\Psr7\Uri;
use Psr\Http\Client\ClientInterface;
Expand All @@ -33,20 +33,19 @@
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Service\ResetInterface;

if (!interface_exists(RequestFactoryInterface::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-factory" package is not installed. Try running "composer require nyholm/psr7".');
if (!interface_exists(ClientInterface::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-client" package is not installed. Try running "composer require php-http/discovery psr/http-client-implementation:*".');
}

if (!interface_exists(ClientInterface::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-client" package is not installed. Try running "composer require psr/http-client".');
if (!interface_exists(RequestFactoryInterface::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-factory" package is not installed. Try running "composer require php-http/discovery psr/http-factory-implementation:*".');
}

/**
* An adapter to turn a Symfony HttpClientInterface into a PSR-18 ClientInterface.
*
* Run "composer require psr/http-client" to install the base ClientInterface. Run
* "composer require nyholm/psr7" to install an efficient implementation of response
* and stream factories with flex-provided autowiring aliases.
* Run "composer require php-http/discovery psr/http-client-implementation:*"
* to get the required dependencies.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
Expand All @@ -62,17 +61,16 @@ public function __construct(HttpClientInterface $client = null, ResponseFactoryI
$streamFactory ??= $responseFactory instanceof StreamFactoryInterface ? $responseFactory : null;

if (null === $responseFactory || null === $streamFactory) {
if (!class_exists(Psr17Factory::class) && !class_exists(Psr17FactoryDiscovery::class)) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as no PSR-17 factories have been provided. Try running "composer require nyholm/psr7".');
if (class_exists(Psr17Factory::class)) {
$psr17Factory = new Psr17Factory();
} elseif (class_exists(NyholmPsr17Factory::class)) {
$psr17Factory = new NyholmPsr17Factory();
} else {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as no PSR-17 factories have been provided. Try running "composer require php-http/discovery psr/http-factory-implementation:*".');
}

try {
$psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
$responseFactory ??= $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
$streamFactory ??= $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
} catch (NotFoundException $e) {
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been found. Try running "composer require nyholm/psr7".', 0, $e);
}
$responseFactory ??= $psr17Factory;
$streamFactory ??= $psr17Factory;
}

$this->responseFactory = $responseFactory;
Expand Down Expand Up @@ -142,15 +140,15 @@ public function createRequest(string $method, $uri): RequestInterface
return $this->responseFactory->createRequest($method, $uri);
}

if (class_exists(Request::class)) {
return new Request($method, $uri);
}

if (class_exists(Psr17FactoryDiscovery::class)) {
return Psr17FactoryDiscovery::findRequestFactory()->createRequest($method, $uri);
}

throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
if (class_exists(Request::class)) {
return new Request($method, $uri);
}

throw new \LogicException(sprintf('You cannot use "%s()" as no PSR-17 factories have been found. Try running "composer require php-http/discovery psr/http-factory-implementation:*".', __METHOD__));
}

public function createStream(string $content = ''): StreamInterface
Expand Down Expand Up @@ -180,15 +178,15 @@ public function createUri(string $uri = ''): UriInterface
return $this->responseFactory->createUri($uri);
}

if (class_exists(Uri::class)) {
return new Uri($uri);
}

if (class_exists(Psr17FactoryDiscovery::class)) {
return Psr17FactoryDiscovery::findUrlFactory()->createUri($uri);
}

throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
if (class_exists(Uri::class)) {
return new Uri($uri);
}

throw new \LogicException(sprintf('You cannot use "%s()" as no PSR-17 factories have been found. Try running "composer require php-http/discovery psr/http-factory-implementation:*".', __METHOD__));
}

public function reset(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ public function testNormalizeBodyMultipartForwardStream($stream)
public static function provideNormalizeBodyMultipartForwardStream()
{
yield 'native' => [fopen('https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png', 'r')];

if (!\defined('OPENSSL_DEFAULT_STREAM_CIPHERS')) {
return;
}

yield 'symfony' => [HttpClient::create()->request('GET', 'https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png')->toStream()];
}

Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpClient/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@
"guzzlehttp/promises": "^1.4",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"php-http/message-factory": "^1.0",
"psr/http-client": "^1.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0",
"symfony/process": "^5.4|^6.0",
"symfony/stopwatch": "^5.4|^6.0"
},
"conflict": {
"php-http/discovery": "<1.15",
"symfony/http-foundation": "<6.3"
},
"autoload": {
Expand Down