Skip to content

Commit 9d7e9fc

Browse files
feature #31976 [HttpClient] add HttplugClient for compat with libs that 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
2 parents e54b62c + 28674b1 commit 9d7e9fc

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