Skip to content

Commit 0a4a96e

Browse files
committed
Introduce fetch API
1 parent 7c667fe commit 0a4a96e

File tree

14 files changed

+966
-9
lines changed

14 files changed

+966
-9
lines changed

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
}
5757
},
5858
"autoload": {
59+
"files": [
60+
"src/functions.php"
61+
],
5962
"psr-4": {
6063
"Phpro\\HttpTools\\": "src"
6164
}

src/Client/FetchClient.php

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phpro\HttpTools\Client;
6+
7+
use Http\Client\Common\Plugin\ErrorPlugin;
8+
use Http\Client\Common\Plugin\HeaderSetPlugin;
9+
use Http\Client\Common\PluginClient;
10+
use Phpro\HttpTools\Request\Request;
11+
use Psr\Http\Client\ClientInterface;
12+
use Psr\Http\Message\ResponseInterface;
13+
use Webmozart\Assert\Assert;
14+
15+
/**
16+
* This class is inspired on the JS fetch() function:.
17+
*
18+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
19+
*
20+
* It also contains aliases, just like axios does:
21+
* @see https://axios-http.com/docs/api_intro
22+
*
23+
* fetch(url[, config])
24+
* get(url[, config])
25+
* delete(url[, config])
26+
* head(url[, config])
27+
* options(url[, config])
28+
* post(url[, data[, config]])
29+
* put(url[, data[, config]])
30+
* patch(url[, data[, config]])
31+
*
32+
* It is linked to this package, so that you can use the transport features as well.
33+
* This makes it possible to e.g. directly parse JSON inside the fetch() function.
34+
*
35+
* @template InstanceData
36+
* @template InstanceTransportRequest
37+
* @template InstanceTransportResponse
38+
*/
39+
final class FetchClient
40+
{
41+
/**
42+
* @var FetchConfig<InstanceData, InstanceTransportRequest, InstanceTransportResponse>|null
43+
*/
44+
private ?FetchConfig $config;
45+
46+
/**
47+
* @param FetchConfig<InstanceData, InstanceTransportRequest, InstanceTransportResponse>|null $config
48+
*/
49+
private function __construct(
50+
?FetchConfig $config = null
51+
) {
52+
$this->config = $config;
53+
}
54+
55+
/**
56+
* @template NewInstanceData
57+
* @template NewInstanceTransportRequest
58+
* @template NewInstanceTransportResponse
59+
*
60+
* @param FetchConfig<InstanceData, InstanceTransportRequest, InstanceTransportResponse>|null $config
61+
*
62+
* @return self<NewInstanceData, NewInstanceTransportRequest, NewInstanceTransportResponse>
63+
*/
64+
public static function create(
65+
?FetchConfig $config = null
66+
): self {
67+
return new self($config);
68+
}
69+
70+
/**
71+
* @template CallTimeData
72+
* @template CallTimeTransportRequest
73+
* @template CallTimeTransportResponse
74+
*
75+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
76+
*
77+
* @return ($config is null
78+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
79+
* : (CallTimeTransportResponse is mixed
80+
* ? CallTimeTransportResponse
81+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
82+
* )
83+
* )
84+
*/
85+
public function __invoke(string $uri, ?FetchConfig $config = null)
86+
{
87+
$allConfig = FetchConfig::defaults()->merge($this->config)->merge($config);
88+
89+
Assert::notNull($allConfig->method, 'Expected an HTTP method to be configured during fetch.');
90+
Assert::notNull($allConfig->transport, 'Expected an HTTP transport factory to be configured during fetch.');
91+
92+
$client = $this->configureClient($allConfig);
93+
$transport = ($allConfig->transport)($client);
94+
$request = new Request($allConfig->method, $uri, [], $allConfig->data);
95+
96+
return $transport($request);
97+
}
98+
99+
/**
100+
* @template CallTimeData
101+
* @template CallTimeTransportRequest
102+
* @template CallTimeTransportResponse
103+
*
104+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
105+
*
106+
* @return ($config is null
107+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
108+
* : (CallTimeTransportResponse is mixed
109+
* ? CallTimeTransportResponse
110+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
111+
* )
112+
* )
113+
*/
114+
public function get(string $uri, ?FetchConfig $config = null)
115+
{
116+
return ($this)($uri, $config);
117+
}
118+
119+
/**
120+
* @template CallTimeData
121+
* @template CallTimeTransportRequest
122+
* @template CallTimeTransportResponse
123+
*
124+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
125+
*
126+
* @return ($config is null
127+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
128+
* : (CallTimeTransportResponse is mixed
129+
* ? CallTimeTransportResponse
130+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
131+
* )
132+
* )
133+
*/
134+
public function options(string $uri, ?FetchConfig $config = null)
135+
{
136+
return ($this)($uri, FetchConfig::of(method: 'OPTIONS')->merge($config));
137+
}
138+
139+
/**
140+
* @template CallTimeData
141+
* @template CallTimeTransportRequest
142+
* @template CallTimeTransportResponse
143+
*
144+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
145+
*
146+
* @return ($config is null
147+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
148+
* : (CallTimeTransportResponse is mixed
149+
* ? CallTimeTransportResponse
150+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
151+
* )
152+
* )
153+
*/
154+
public function head(string $uri, ?FetchConfig $config = null)
155+
{
156+
return ($this)($uri, FetchConfig::of(method: 'HEAD')->merge($config));
157+
}
158+
159+
/**
160+
* @template CallTimeData
161+
* @template CallTimeTransportRequest
162+
* @template CallTimeTransportResponse
163+
*
164+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
165+
*
166+
* @return ($config is null
167+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
168+
* : (CallTimeTransportResponse is mixed
169+
* ? CallTimeTransportResponse
170+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
171+
* )
172+
* )
173+
*/
174+
public function delete(string $uri, ?FetchConfig $config = null)
175+
{
176+
return ($this)($uri, FetchConfig::of(method: 'DELETE')->merge($config));
177+
}
178+
179+
/**
180+
* @template CallTimeData
181+
* @template CallTimeTransportRequest
182+
* @template CallTimeTransportResponse
183+
*
184+
* @param CallTimeData $data
185+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
186+
*
187+
* @return ($config is null
188+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
189+
* : (CallTimeTransportResponse is mixed
190+
* ? CallTimeTransportResponse
191+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
192+
* )
193+
* )
194+
*/
195+
public function post(string $uri, mixed $data = null, ?FetchConfig $config = null)
196+
{
197+
return ($this)($uri, FetchConfig::of(
198+
method: 'POST',
199+
data: $data
200+
)->merge($config));
201+
}
202+
203+
/**
204+
* @template CallTimeData
205+
* @template CallTimeTransportRequest
206+
* @template CallTimeTransportResponse
207+
*
208+
* @param CallTimeData $data
209+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
210+
*
211+
* @return ($config is null
212+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
213+
* : (CallTimeTransportResponse is mixed
214+
* ? CallTimeTransportResponse
215+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
216+
* )
217+
* )
218+
*/
219+
public function put(string $uri, mixed $data = null, ?FetchConfig $config = null)
220+
{
221+
return ($this)($uri, FetchConfig::of(
222+
method: 'PUT',
223+
data: $data
224+
)->merge($config));
225+
}
226+
227+
/**
228+
* @template CallTimeData
229+
* @template CallTimeTransportRequest
230+
* @template CallTimeTransportResponse
231+
*
232+
* @param CallTimeData $data
233+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
234+
*
235+
* @return ($config is null
236+
* ? (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
237+
* : (CallTimeTransportResponse is mixed
238+
* ? CallTimeTransportResponse
239+
* : (InstanceTransportResponse is mixed ? InstanceTransportResponse : ResponseInterface)
240+
* )
241+
* )
242+
*/
243+
public function patch(string $uri, mixed $data = null, ?FetchConfig $config = null)
244+
{
245+
return ($this)($uri, FetchConfig::of(
246+
method: 'PATCH',
247+
data: $data
248+
)->merge($config));
249+
}
250+
251+
private function configureClient(FetchConfig $config): ClientInterface
252+
{
253+
Assert::notNull($config->client, 'Expected an HTTP client to be configured during fetch.');
254+
255+
return new PluginClient(
256+
$config->client,
257+
[
258+
new ErrorPlugin(),
259+
...($config->headers ? [new HeaderSetPlugin($config->headers)] : []),
260+
...$config->plugins,
261+
]
262+
);
263+
}
264+
}

0 commit comments

Comments
 (0)