Skip to content

Commit c7f428a

Browse files
[HttpClient] add HttplugClient for compat with libs that need httplug v1 or v2
1 parent cdaa4d6 commit c7f428a

File tree

5 files changed

+222
-0
lines changed

5 files changed

+222
-0
lines changed

src/Symfony/Component/HttpClient/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* added `$response->cancel()`
8+
* added `HttplugClient`
89

910
4.3.0
1011
-----
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpClient;
13+
14+
use Http\Client\HttpClient;
15+
use Http\Message\RequestFactory;
16+
use Http\Message\StreamFactory;
17+
use Http\Message\UriFactory;
18+
use Nyholm\Psr7\Request;
19+
use Nyholm\Psr7\Response;
20+
use Nyholm\Psr7\Stream;
21+
use Nyholm\Psr7\Uri;
22+
use Psr\Http\Client\ClientInterface;
23+
use Psr\Http\Message\ResponseFactoryInterface;
24+
use Psr\Http\Message\StreamFactoryInterface;
25+
use Psr\Http\Message\UriInterface;
26+
use Symfony\Contracts\HttpClient\HttpClientInterface;
27+
28+
if (!interface_exists(ClientInterface::class)) {
29+
throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "psr/http-client" package is not installed. Try running "composer require nyholm/psr7".');
30+
}
31+
32+
if (!interface_exists(RequestFactory::class)) {
33+
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".');
34+
}
35+
36+
/**
37+
* An adapter to turn a Symfony HttpClientInterface into an Httplug client.
38+
*
39+
* Run "composer require nyholm/psr7" to install an efficient implementation of response
40+
* and stream factories with flex-provided autowiring aliases.
41+
*
42+
* @author Nicolas Grekas <p@tchwork.com>
43+
*/
44+
final class HttplugClient implements HttpClient, RequestFactory, StreamFactory, UriFactory
45+
{
46+
use Internal\HttplugClientTrait;
47+
48+
private $client;
49+
50+
public function __construct(HttpClientInterface $client = null, ResponseFactoryInterface $responseFactory = null, StreamFactoryInterface $streamFactory = null)
51+
{
52+
$this->client = new Psr18Client($client, $responseFactory, $streamFactory);
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1')
59+
{
60+
if (!class_exists(Request::class)) {
61+
throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
62+
}
63+
64+
return new Request($method, $uri, $headers, $body, $protocolVersion);
65+
}
66+
67+
/**
68+
* {@inheritdoc}
69+
*/
70+
public function createStream($body = null)
71+
{
72+
if (!class_exists(Stream::class)) {
73+
throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
74+
}
75+
76+
return Stream::create($body ?? '');
77+
}
78+
79+
/**
80+
* {@inheritdoc}
81+
*/
82+
public function createUri($uri = ''): UriInterface
83+
{
84+
if ($uri instanceof UriInterface) {
85+
return $uri;
86+
}
87+
88+
if (!class_exists(Uri::class)) {
89+
throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
90+
}
91+
92+
return new Uri($uri);
93+
}
94+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpClient\Internal;
13+
14+
use Http\Client\Exception\NetworkException;
15+
use Http\Client\Exception\RequestException;
16+
use Http\Client\HttpClient;
17+
use Psr\Http\Client\ClientInterface;
18+
use Psr\Http\Client\NetworkExceptionInterface;
19+
use Psr\Http\Client\RequestExceptionInterface;
20+
use Psr\Http\Message\RequestInterface;
21+
use Psr\Http\Message\ResponseInterface;
22+
23+
if (is_subclass_of(HttpClient::class, ClientInterface::class)) {
24+
trait HttplugClientTrait
25+
{
26+
public function sendRequest(RequestInterface $request): ResponseInterface
27+
{
28+
try {
29+
return $this->client->sendRequest($request);
30+
} catch (RequestExceptionInterface $e) {
31+
throw new RequestException($e->getMessage(), $request, $e);
32+
} catch (NetworkExceptionInterface $e) {
33+
throw new NetworkException($e->getMessage(), $request, $e);
34+
}
35+
}
36+
}
37+
} else {
38+
trait HttplugClientTrait
39+
{
40+
/**
41+
* @return ResponseInterface
42+
*/
43+
public function sendRequest(RequestInterface $request)
44+
{
45+
try {
46+
return $this->client->sendRequest($request);
47+
} catch (RequestExceptionInterface $e) {
48+
throw new RequestException($e->getMessage(), $request, $e);
49+
} catch (NetworkExceptionInterface $e) {
50+
throw new NetworkException($e->getMessage(), $request, $e);
51+
}
52+
}
53+
}
54+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpClient\Tests;
13+
14+
use Http\Client\Exception\NetworkException;
15+
use Http\Client\Exception\RequestException;
16+
use PHPUnit\Framework\TestCase;
17+
use Symfony\Component\HttpClient\HttplugClient;
18+
use Symfony\Component\HttpClient\NativeHttpClient;
19+
use Symfony\Contracts\HttpClient\Test\TestHttpServer;
20+
21+
class HttplugClientTest extends TestCase
22+
{
23+
private static $server;
24+
25+
public static function setUpBeforeClass()
26+
{
27+
TestHttpServer::start();
28+
}
29+
30+
public function testSendRequest()
31+
{
32+
$client = new HttplugClient(new NativeHttpClient());
33+
34+
$response = $client->sendRequest($client->createRequest('GET', 'http://localhost:8057'));
35+
36+
$this->assertSame(200, $response->getStatusCode());
37+
$this->assertSame('application/json', $response->getHeaderLine('content-type'));
38+
39+
$body = json_decode((string) $response->getBody(), true);
40+
41+
$this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']);
42+
}
43+
44+
public function testPostRequest()
45+
{
46+
$client = new HttplugClient(new NativeHttpClient());
47+
48+
$request = $client->createRequest('POST', 'http://localhost:8057/post')
49+
->withBody($client->createStream('foo=0123456789'));
50+
51+
$response = $client->sendRequest($request);
52+
$body = json_decode((string) $response->getBody(), true);
53+
54+
$this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body);
55+
}
56+
57+
public function testNetworkException()
58+
{
59+
$client = new HttplugClient(new NativeHttpClient());
60+
61+
$this->expectException(NetworkException::class);
62+
$client->sendRequest($client->createRequest('GET', 'http://localhost:8058'));
63+
}
64+
65+
public function testRequestException()
66+
{
67+
$client = new HttplugClient(new NativeHttpClient());
68+
69+
$this->expectException(RequestException::class);
70+
$client->sendRequest($client->createRequest('BAD.METHOD', 'http://localhost:8057'));
71+
}
72+
}

src/Symfony/Component/HttpClient/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
}
1616
],
1717
"provide": {
18+
"php-http/client-implementation": "*",
1819
"psr/http-client-implementation": "1.0",
1920
"symfony/http-client-implementation": "1.1"
2021
},

0 commit comments

Comments
 (0)