Skip to content

Commit 27b138c

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

File tree

5 files changed

+196
-1
lines changed

5 files changed

+196
-1
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
"monolog/monolog": "~1.11",
112112
"nyholm/psr7": "^1.0",
113113
"ocramius/proxy-manager": "^2.1",
114+
"php-http/httplug": "^1.0|^2.0",
114115
"predis/predis": "~1.1",
115116
"psr/http-client": "^1.0",
116117
"psr/simple-cache": "^1.0",

src/Symfony/Component/HttpClient/CHANGELOG.md

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

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

1010
4.3.0
1111
-----
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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\Exception\NetworkException;
15+
use Http\Client\Exception\RequestException;
16+
use Http\Client\HttpClient;
17+
use Http\Message\RequestFactory;
18+
use Http\Message\StreamFactory;
19+
use Http\Message\UriFactory;
20+
use Psr\Http\Client\ClientInterface;
21+
use Psr\Http\Client\NetworkExceptionInterface;
22+
use Psr\Http\Client\RequestExceptionInterface;
23+
use Psr\Http\Message\RequestInterface;
24+
use Psr\Http\Message\ResponseFactoryInterface;
25+
use Psr\Http\Message\ResponseInterface;
26+
use Psr\Http\Message\StreamFactoryInterface;
27+
use Psr\Http\Message\StreamInterface;
28+
use Psr\Http\Message\UriInterface;
29+
use Symfony\Contracts\HttpClient\HttpClientInterface;
30+
31+
if (!interface_exists(HttpClient::class)) {
32+
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".');
33+
}
34+
35+
if (!interface_exists(ClientInterface::class)) {
36+
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".');
37+
}
38+
39+
if (!interface_exists(RequestFactory::class)) {
40+
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".');
41+
}
42+
43+
/**
44+
* An adapter to turn a Symfony HttpClientInterface into an Httplug client.
45+
*
46+
* Run "composer require psr/http-client" to install the base ClientInterface. Run
47+
* "composer require nyholm/psr7" to install an efficient implementation of response
48+
* and stream factories with flex-provided autowiring aliases.
49+
*
50+
* @author Nicolas Grekas <p@tchwork.com>
51+
*/
52+
final class HttplugClient implements HttpClient, RequestFactory, StreamFactory, UriFactory
53+
{
54+
private $client;
55+
56+
public function __construct(HttpClientInterface $client = null, ResponseFactoryInterface $responseFactory = null, StreamFactoryInterface $streamFactory = null)
57+
{
58+
$this->client = new Psr18Client($client, $responseFactory, $streamFactory);
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function sendRequest(RequestInterface $request): ResponseInterface
65+
{
66+
try {
67+
return $this->client->sendRequest($request);
68+
} catch (RequestExceptionInterface $e) {
69+
throw new RequestException($e->getMessage(), $request, $e);
70+
} catch (NetworkExceptionInterface $e) {
71+
throw new NetworkException($e->getMessage(), $request, $e);
72+
}
73+
}
74+
75+
/**
76+
* {@inheritdoc}
77+
*/
78+
public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): RequestInterface
79+
{
80+
$request = $this->client
81+
->createRequest($method, $uri)
82+
->withProtocolVersion($protocolVersion)
83+
->withBody($this->createStream($body))
84+
;
85+
86+
foreach ($headers as $name => $value) {
87+
$request = $request->withAddedHeader($name, $value);
88+
}
89+
90+
return $request;
91+
}
92+
93+
/**
94+
* {@inheritdoc}
95+
*/
96+
public function createStream($body = null): StreamInterface
97+
{
98+
if ($body instanceof StreamInterface) {
99+
return $body;
100+
}
101+
102+
if (\is_string($body ?? '')) {
103+
return $this->client->createStream($body ?? '');
104+
}
105+
106+
if (\is_resource($body)) {
107+
return $this->client->createStreamFromResource($body);
108+
}
109+
110+
throw new \InvalidArgumentException(sprintf('%s() expects string, resource or StreamInterface, %s given.', __METHOD__, \gettype($body)));
111+
}
112+
113+
/**
114+
* {@inheritdoc}
115+
*/
116+
public function createUri($uri = ''): UriInterface
117+
{
118+
return $uri instanceof UriInterface ? $uri : $this->client->createUri($uri);
119+
}
120+
}
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: 2 additions & 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
},
@@ -26,6 +27,7 @@
2627
},
2728
"require-dev": {
2829
"nyholm/psr7": "^1.0",
30+
"php-http/httplug": "^1.0|^2.0",
2931
"psr/http-client": "^1.0",
3032
"symfony/http-kernel": "^4.3|^5.0",
3133
"symfony/process": "^4.2|^5.0"

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy