Skip to content

Commit 6acb643

Browse files
authored
Opening the possibility of injecting a PSR-18 compatible HTTP Client (mghoneimy#33)
* Opening the possibility of injecting a PSR-18 compatible HTTP Client * The use of function stream_for() is discouraged * Dropping $authorizationHeaders property * README update * Providing explanation for removing the headers from the options before creating a guzzle instance
1 parent 398ae8d commit 6acb643

File tree

6 files changed

+96
-59
lines changed

6 files changed

+96
-59
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,20 @@ $client = new Client(
353353
);
354354
```
355355

356+
357+
It is possible to use your own preconfigured HTTP client that implements the [PSR-18 interface](https://www.php-fig.org/psr/psr-18/).
358+
359+
Example:
360+
361+
```php
362+
$client = new Client(
363+
'http://api.graphql.com',
364+
[],
365+
[],
366+
$myHttpClient
367+
);
368+
```
369+
356370
# Running Queries
357371

358372
## Result Formatting

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
"require": {
3232
"php": "^7.1",
3333
"ext-json": "*",
34+
"psr/http-message": "^1.0",
35+
"psr/http-client": "^1.0",
3436
"guzzlehttp/guzzle": "^6.3"
3537
},
3638
"require-dev": {

src/Client.php

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
use GraphQL\Exception\QueryError;
66
use GraphQL\QueryBuilder\QueryBuilderInterface;
7+
use GraphQL\Util\GuzzleAdapter;
78
use GuzzleHttp\Exception\ClientException;
9+
use GuzzleHttp\Psr7\Request;
10+
use Psr\Http\Client\ClientInterface;
811
use TypeError;
12+
use GuzzleHttp\Psr7;
913

1014
/**
1115
* Class Client
@@ -20,34 +24,41 @@ class Client
2024
protected $endpointUrl;
2125

2226
/**
23-
* @var array
24-
*/
25-
protected $authorizationHeaders;
26-
27-
/**
28-
* @var \GuzzleHttp\Client
27+
* @var ClientInterface
2928
*/
3029
protected $httpClient;
3130

3231
/**
3332
* @var array
3433
*/
35-
protected $httpOptions;
36-
34+
protected $httpHeaders;
3735

3836
/**
3937
* Client constructor.
4038
*
4139
* @param string $endpointUrl
4240
* @param array $authorizationHeaders
4341
* @param array $httpOptions
42+
* @param ClientInterface $httpClient
4443
*/
45-
public function __construct(string $endpointUrl, array $authorizationHeaders = [], array $httpOptions = [])
44+
public function __construct(string $endpointUrl, array $authorizationHeaders = [], array $httpOptions = [], ClientInterface $httpClient = null)
4645
{
46+
$headers = array_merge(
47+
$authorizationHeaders,
48+
$httpOptions['headers'] ?? [],
49+
['Content-Type' => 'application/json']
50+
);
51+
52+
/**
53+
* All headers will be set on the request objects explicitly,
54+
* Guzzle doesn't have to care about them at this point, so to avoid any conflicts
55+
* we are removing the headers from the options
56+
*/
57+
unset($httpOptions['headers']);
58+
4759
$this->endpointUrl = $endpointUrl;
48-
$this->authorizationHeaders = $authorizationHeaders;
49-
$this->httpClient = new \GuzzleHttp\Client();
50-
$this->httpOptions = $httpOptions;
60+
$this->httpClient = $httpClient ?? new GuzzleAdapter(new \GuzzleHttp\Client($httpOptions));
61+
$this->httpHeaders = $headers;
5162
}
5263

5364
/**
@@ -75,33 +86,28 @@ public function runQuery($query, bool $resultsAsArray = false, array $variables
7586
* @param string $queryString
7687
* @param bool $resultsAsArray
7788
* @param array $variables
89+
* @param
7890
*
7991
* @return Results
8092
* @throws QueryError
8193
*/
8294
public function runRawQuery(string $queryString, $resultsAsArray = false, array $variables = []): Results
8395
{
84-
// Set request headers for authorization and content type
85-
if (!empty($this->authorizationHeaders)) {
86-
$options['headers'] = $this->authorizationHeaders;
87-
}
96+
$request = new Request('POST', $this->endpointUrl);
8897

89-
// Set request options for \GuzzleHttp\Client
90-
if (!empty($this->httpOptions)) {
91-
$options = $this->httpOptions;
98+
foreach($this->httpHeaders as $header => $value) {
99+
$request = $request->withHeader($header, $value);
92100
}
93101

94-
$options['headers']['Content-Type'] = 'application/json';
95-
96102
// Convert empty variables array to empty json object
97103
if (empty($variables)) $variables = (object) null;
98104
// Set query in the request body
99-
$bodyArray = ['query' => (string) $queryString, 'variables' => $variables];
100-
$options['body'] = json_encode($bodyArray);
105+
$bodyArray = ['query' => (string) $queryString, 'variables' => $variables];
106+
$request = $request->withBody(Psr7\stream_for(json_encode($bodyArray)));
101107

102108
// Send api request and get response
103109
try {
104-
$response = $this->httpClient->post($this->endpointUrl, $options);
110+
$response = $this->httpClient->sendRequest($request);
105111
}
106112
catch (ClientException $exception) {
107113
$response = $exception->getResponse();
@@ -114,8 +120,6 @@ public function runRawQuery(string $queryString, $resultsAsArray = false, array
114120
}
115121

116122
// Parse response to extract results
117-
$results = new Results($response, $resultsAsArray);
118-
119-
return $results;
123+
return new Results($response, $resultsAsArray);
120124
}
121125
}

src/Util/GuzzleAdapter.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace GraphQL\Util;
4+
5+
use GuzzleHttp\ClientInterface;
6+
use GuzzleHttp\Exception\GuzzleException;
7+
use Psr\Http\Client;
8+
use Psr\Http\Message\RequestInterface;
9+
use Psr\Http\Message\ResponseInterface;
10+
11+
class GuzzleAdapter implements Client\ClientInterface
12+
{
13+
/**
14+
* @var ClientInterface
15+
*/
16+
private $client;
17+
18+
/**
19+
* GuzzleAdapter constructor.
20+
*
21+
* @param ClientInterface $client
22+
*/
23+
public function __construct(ClientInterface $client)
24+
{
25+
$this->client = $client;
26+
}
27+
28+
/**
29+
* @param RequestInterface $request
30+
*
31+
* @return ResponseInterface
32+
* @throws GuzzleException
33+
*/
34+
public function sendRequest(RequestInterface $request): ResponseInterface
35+
{
36+
/**
37+
* We are not catching and converting the guzzle exceptions to psr-18 exceptions
38+
* for backward-compatibility sake
39+
*/
40+
41+
return $this->client->send($request);
42+
}
43+
}

tests/ClientTest.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use GraphQL\Exception\QueryError;
77
use GraphQL\QueryBuilder\QueryBuilder;
88
use GraphQL\RawObject;
9+
use GraphQL\Util\GuzzleAdapter;
910
use GuzzleHttp\Exception\ClientException;
1011
use GuzzleHttp\Exception\ConnectException;
1112
use GuzzleHttp\Exception\ServerException;
@@ -41,7 +42,7 @@ protected function setUp(): void
4142
{
4243
$this->mockHandler = new MockHandler();
4344
$handler = HandlerStack::create($this->mockHandler);
44-
$this->client = new MockClient('', $handler);
45+
$this->client = new Client('', [], ['handler' => $handler]);
4546
}
4647

4748
/**
@@ -61,16 +62,16 @@ public function testConstructClient()
6162
$mockHandler->append(new Response(200));
6263
$mockHandler->append(new Response(200));
6364

64-
$client = new MockClient('', $handler);
65+
$client = new Client('', [], ['handler' => $handler]);
6566
$client->runRawQuery('query_string');
6667

67-
$client = new MockClient('', $handler, ['Authorization' => 'Basic xyz']);
68+
$client = new Client('', ['Authorization' => 'Basic xyz'], ['handler' => $handler]);
6869
$client->runRawQuery('query_string');
6970

70-
$client = new MockClient('', $handler);
71+
$client = new Client('', [], ['handler' => $handler]);
7172
$client->runRawQuery('query_string', false, ['name' => 'val']);
7273

73-
$client = new MockClient('', $handler, ['Authorization' => 'Basic xyz'], ['headers' => [ 'Authorization' => 'Basic zyx', 'User-Agent' => 'test' ]]);
74+
$client = new Client('', ['Authorization' => 'Basic xyz'], ['handler' => $handler, 'headers' => [ 'Authorization' => 'Basic zyx', 'User-Agent' => 'test' ]]);
7475
$client->runRawQuery('query_string');
7576

7677
/** @var Request $firstRequest */

tests/MockClient.php

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)