1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Cerbero\LazyJsonPages; |
||||
6 | |||||
7 | use Cerbero\LazyJsonPages\Data\Config; |
||||
8 | use Cerbero\LazyJsonPages\Data\Dot; |
||||
9 | use Cerbero\LazyJsonPages\Paginations\AnyPagination; |
||||
10 | use Cerbero\LazyJsonPages\Services\ClientFactory; |
||||
11 | use Cerbero\LazyJsonPages\Sources\AnySource; |
||||
12 | use Closure; |
||||
13 | use GuzzleHttp\RequestOptions; |
||||
14 | use Illuminate\Support\LazyCollection; |
||||
15 | use Psr\Http\Message\RequestInterface as Request; |
||||
16 | use Psr\Http\Message\ResponseInterface as Response; |
||||
17 | use Throwable; |
||||
18 | |||||
19 | /** |
||||
20 | * The Lazy JSON Pages entry-point |
||||
21 | */ |
||||
22 | final class LazyJsonPages |
||||
23 | { |
||||
24 | /** |
||||
25 | * The HTTP client factory. |
||||
26 | */ |
||||
27 | private readonly ClientFactory $client; |
||||
28 | |||||
29 | /** |
||||
30 | * The raw configuration of the API pagination. |
||||
31 | * |
||||
32 | * @var array<Config::OPTION_*, mixed> |
||||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||||
33 | */ |
||||
34 | private array $config = []; |
||||
35 | |||||
36 | /** |
||||
37 | * Add a global middleware. |
||||
38 | */ |
||||
39 | 2 | public static function globalMiddleware(string $name, callable $middleware): void |
|||
40 | { |
||||
41 | 2 | ClientFactory::globalMiddleware($name, $middleware); |
|||
42 | } |
||||
43 | |||||
44 | /** |
||||
45 | * Instantiate the class statically. |
||||
46 | */ |
||||
47 | 50 | public static function from(mixed $source): self |
|||
48 | { |
||||
49 | 50 | return new self($source); |
|||
50 | } |
||||
51 | |||||
52 | /** |
||||
53 | * Instantiate the class. |
||||
54 | */ |
||||
55 | 50 | public function __construct(private readonly mixed $source) |
|||
56 | { |
||||
57 | 50 | $this->client = new ClientFactory(); |
|||
0 ignored issues
–
show
|
|||||
58 | } |
||||
59 | |||||
60 | /** |
||||
61 | * Set the name of the page. |
||||
62 | */ |
||||
63 | 1 | public function pageName(string $name): self |
|||
64 | { |
||||
65 | 1 | $this->config[Config::OPTION_PAGE_NAME] = $name; |
|||
66 | |||||
67 | 1 | return $this; |
|||
68 | } |
||||
69 | |||||
70 | /** |
||||
71 | * Set the pattern to capture the page in the URI path. |
||||
72 | */ |
||||
73 | 4 | public function pageInPath(string $pattern = '/(\d+)(?!.*\d)/'): self |
|||
74 | { |
||||
75 | 4 | $this->config[Config::OPTION_PAGE_IN_PATH] = $pattern; |
|||
76 | |||||
77 | 4 | return $this; |
|||
78 | } |
||||
79 | |||||
80 | /** |
||||
81 | * Set the number of the first page. |
||||
82 | */ |
||||
83 | 10 | public function firstPage(int $page): self |
|||
84 | { |
||||
85 | 10 | $this->config[Config::OPTION_FIRST_PAGE] = max(0, $page); |
|||
86 | |||||
87 | 10 | return $this; |
|||
88 | } |
||||
89 | |||||
90 | /** |
||||
91 | * Set the total number of pages. |
||||
92 | */ |
||||
93 | 33 | public function totalPages(string $key): self |
|||
94 | { |
||||
95 | 33 | $this->config[Config::OPTION_TOTAL_PAGES_KEY] = $key; |
|||
96 | |||||
97 | 33 | return $this; |
|||
98 | } |
||||
99 | |||||
100 | /** |
||||
101 | * Set the total number of items. |
||||
102 | */ |
||||
103 | 4 | public function totalItems(string $key): self |
|||
104 | { |
||||
105 | 4 | $this->config[Config::OPTION_TOTAL_ITEMS_KEY] = $key; |
|||
106 | |||||
107 | 4 | return $this; |
|||
108 | } |
||||
109 | |||||
110 | /** |
||||
111 | * Set the number of the last page. |
||||
112 | */ |
||||
113 | 5 | public function lastPage(string $key): self |
|||
114 | { |
||||
115 | 5 | $this->config[Config::OPTION_LAST_PAGE_KEY] = $key; |
|||
116 | |||||
117 | 5 | return $this; |
|||
118 | } |
||||
119 | |||||
120 | /** |
||||
121 | * Set the cursor or next page. |
||||
122 | */ |
||||
123 | 2 | public function cursor(string $key): self |
|||
124 | { |
||||
125 | 2 | $this->config[Config::OPTION_CURSOR_KEY] = $key; |
|||
126 | |||||
127 | 2 | return $this; |
|||
128 | } |
||||
129 | |||||
130 | /** |
||||
131 | * Set the offset. |
||||
132 | */ |
||||
133 | 4 | public function offset(string $key = 'offset'): self |
|||
134 | { |
||||
135 | 4 | $this->config[Config::OPTION_OFFSET_KEY] = $key; |
|||
136 | |||||
137 | 4 | return $this; |
|||
138 | } |
||||
139 | |||||
140 | /** |
||||
141 | * Set the Link header pagination. |
||||
142 | */ |
||||
143 | 4 | public function linkHeader(): self |
|||
144 | { |
||||
145 | 4 | $this->config[Config::OPTION_HAS_LINK_HEADER] = true; |
|||
146 | |||||
147 | 4 | return $this; |
|||
148 | } |
||||
149 | |||||
150 | /** |
||||
151 | * Set the custom pagination. |
||||
152 | */ |
||||
153 | 5 | public function pagination(string $class): self |
|||
154 | { |
||||
155 | 5 | $this->config[Config::OPTION_PAGINATION] = $class; |
|||
156 | |||||
157 | 5 | return $this; |
|||
158 | } |
||||
159 | |||||
160 | /** |
||||
161 | * Throttle the requests to respect rate limiting. |
||||
162 | */ |
||||
163 | 1 | public function throttle(int $requests, int $perSeconds = 0, int $perMinutes = 0, int $perHours = 0): self |
|||
164 | { |
||||
165 | 1 | $seconds = $perSeconds + $perMinutes * 60 + $perHours * 3600; |
|||
166 | |||||
167 | 1 | if ($requests > 0 && $seconds > 0) { |
|||
168 | 1 | $this->config[Config::OPTION_RATE_LIMITS] ??= $this->client->rateLimits; |
|||
169 | |||||
170 | 1 | $this->client->throttle($requests, $seconds); |
|||
171 | } |
||||
172 | |||||
173 | 1 | return $this; |
|||
174 | } |
||||
175 | |||||
176 | /** |
||||
177 | * Set the maximum number of concurrent async HTTP requests. |
||||
178 | */ |
||||
179 | 2 | public function async(int $requests): self |
|||
180 | { |
||||
181 | 2 | $this->config[Config::OPTION_ASYNC] = max(1, $requests); |
|||
182 | |||||
183 | 2 | $this->client->config(RequestOptions::STREAM, $this->config[Config::OPTION_ASYNC] === 1); |
|||
184 | |||||
185 | 2 | return $this; |
|||
186 | } |
||||
187 | |||||
188 | /** |
||||
189 | * Set the server connection timeout in seconds. |
||||
190 | */ |
||||
191 | 1 | public function connectionTimeout(float|int $seconds): self |
|||
192 | { |
||||
193 | 1 | $this->client->config(RequestOptions::CONNECT_TIMEOUT, max(0, $seconds)); |
|||
194 | |||||
195 | 1 | $this->client->config(RequestOptions::READ_TIMEOUT, max(0, $seconds)); |
|||
196 | |||||
197 | 1 | return $this; |
|||
198 | } |
||||
199 | |||||
200 | /** |
||||
201 | * Set an HTTP request timeout in seconds. |
||||
202 | */ |
||||
203 | 1 | public function requestTimeout(float|int $seconds): self |
|||
204 | { |
||||
205 | 1 | $this->client->config(RequestOptions::TIMEOUT, max(0, $seconds)); |
|||
206 | |||||
207 | 1 | return $this; |
|||
208 | } |
||||
209 | |||||
210 | /** |
||||
211 | * Set the number of attempts to fetch pages. |
||||
212 | */ |
||||
213 | 1 | public function attempts(int $times): self |
|||
214 | { |
||||
215 | 1 | $this->config[Config::OPTION_ATTEMPTS] = max(1, $times); |
|||
216 | |||||
217 | 1 | return $this; |
|||
218 | } |
||||
219 | |||||
220 | /** |
||||
221 | * Set the backoff strategy. |
||||
222 | */ |
||||
223 | 1 | public function backoff(Closure $callback): self |
|||
224 | { |
||||
225 | 1 | $this->config[Config::OPTION_BACKOFF] = $callback; |
|||
226 | |||||
227 | 1 | return $this; |
|||
228 | } |
||||
229 | |||||
230 | /** |
||||
231 | * Add an HTTP client middleware. |
||||
232 | */ |
||||
233 | 2 | public function middleware(string $name, callable $middleware): self |
|||
234 | { |
||||
235 | 2 | $this->client->middleware($name, $middleware); |
|||
236 | |||||
237 | 2 | return $this; |
|||
238 | } |
||||
239 | |||||
240 | /** |
||||
241 | * Handle the sending request. |
||||
242 | * |
||||
243 | * @param Closure(Request $request, array<string, mixed> $config): void $callback |
||||
244 | */ |
||||
245 | 2 | public function onRequest(Closure $callback): self |
|||
246 | { |
||||
247 | 2 | $this->client->onRequest($callback); |
|||
248 | |||||
249 | 2 | return $this; |
|||
250 | } |
||||
251 | |||||
252 | /** |
||||
253 | * Handle the received response. |
||||
254 | * |
||||
255 | * @param Closure(Response $response, Request $request, array<string, mixed> $config): void $callback |
||||
256 | */ |
||||
257 | 2 | public function onResponse(Closure $callback): self |
|||
258 | { |
||||
259 | 2 | $this->client->onResponse($callback); |
|||
260 | |||||
261 | 2 | return $this; |
|||
262 | } |
||||
263 | |||||
264 | /** |
||||
265 | * Handle a transaction error. |
||||
266 | * |
||||
267 | * @param Closure(Throwable $e, Request $request, ?Response $response, array<string, mixed> $config): void $callback |
||||
268 | */ |
||||
269 | 1 | public function onError(Closure $callback): self |
|||
270 | { |
||||
271 | 1 | $this->client->onError($callback); |
|||
272 | |||||
273 | 1 | return $this; |
|||
274 | } |
||||
275 | |||||
276 | /** |
||||
277 | * Retrieve a lazy collection yielding the paginated items. |
||||
278 | * |
||||
279 | * @return LazyCollection<int, mixed> |
||||
280 | * @throws \Cerbero\LazyJsonPages\Exceptions\UnsupportedPaginationException |
||||
281 | */ |
||||
282 | 50 | public function collect(string $dot = '*'): LazyCollection |
|||
283 | { |
||||
284 | 50 | $this->config[Config::OPTION_ITEMS_POINTER] = (new Dot($dot))->toPointer(); |
|||
285 | |||||
286 | 50 | return new LazyCollection(function () { |
|||
0 ignored issues
–
show
function(...) { /* ... */ } of type callable is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $source of Illuminate\Support\LazyCollection::__construct() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
287 | 50 | $client = $this->client->make(); |
|||
288 | 50 | $config = new Config(...$this->config); /** @phpstan-ignore-line */ |
|||
0 ignored issues
–
show
$this->config is expanded, but the parameter $itemsPointer of Cerbero\LazyJsonPages\Data\Config::__construct() does not expect variable arguments.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
289 | 50 | $source = (new AnySource($this->source))->setClient($client); |
|||
290 | |||||
291 | 50 | yield from new AnyPagination($source, $client, $config); |
|||
292 | 50 | }); |
|||
293 | } |
||||
294 | } |
||||
295 |