Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #31976 [HttpClient] add HttplugClient for compat with libs th…
…at need httplug v1 or v2 (nicolas-grekas) This PR was merged into the 4.4 branch. Discussion ---------- [HttpClient] add HttplugClient for compat with libs that need httplug v1 or v2 | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Many libs still depend on httplug: https://packagist.org/packages/php-http/client-implementation/dependents Until they're all updated to PSR-18 or SFContracts, this PR provides an adapter for injecting a Symfony HttpClient into httplug-compatible classes, v1 or v2. Commits ------- 28674b1 [HttpClient] add HttplugClient for compat with libs that need httplug v1 or v2
- Loading branch information
Showing
5 changed files
with
196 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?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; | ||
|
||
use Http\Client\Exception\NetworkException; | ||
use Http\Client\Exception\RequestException; | ||
use Http\Client\HttpClient; | ||
use Http\Message\RequestFactory; | ||
use Http\Message\StreamFactory; | ||
use Http\Message\UriFactory; | ||
use Psr\Http\Client\ClientInterface; | ||
use Psr\Http\Client\NetworkExceptionInterface; | ||
use Psr\Http\Client\RequestExceptionInterface; | ||
use Psr\Http\Message\RequestInterface; | ||
use Psr\Http\Message\ResponseFactoryInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\StreamFactoryInterface; | ||
use Psr\Http\Message\StreamInterface; | ||
use Psr\Http\Message\UriInterface; | ||
use Symfony\Contracts\HttpClient\HttpClientInterface; | ||
|
||
if (!interface_exists(HttpClient::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(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".'); | ||
} | ||
|
||
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 nyholm/psr7".'); | ||
} | ||
|
||
/** | ||
* An adapter to turn a Symfony HttpClientInterface into an Httplug client. | ||
* | ||
* 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. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
*/ | ||
final class HttplugClient implements HttpClient, RequestFactory, StreamFactory, UriFactory | ||
{ | ||
private $client; | ||
|
||
public function __construct(HttpClientInterface $client = null, ResponseFactoryInterface $responseFactory = null, StreamFactoryInterface $streamFactory = null) | ||
{ | ||
$this->client = new Psr18Client($client, $responseFactory, $streamFactory); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function sendRequest(RequestInterface $request): ResponseInterface | ||
{ | ||
try { | ||
return $this->client->sendRequest($request); | ||
} catch (RequestExceptionInterface $e) { | ||
throw new RequestException($e->getMessage(), $request, $e); | ||
} catch (NetworkExceptionInterface $e) { | ||
throw new NetworkException($e->getMessage(), $request, $e); | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): RequestInterface | ||
{ | ||
$request = $this->client | ||
->createRequest($method, $uri) | ||
->withProtocolVersion($protocolVersion) | ||
->withBody($this->createStream($body)) | ||
; | ||
|
||
foreach ($headers as $name => $value) { | ||
$request = $request->withAddedHeader($name, $value); | ||
} | ||
|
||
return $request; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function createStream($body = null): StreamInterface | ||
{ | ||
if ($body instanceof StreamInterface) { | ||
return $body; | ||
} | ||
|
||
if (\is_string($body ?? '')) { | ||
return $this->client->createStream($body ?? ''); | ||
} | ||
|
||
if (\is_resource($body)) { | ||
return $this->client->createStreamFromResource($body); | ||
} | ||
|
||
throw new \InvalidArgumentException(sprintf('%s() expects string, resource or StreamInterface, %s given.', __METHOD__, \gettype($body))); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function createUri($uri = ''): UriInterface | ||
{ | ||
return $uri instanceof UriInterface ? $uri : $this->client->createUri($uri); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
<?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\Tests; | ||
|
||
use Http\Client\Exception\NetworkException; | ||
use Http\Client\Exception\RequestException; | ||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\HttpClient\HttplugClient; | ||
use Symfony\Component\HttpClient\NativeHttpClient; | ||
use Symfony\Contracts\HttpClient\Test\TestHttpServer; | ||
|
||
class HttplugClientTest extends TestCase | ||
{ | ||
private static $server; | ||
|
||
public static function setUpBeforeClass() | ||
{ | ||
TestHttpServer::start(); | ||
} | ||
|
||
public function testSendRequest() | ||
{ | ||
$client = new HttplugClient(new NativeHttpClient()); | ||
|
||
$response = $client->sendRequest($client->createRequest('GET', 'http://localhost:8057')); | ||
|
||
$this->assertSame(200, $response->getStatusCode()); | ||
$this->assertSame('application/json', $response->getHeaderLine('content-type')); | ||
|
||
$body = json_decode((string) $response->getBody(), true); | ||
|
||
$this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']); | ||
} | ||
|
||
public function testPostRequest() | ||
{ | ||
$client = new HttplugClient(new NativeHttpClient()); | ||
|
||
$request = $client->createRequest('POST', 'http://localhost:8057/post') | ||
->withBody($client->createStream('foo=0123456789')); | ||
|
||
$response = $client->sendRequest($request); | ||
$body = json_decode((string) $response->getBody(), true); | ||
|
||
$this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body); | ||
} | ||
|
||
public function testNetworkException() | ||
{ | ||
$client = new HttplugClient(new NativeHttpClient()); | ||
|
||
$this->expectException(NetworkException::class); | ||
$client->sendRequest($client->createRequest('GET', 'http://localhost:8058')); | ||
} | ||
|
||
public function testRequestException() | ||
{ | ||
$client = new HttplugClient(new NativeHttpClient()); | ||
|
||
$this->expectException(RequestException::class); | ||
$client->sendRequest($client->createRequest('BAD.METHOD', 'http://localhost:8057')); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters