Skip to content
Permalink
Browse files

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...
nicolas-grekas committed Jun 11, 2019
2 parents e54b62c + 28674b1 commit 9d7e9fcac9b647e6b6df25bc35020c5b5734273c
@@ -111,6 +111,7 @@
"monolog/monolog": "~1.11",
"nyholm/psr7": "^1.0",
"ocramius/proxy-manager": "^2.1",
"php-http/httplug": "^1.0|^2.0",
"predis/predis": "~1.1",
"psr/http-client": "^1.0",
"psr/simple-cache": "^1.0",
@@ -5,7 +5,7 @@ CHANGELOG
-----

* made `Psr18Client` implement relevant PSR-17 factories
* added `$response->cancel()`
* added `HttplugClient`

4.3.0
-----
@@ -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);
}
}
@@ -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'));
}
}
@@ -15,6 +15,7 @@
}
],
"provide": {
"php-http/client-implementation": "*",
"psr/http-client-implementation": "1.0",
"symfony/http-client-implementation": "1.1"
},
@@ -26,6 +27,7 @@
},
"require-dev": {
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
"symfony/http-kernel": "^4.3|^5.0",
"symfony/process": "^4.2|^5.0"

0 comments on commit 9d7e9fc

Please sign in to comment.
You can’t perform that action at this time.