Skip to content

Commit 4651b17

Browse files
committed
Introduce fetch API
1 parent 850595c commit 4651b17

39 files changed

Lines changed: 79691 additions & 75647 deletions

composer.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,15 @@
4646
"php-http/vcr-plugin": "For testing HTTP clients through storing and replaying requests and responses."
4747
},
4848
"config": {
49-
"sort-packages": true
49+
"sort-packages": true,
50+
"allow-plugins": {
51+
"phpro/grumphp-shim": true
52+
}
5053
},
5154
"autoload": {
55+
"files": [
56+
"src/functions.php"
57+
],
5258
"psr-4": {
5359
"Phpro\\HttpTools\\": "src"
5460
}

examples/sdk.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
/**
4141
* @template ResultType
42+
*
4243
* @extends HttpResource<ResultType>
4344
*/
4445
final class UsersResource extends HttpResource
@@ -63,6 +64,7 @@ final class Sdk
6364
{
6465
/**
6566
* @var UsersResource<ResultType>
67+
*
6668
* @psalm-readonly
6769
*/
6870
public UsersResource $users;

phive.xml

Lines changed: 3 additions & 3 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.13.1" installed="4.13.1" location="./tools/psalm.phar" copy="true"/>
4-
<phar name="phpunit" version="^9.5" installed="9.5.10" location="./tools/phpunit.phar" copy="true"/>
5-
<phar name="php-cs-fixer" version="^3.3" installed="3.3.2" location="./tools/php-cs-fixer.phar" copy="true"/>
3+
<phar name="psalm" version="4.27.0" installed="4.27.0" location="./tools/psalm.phar" copy="true"/>
4+
<phar name="phpunit" version="^9.5" installed="9.5.24" location="./tools/phpunit.phar" copy="true"/>
5+
<phar name="php-cs-fixer" version="^3.3" installed="3.11.0" location="./tools/php-cs-fixer.phar" copy="true"/>
66
</phive>

src/Client/Configurator/PluginsConfigurator.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
use Http\Client\Common\Plugin;
88
use Http\Client\Common\PluginClient;
9+
910
use function is_array;
1011
use function iterator_to_array;
12+
1113
use Psr\Http\Client\ClientInterface;
1214
use Webmozart\Assert\Assert;
1315

src/Client/FetchClient.php

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

0 commit comments

Comments
 (0)