1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Gemz\HttpClient\Contracts; |
4
|
|
|
|
5
|
|
|
use Gemz\HttpClient\Exceptions\InvalidArgument; |
6
|
|
|
use Symfony\Component\Mime\Part\Multipart\FormDataPart; |
7
|
|
|
|
8
|
|
|
trait Options |
9
|
|
|
{ |
10
|
|
|
/** @var string */ |
11
|
|
|
private static $CONTENT_TYPE_JSON = 'application/json'; |
12
|
|
|
|
13
|
|
|
/** @var string */ |
14
|
|
|
private static $CONTENT_TYPE_PLAIN = 'text/plain'; |
15
|
|
|
|
16
|
|
|
/** @var string */ |
17
|
|
|
private static $CONTENT_TYPE_MULTIPART = 'multipart/form-data'; |
18
|
|
|
|
19
|
|
|
/** @var string */ |
20
|
|
|
private static $CONTENT_TYPE_FORM_PARAMS = 'application/x-www-form-urlencoded'; |
21
|
|
|
|
22
|
|
|
/** @var string */ |
23
|
|
|
protected static $CUSTOM_DATA_HEADER = 'X-Custom-Data'; |
24
|
|
|
|
25
|
|
|
/** @var array<mixed> */ |
26
|
|
|
protected $options = []; |
27
|
|
|
|
28
|
|
|
/** @var bool */ |
29
|
|
|
protected $throwErrors = false; |
30
|
|
|
|
31
|
|
|
/** @var array<String> */ |
32
|
|
|
protected $bodyFormats = ['json', 'multipart', 'form_params']; |
33
|
|
|
|
34
|
|
|
/** @var string */ |
35
|
|
|
protected $bodyFormat = 'json'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Set authentication auth bearer token |
39
|
|
|
* |
40
|
|
|
* @param string $token |
41
|
|
|
* |
42
|
|
|
* @return $this |
43
|
|
|
*/ |
44
|
|
|
public function authBearer(string $token): self |
45
|
|
|
{ |
46
|
|
|
return $this->option('auth_bearer', $token); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Set authentication auth basic. If password is null |
51
|
|
|
* only username will be used |
52
|
|
|
* |
53
|
|
|
* @param string $username |
54
|
|
|
* @param string $password |
55
|
|
|
* |
56
|
|
|
* @return $this |
57
|
|
|
*/ |
58
|
|
|
public function authBasic(string $username, string $password = ''): self |
59
|
|
|
{ |
60
|
|
|
$this->options['auth_basic'] = $username; |
61
|
|
|
|
62
|
|
|
if ('' !== $password) { |
63
|
|
|
$this->options['auth_basic'] .= ':' . $password; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
return $this; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @return $this |
71
|
|
|
*/ |
72
|
|
|
public function throwErrors(): self |
73
|
|
|
{ |
74
|
|
|
$this->throwErrors = true; |
75
|
|
|
|
76
|
|
|
return $this; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @return bool |
81
|
|
|
*/ |
82
|
|
|
public function shouldThrowErrors(): bool |
83
|
|
|
{ |
84
|
|
|
return $this->throwErrors; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Values for existing header keys will be replaced |
89
|
|
|
* |
90
|
|
|
* @param string $key |
91
|
|
|
* @param string $value |
92
|
|
|
* |
93
|
|
|
* @return $this |
94
|
|
|
*/ |
95
|
|
|
public function header(string $key, string $value): self |
96
|
|
|
{ |
97
|
|
|
$this->options['headers'][$key] = $value; |
98
|
|
|
|
99
|
|
|
return $this; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Values for existing header keys will be replaced |
104
|
|
|
* |
105
|
|
|
* @param array<String> $headers |
106
|
|
|
* |
107
|
|
|
* @return $this |
108
|
|
|
*/ |
109
|
|
|
public function headers(array $headers): self |
110
|
|
|
{ |
111
|
|
|
foreach ($headers as $key => $value) { |
112
|
|
|
$this->header($key, $value); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
return $this; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Set the base uri. |
120
|
|
|
* |
121
|
|
|
* @param string $uri |
122
|
|
|
* |
123
|
|
|
* @return $this |
124
|
|
|
*/ |
125
|
|
|
public function baseUri(string $uri): self |
126
|
|
|
{ |
127
|
|
|
return $this->option('base_uri', $uri); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Disallows redirects. |
132
|
|
|
* |
133
|
|
|
* @return $this |
134
|
|
|
*/ |
135
|
|
|
public function disallowRedirects(): self |
136
|
|
|
{ |
137
|
|
|
return $this->option('max_redirects', -1); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @param int $max |
142
|
|
|
* |
143
|
|
|
* @return $this |
144
|
|
|
*/ |
145
|
|
|
public function allowRedirects(int $max = 0): self |
146
|
|
|
{ |
147
|
|
|
return $this->option('max_redirects', $max); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Float describing the timeout of the request in seconds. |
152
|
|
|
* use 0 to wait indefinitely |
153
|
|
|
* |
154
|
|
|
* @param float $seconds |
155
|
|
|
* |
156
|
|
|
* @return $this |
157
|
|
|
*/ |
158
|
|
|
public function timeout(float $seconds): self |
159
|
|
|
{ |
160
|
|
|
return $this->option('timeout', $seconds); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* Pass a string to specify an HTTP proxy, or an array to specify different proxies for different protocols. |
165
|
|
|
* |
166
|
|
|
* @param string|array<String> $proxy |
167
|
|
|
* |
168
|
|
|
* @return $this |
169
|
|
|
*/ |
170
|
|
|
public function useProxy($proxy): self |
171
|
|
|
{ |
172
|
|
|
return $this->option('proxy', $proxy); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @param float $seconds |
177
|
|
|
* |
178
|
|
|
* @return $this |
179
|
|
|
*/ |
180
|
|
|
public function maxDuration(float $seconds): self |
181
|
|
|
{ |
182
|
|
|
return $this->option('max_duration', $seconds); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Does not verify SSL certificates |
187
|
|
|
* |
188
|
|
|
* @return $this |
189
|
|
|
*/ |
190
|
|
|
public function doNotVerifySsl(): self |
191
|
|
|
{ |
192
|
|
|
$this->option('verify_peer', false); |
193
|
|
|
$this->option('verify_host', false); |
194
|
|
|
|
195
|
|
|
return $this; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Does verify SSL certificates |
200
|
|
|
* |
201
|
|
|
* @return $this |
202
|
|
|
*/ |
203
|
|
|
public function verifySsl(): self |
204
|
|
|
{ |
205
|
|
|
$this->option('verify_peer', true); |
206
|
|
|
$this->option('verify_host', true); |
207
|
|
|
|
208
|
|
|
return $this; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Set the content type |
213
|
|
|
* |
214
|
|
|
* @param string $type |
215
|
|
|
* |
216
|
|
|
* @return $this |
217
|
|
|
*/ |
218
|
|
|
public function contentType(string $type): self |
219
|
|
|
{ |
220
|
|
|
return $this->header('Content-Type', $type); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Set the clients user agent |
225
|
|
|
* |
226
|
|
|
* @param string $agent |
227
|
|
|
* |
228
|
|
|
* @return $this |
229
|
|
|
*/ |
230
|
|
|
public function userAgent(string $agent): self |
231
|
|
|
{ |
232
|
|
|
return $this->header('User-Agent', $agent); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Set accept header |
237
|
|
|
* |
238
|
|
|
* @param string $value |
239
|
|
|
* |
240
|
|
|
* @return $this |
241
|
|
|
*/ |
242
|
|
|
public function accept(string $value): self |
243
|
|
|
{ |
244
|
|
|
return $this->header('Accept', $value); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Set option according guzzle request options |
249
|
|
|
* |
250
|
|
|
* @param string $key |
251
|
|
|
* @param mixed $value |
252
|
|
|
* |
253
|
|
|
* @return $this |
254
|
|
|
*/ |
255
|
|
|
public function option(string $key, $value): self |
256
|
|
|
{ |
257
|
|
|
$this->options[$key] = $value; |
258
|
|
|
|
259
|
|
|
return $this; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Set param for the query url |
264
|
|
|
* |
265
|
|
|
* @param string $key |
266
|
|
|
* @param string $value |
267
|
|
|
* |
268
|
|
|
* @return $this |
269
|
|
|
*/ |
270
|
|
|
public function queryParam(string $key, string $value): self |
271
|
|
|
{ |
272
|
|
|
$this->options['query'][$key] = $value; |
273
|
|
|
|
274
|
|
|
return $this; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Set multiple params for query url |
279
|
|
|
* in form of [<key> => <value>, <key2> => <value2>] |
280
|
|
|
* |
281
|
|
|
* @param array<String> $params |
282
|
|
|
* |
283
|
|
|
* @return $this |
284
|
|
|
*/ |
285
|
|
|
public function queryParams(array $params): self |
286
|
|
|
{ |
287
|
|
|
foreach ($params as $key => $value) { |
288
|
|
|
$this->queryParam($key, $value); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
return $this; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Any extra data to attach to the response header |
296
|
|
|
* Available in response->customData(). Useful when using asynchronous requests |
297
|
|
|
* to identify the request |
298
|
|
|
* |
299
|
|
|
* @param mixed $data |
300
|
|
|
* |
301
|
|
|
* @return $this |
302
|
|
|
*/ |
303
|
|
|
public function customData($data): self |
304
|
|
|
{ |
305
|
|
|
return $this->option('user_data', $data); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* @param string $format |
310
|
|
|
* |
311
|
|
|
* @return $this |
312
|
|
|
*/ |
313
|
|
|
protected function bodyFormat(string $format): self |
314
|
|
|
{ |
315
|
|
|
$this->bodyFormat = $format; |
316
|
|
|
|
317
|
|
|
return $this; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* @return $this |
322
|
|
|
*/ |
323
|
|
|
public function asPlainText() |
324
|
|
|
{ |
325
|
|
|
$this->bodyFormat('body'); |
326
|
|
|
|
327
|
|
|
return $this->contentType(self::$CONTENT_TYPE_PLAIN); |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* @return $this |
332
|
|
|
*/ |
333
|
|
|
public function asJson(): self |
334
|
|
|
{ |
335
|
|
|
$this->bodyFormat('json'); |
336
|
|
|
|
337
|
|
|
return $this->contentType(self::$CONTENT_TYPE_JSON); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* @return $this |
342
|
|
|
*/ |
343
|
|
|
public function asFormParams(): self |
344
|
|
|
{ |
345
|
|
|
$this->bodyFormat('form_params'); |
346
|
|
|
|
347
|
|
|
return $this->contentType(self::$CONTENT_TYPE_FORM_PARAMS); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Set the content type multipart form-data |
352
|
|
|
* |
353
|
|
|
* @return $this |
354
|
|
|
*/ |
355
|
|
|
public function asMultipart(): self |
356
|
|
|
{ |
357
|
|
|
$this->bodyFormat('multipart'); |
358
|
|
|
|
359
|
|
|
return $this->contentType(self::$CONTENT_TYPE_MULTIPART); |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* @return array<mixed> |
364
|
|
|
*/ |
365
|
|
|
public function getHeaders(): array |
366
|
|
|
{ |
367
|
|
|
return $this->options['headers'] ?? []; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* @return null|string |
372
|
|
|
*/ |
373
|
|
|
public function getContentType() |
374
|
|
|
{ |
375
|
|
|
return array_key_exists('Content-Type', $this->getHeaders()) |
376
|
|
|
? $this->getHeaders()['Content-Type'] |
377
|
|
|
: null; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* @return string |
382
|
|
|
*/ |
383
|
|
|
public function getBodyFormat(): string |
384
|
|
|
{ |
385
|
|
|
return $this->bodyFormat; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* Indicates if the payload must be an array |
390
|
|
|
* depending on body format |
391
|
|
|
* |
392
|
|
|
* @return bool |
393
|
|
|
*/ |
394
|
|
|
protected function payloadMustBeArray(): bool |
395
|
|
|
{ |
396
|
|
|
return in_array( |
397
|
|
|
$this->bodyFormat, |
398
|
|
|
$this->bodyFormats |
399
|
|
|
); |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* @param mixed $payload |
404
|
|
|
* |
405
|
|
|
* @return $this |
406
|
|
|
*/ |
407
|
|
|
protected function throwExceptionWhenPayloadIsNotArray($payload): self |
408
|
|
|
{ |
409
|
|
|
if (! is_array($payload)) { |
410
|
|
|
throw InvalidArgument::payloadMustBeArray(); |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
return $this; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Set any payload text, array |
418
|
|
|
* |
419
|
|
|
* @param mixed $payload |
420
|
|
|
* |
421
|
|
|
* @return $this |
422
|
|
|
*/ |
423
|
|
|
public function payload($payload): self |
424
|
|
|
{ |
425
|
|
|
return $this->option('payload', $payload); |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* @param string $option |
430
|
|
|
* |
431
|
|
|
* @return mixed |
432
|
|
|
*/ |
433
|
|
|
protected function getOption(string $option) |
434
|
|
|
{ |
435
|
|
|
return $this->options[$option] ?? ''; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* @return $this |
440
|
|
|
*/ |
441
|
|
|
protected function resolvePayload(): self |
442
|
|
|
{ |
443
|
|
|
$payload = $this->getOption('payload'); |
444
|
|
|
|
445
|
|
|
if (! is_array($payload) && $this->payloadMustBeArray()) { |
446
|
|
|
throw InvalidArgument::payloadMustBeArray(); |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
if (empty($payload) || $payload == null) { |
450
|
|
|
$this->removeOptions(['body', 'payload']); |
451
|
|
|
|
452
|
|
|
return $this; |
453
|
|
|
} |
454
|
|
|
|
455
|
|
View Code Duplication |
if ($this->bodyFormat == 'json') { |
|
|
|
|
456
|
|
|
$this->option('json', $payload); |
457
|
|
|
$this->removeOptions(['body', 'payload']); |
458
|
|
|
|
459
|
|
|
return $this; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
if ($this->bodyFormat == 'multipart') { |
463
|
|
|
$formData = new FormDataPart($payload); |
464
|
|
|
|
465
|
|
|
$this->headers($formData->getPreparedHeaders()->toArray()); |
466
|
|
|
$this->option('body', $formData->bodyToIterable()); |
467
|
|
|
$this->removeOptions(['json', 'payload']); |
468
|
|
|
|
469
|
|
|
return $this; |
470
|
|
|
} |
471
|
|
|
|
472
|
|
View Code Duplication |
if ($this->bodyFormat == 'form_params') { |
|
|
|
|
473
|
|
|
$this->option('body', $payload); |
474
|
|
|
$this->removeOptions(['json', 'payload']); |
475
|
|
|
|
476
|
|
|
return $this; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
if (is_string($payload) |
480
|
|
|
|| is_resource($payload) |
481
|
|
|
|| is_callable($payload)) { |
482
|
|
|
|
483
|
|
|
$this->option('body', $payload); |
484
|
|
|
$this->removeOptions(['json', 'payload']); |
485
|
|
|
|
486
|
|
|
return $this; |
487
|
|
|
} else { |
488
|
|
|
throw InvalidArgument::payloadAndBodyFormatNotCompatible($this->bodyFormat); |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* remove an option |
494
|
|
|
* |
495
|
|
|
* @param string $option |
496
|
|
|
* |
497
|
|
|
* @return $this |
498
|
|
|
*/ |
499
|
|
|
protected function removeOption(string $option): self |
500
|
|
|
{ |
501
|
|
|
unset($this->options[$option]); |
502
|
|
|
|
503
|
|
|
return $this; |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* @param array<String> $options |
508
|
|
|
* |
509
|
|
|
* @return $this |
510
|
|
|
*/ |
511
|
|
|
protected function removeOptions(array $options): self |
512
|
|
|
{ |
513
|
|
|
foreach ($options as $option) { |
514
|
|
|
if (! is_string($option)) { |
515
|
|
|
continue; |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
$this->removeOption($option); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
return $this; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* @param mixed $body |
526
|
|
|
* |
527
|
|
|
* @return $this |
528
|
|
|
*/ |
529
|
|
|
protected function body($body): self |
530
|
|
|
{ |
531
|
|
|
return $this->option('body', $body); |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
} |
535
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.