Skip to content

Commit d7fb885

Browse files
committed
Introduce fetch API
1 parent 7c667fe commit d7fb885

18 files changed

Lines changed: 1122 additions & 100288 deletions

File tree

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
}

phive.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<phive xmlns="https://phar.io/phive">
3-
<phar name="psalm" version="^4.30.0" installed="4.30.0" location="./tools/psalm" copy="true"/>
4-
<phar name="phpunit" version="^9.5.25" installed="9.5.26" location="./tools/phpunit" copy="true"/>
3+
<phar name="psalm" version="^5.4.0" installed="5.4.0" location="./tools/psalm.phar" copy="true"/>
4+
<phar name="phpunit" version="^9.5.25" installed="9.5.26" location="./tools/phpunit.phar" copy="true"/>
55
<phar name="php-cs-fixer" version="^3.13.0" installed="3.13.0" location="./tools/php-cs-fixer.phar" copy="true"/>
66
</phive>

src/Client/FetchClient.php

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
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+
* @pure
57+
*
58+
* @return self<null, never, never>
59+
*/
60+
public static function default(): self
61+
{
62+
return new self();
63+
}
64+
65+
/**
66+
* @pure
67+
*
68+
* @template NewInstanceData
69+
* @template NewInstanceTransportRequest
70+
* @template NewInstanceTransportResponse
71+
*
72+
* @param FetchConfig<InstanceData, InstanceTransportRequest, InstanceTransportResponse> $config
73+
*
74+
* @return self<NewInstanceData, NewInstanceTransportRequest, NewInstanceTransportResponse>
75+
*/
76+
public static function configure(FetchConfig $config): self
77+
{
78+
return new self($config);
79+
}
80+
81+
/**
82+
* @template CallTimeData
83+
* @template CallTimeTransportRequest
84+
* @template CallTimeTransportResponse
85+
*
86+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
87+
*
88+
* @return (CallTimeTransportResponse is never
89+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
90+
* : CallTimeTransportResponse
91+
* )
92+
*/
93+
public function __invoke(string $uri, FetchConfig $config = null)
94+
{
95+
$allConfig = FetchConfig::defaults()->merge($this->config)->merge($config);
96+
97+
Assert::notNull($allConfig->method, 'Expected an HTTP method to be configured during fetch.');
98+
Assert::notNull($allConfig->transport, 'Expected an HTTP transport factory to be configured during fetch.');
99+
100+
$client = $this->configureClient($allConfig);
101+
$transport = ($allConfig->transport)($client);
102+
$request = new Request($allConfig->method, $uri, [], $allConfig->data);
103+
104+
return $transport($request);
105+
}
106+
107+
/**
108+
* @template CallTimeData
109+
* @template CallTimeTransportRequest
110+
* @template CallTimeTransportResponse
111+
*
112+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
113+
*
114+
* @return (CallTimeTransportResponse is never
115+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
116+
* : CallTimeTransportResponse
117+
* )
118+
*/
119+
public function get(string $uri, ?FetchConfig $config = null)
120+
{
121+
return ($this)($uri, $config);
122+
}
123+
124+
/**
125+
* @template CallTimeData
126+
* @template CallTimeTransportRequest
127+
* @template CallTimeTransportResponse
128+
*
129+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
130+
*
131+
* @return (CallTimeTransportResponse is never
132+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
133+
* : CallTimeTransportResponse
134+
* )
135+
*/
136+
public function options(string $uri, ?FetchConfig $config = null)
137+
{
138+
return ($this)($uri, FetchConfig::of(method: 'OPTIONS')->merge($config));
139+
}
140+
141+
/**
142+
* @template CallTimeData
143+
* @template CallTimeTransportRequest
144+
* @template CallTimeTransportResponse
145+
*
146+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
147+
*
148+
* @return (CallTimeTransportResponse is never
149+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
150+
* : CallTimeTransportResponse
151+
* )
152+
*/
153+
public function head(string $uri, ?FetchConfig $config = null)
154+
{
155+
return ($this)($uri, FetchConfig::of(method: 'HEAD')->merge($config));
156+
}
157+
158+
/**
159+
* @template CallTimeData
160+
* @template CallTimeTransportRequest
161+
* @template CallTimeTransportResponse
162+
*
163+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
164+
*
165+
* @return (CallTimeTransportResponse is never
166+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
167+
* : CallTimeTransportResponse
168+
* )
169+
*/
170+
public function delete(string $uri, ?FetchConfig $config = null)
171+
{
172+
return ($this)($uri, FetchConfig::of(method: 'DELETE')->merge($config));
173+
}
174+
175+
/**
176+
* @template CallTimeData
177+
* @template CallTimeTransportRequest
178+
* @template CallTimeTransportResponse
179+
*
180+
* @param CallTimeData $data
181+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
182+
*
183+
* @return (CallTimeTransportResponse is never
184+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
185+
* : CallTimeTransportResponse
186+
* )
187+
*/
188+
public function post(string $uri, mixed $data = null, ?FetchConfig $config = null)
189+
{
190+
return ($this)($uri, FetchConfig::of(
191+
method: 'POST',
192+
data: $data
193+
)->merge($config));
194+
}
195+
196+
/**
197+
* @template CallTimeData
198+
* @template CallTimeTransportRequest
199+
* @template CallTimeTransportResponse
200+
*
201+
* @param CallTimeData $data
202+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
203+
*
204+
* @return (CallTimeTransportResponse is never
205+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
206+
* : CallTimeTransportResponse
207+
* )
208+
*/
209+
public function put(string $uri, mixed $data = null, ?FetchConfig $config = null)
210+
{
211+
return ($this)($uri, FetchConfig::of(
212+
method: 'PUT',
213+
data: $data
214+
)->merge($config));
215+
}
216+
217+
/**
218+
* @template CallTimeData
219+
* @template CallTimeTransportRequest
220+
* @template CallTimeTransportResponse
221+
*
222+
* @param CallTimeData $data
223+
* @param FetchConfig<CallTimeData, CallTimeTransportRequest, CallTimeTransportResponse>|null $config
224+
*
225+
* @return (CallTimeTransportResponse is never
226+
* ? (InstanceTransportResponse is never ? ResponseInterface : InstanceTransportResponse)
227+
* : CallTimeTransportResponse
228+
* )
229+
*/
230+
public function patch(string $uri, mixed $data = null, ?FetchConfig $config = null)
231+
{
232+
return ($this)($uri, FetchConfig::of(
233+
method: 'PATCH',
234+
data: $data
235+
)->merge($config));
236+
}
237+
238+
private function configureClient(FetchConfig $config): ClientInterface
239+
{
240+
Assert::notNull($config->client, 'Expected an HTTP client to be configured during fetch.');
241+
242+
return new PluginClient(
243+
$config->client,
244+
[
245+
new ErrorPlugin(),
246+
...($config->headers ? [new HeaderSetPlugin($config->headers)] : []),
247+
...$config->plugins,
248+
]
249+
);
250+
}
251+
}

0 commit comments

Comments
 (0)