Passed
Pull Request — main (#138)
by Andrey
12:48
created

Builder::withQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
namespace Helldar\Support\Helpers\Http;
4
5
use Helldar\Support\Concerns\Validation;
6
use Helldar\Support\Exceptions\UnknownUrlComponentIndexException;
7
use Helldar\Support\Facades\Helpers\Ables\Arrayable;
8
use Helldar\Support\Facades\Helpers\Arr;
9
use Helldar\Support\Facades\Helpers\Str;
10
use Helldar\Support\Facades\Http\Url as UrlHelper;
11
use Psr\Http\Message\UriInterface;
12
13
class Builder implements UriInterface
14
{
15
    use Validation;
16
17
    public const PHP_URL_ALL = -1;
18
19
    protected $parsed = [];
20
21
    protected $components = [
22
        PHP_URL_SCHEME   => 'scheme',
23
        PHP_URL_HOST     => 'host',
24
        PHP_URL_PORT     => 'port',
25
        PHP_URL_USER     => 'user',
26
        PHP_URL_PASS     => 'pass',
27
        PHP_URL_QUERY    => 'query',
28
        PHP_URL_PATH     => 'path',
29
        PHP_URL_FRAGMENT => 'fragment',
30
    ];
31
32
    protected $casts = [
33
        'query' => 'array',
34
        'port'  => 'integer',
35
    ];
36
37
    protected $validate = [
38
        PHP_URL_SCHEME   => ['string'],
39
        PHP_URL_HOST     => ['string'],
40
        PHP_URL_PORT     => ['integer'],
41
        PHP_URL_USER     => ['string'],
42
        PHP_URL_PASS     => ['string'],
43
        PHP_URL_QUERY    => ['string', 'array'],
44
        PHP_URL_PATH     => ['string'],
45
        PHP_URL_FRAGMENT => ['string'],
46
    ];
47
48
    /**
49
     * Return the string representation as a URI reference.
50
     *
51
     * @return string
52
     */
53
    public function __toString()
54
    {
55
        return $this->toUrl();
56
    }
57
58
    /**
59
     * Gets the current instance of the object.
60
     *
61
     * @return \Helldar\Support\Helpers\Http\Builder
62
     */
63
    public function same(): self
64
    {
65
        return $this;
66
    }
67
68
    /**
69
     * Parse a URL.
70
     *
71
     * @param  \Psr\Http\Message\UriInterface|string|null  $url
72
     * @param  int  $component
73
     *
74
     * @return \Helldar\Support\Helpers\Http\Builder
75
     */
76
    public function parse($url, int $component = self::PHP_URL_ALL): self
77
    {
78
        if ($component === self::PHP_URL_ALL) {
79
            UrlHelper::validate($url);
80
        }
81
82
        $key = $this->componentNameByIndex($component);
83
84
        $component === self::PHP_URL_ALL || empty($key)
85
            ? $this->parsed       = parse_url($url)
86
            : $this->parsed[$key] = parse_url($url, $component);
87
88
        $this->cast();
89
90
        return $this;
91
    }
92
93
    /**
94
     * Populate an object with parsed data.
95
     *
96
     * @param  array  $parsed
97
     *
98
     * @return \Helldar\Support\Helpers\Http\Builder
99
     */
100
    public function parsed(array $parsed): self
101
    {
102
        $components = array_values($this->components);
103
104
        $this->parsed = Arr::only($parsed, $components);
105
106
        $this->cast();
107
108
        return $this;
109
    }
110
111
    /**
112
     * Retrieve the domain name of the URI.
113
     *
114
     * @return string
115
     */
116
    public function getDomain(): string
117
    {
118
        return $this->getHost();
119
    }
120
121
    /**
122
     * Retrieve the subdomain name of the URI.
123
     *
124
     * @return string
125
     */
126
    public function getSubDomain(): string
127
    {
128
        $host = explode('.', $this->getHost());
129
130
        return count($host) > 2 ? reset($host) : '';
131
    }
132
133
    /**
134
     * Retrieve the scheme and host of the URI.
135
     *
136
     * @return string
137
     */
138
    public function getBaseUrl(): string
139
    {
140
        $schema = $this->getScheme();
141
        $host   = $this->getHost();
142
143
        return (string) Str::of("$schema://$host")->trim('://');
144
    }
145
146
    /**
147
     * Retrieve the scheme component of the URI.
148
     *
149
     * @return string
150
     */
151
    public function getScheme(): string
152
    {
153
        return $this->get(PHP_URL_SCHEME);
154
    }
155
156
    /**
157
     * Retrieve the authority component of the URI.
158
     *
159
     * @return string
160
     */
161
    public function getAuthority(): string
162
    {
163
        $auth = $this->getUserInfo();
164
        $host = $this->getHost();
165
        $port = $this->getPort();
166
167
        return (string) Str::of("$auth@$host:$port")->trim('@:');
168
    }
169
170
    /**
171
     * Retrieve the user information component of the URI.
172
     *
173
     * @return string
174
     */
175
    public function getUserInfo(): string
176
    {
177
        $user = $this->getUser();
178
        $pass = $this->getPassword();
179
180
        return (string) Str::of("$user:$pass")->trim(':');
181
    }
182
183
    /**
184
     * Retrieve the user name component of the URI.
185
     *
186
     * @return string
187
     */
188
    public function getUser(): string
189
    {
190
        return $this->get(PHP_URL_USER);
191
    }
192
193
    /**
194
     * Retrieve the user password component of the URI.
195
     *
196
     * @return string
197
     */
198
    public function getPassword(): string
199
    {
200
        return $this->get(PHP_URL_PASS);
201
    }
202
203
    /**
204
     * Retrieve the host component of the URI.
205
     *
206
     * @return string
207
     */
208
    public function getHost(): string
209
    {
210
        return $this->get(PHP_URL_HOST);
211
    }
212
213
    /**
214
     * Retrieve the port component of the URI.
215
     *
216
     * @return int|null
217
     */
218
    public function getPort(): ?int
219
    {
220
        return $this->get(PHP_URL_PORT);
221
    }
222
223
    /**
224
     * Retrieve the path component of the URI.
225
     *
226
     * @return string
227
     */
228
    public function getPath(): string
229
    {
230
        $value = $this->get(PHP_URL_PATH);
231
232
        return (string) Str::start($value, '/');
233
    }
234
235
    /**
236
     * Retrieve the query string of the URI.
237
     *
238
     * @return string
239
     */
240
    public function getQuery(): string
241
    {
242
        return $this->get(PHP_URL_QUERY);
243
    }
244
245
    /**
246
     * Retrieve the fragment component of the URI.
247
     *
248
     * @return string
249
     */
250
    public function getFragment(): string
251
    {
252
        return $this->get(PHP_URL_FRAGMENT);
253
    }
254
255
    /**
256
     * Return an instance with the specified scheme.
257
     *
258
     * @param  string  $scheme
259
     *
260
     * @return \Helldar\Support\Helpers\Http\Builder
261
     */
262
    public function withScheme($scheme): self
263
    {
264
        return $this->set(PHP_URL_SCHEME, $scheme);
265
    }
266
267
    /**
268
     * Return an instance with the specified user information.
269
     *
270
     * @param  string  $user
271
     * @param  string|null  $password
272
     *
273
     * @return \Helldar\Support\Helpers\Http\Builder
274
     */
275
    public function withUserInfo($user, $password = null): self
276
    {
277
        return $this
278
            ->set(PHP_URL_USER, $user)
279
            ->set(PHP_URL_PASS, $password);
280
    }
281
282
    /**
283
     * Return an instance with the specified host.
284
     *
285
     * @param  string  $host
286
     *
287
     * @return \Helldar\Support\Helpers\Http\Builder
288
     */
289
    public function withHost($host): self
290
    {
291
        return $this->set(PHP_URL_HOST, $host);
292
    }
293
294
    /**
295
     * Return an instance with the specified port.
296
     *
297
     * @param  int|null  $port
298
     *
299
     * @return \Helldar\Support\Helpers\Http\Builder
300
     */
301
    public function withPort($port): self
302
    {
303
        return $this->set(PHP_URL_PORT, $port);
304
    }
305
306
    /**
307
     * Return an instance with the specified path.
308
     *
309
     * @param  string  $path
310
     *
311
     * @return \Helldar\Support\Helpers\Http\Builder
312
     */
313
    public function withPath($path): self
314
    {
315
        return $this->set(PHP_URL_PATH, $path);
316
    }
317
318
    /**
319
     * Return an instance with the specified query string.
320
     *
321
     * @param  string  $query
322
     *
323
     * @return \Helldar\Support\Helpers\Http\Builder
324
     */
325
    public function withQuery($query): self
326
    {
327
        return $this->set(PHP_URL_QUERY, $query);
328
    }
329
330
    /**
331
     * Return an instance with the specified query object.
332
     *
333
     * @param  string  $key
334
     * @param  mixed  $value
335
     *
336
     * @return \Helldar\Support\Helpers\Http\Builder
337
     */
338
    public function putQuery(string $key, $value): self
339
    {
340
        $query = $this->get(PHP_URL_QUERY);
341
342
        $query[$key] = $value;
343
344
        return $this->set(PHP_URL_QUERY, $query);
345
    }
346
347
    /**
348
     * Return an instance with the specified query object.
349
     *
350
     * @param  string  $key
351
     *
352
     * @return \Helldar\Support\Helpers\Http\Builder
353
     */
354
    public function removeQuery(string $key): self
355
    {
356
        unset($this->parsed[$key]);
357
358
        return $this;
359
    }
360
361
    /**
362
     * Return an instance with the specified URI fragment.
363
     *
364
     * @param  string  $fragment
365
     *
366
     * @return \Helldar\Support\Helpers\Http\Builder
367
     */
368
    public function withFragment($fragment): self
369
    {
370
        return $this->set(PHP_URL_FRAGMENT, $fragment);
371
    }
372
373
    /**
374
     * Return an instance with the specified `UriInterface`.
375
     *
376
     * @param  \Psr\Http\Message\UriInterface  $uri
377
     *
378
     * @return $this
379
     */
380
    public function fromPsrUrl(UriInterface $uri): self
381
    {
382
        return $this->parse($uri);
383
    }
384
385
    /**
386
     * Return the string representation as a URI reference.
387
     *
388
     * @return \Psr\Http\Message\UriInterface
389
     */
390
    public function toPsrUrl(): UriInterface
391
    {
392
        $url = clone $this->same();
393
394
        $key = $this->componentNameByIndex(PHP_URL_QUERY);
395
396
        if (isset($url->parsed[$key])) {
397
            $url->parsed[$key] = http_build_query($url->parsed[$key]);
398
        }
399
400
        return $url;
401
    }
402
403
    /**
404
     * Return the string representation as a URI reference.
405
     *
406
     * @return string
407
     */
408
    public function toUrl(): string
409
    {
410
        return (string) $this->toPsrUrl();
411
    }
412
413
    protected function componentNameByIndex(int $component): ?string
414
    {
415
        $this->validateComponentIndex($component);
416
417
        return Arr::get($this->components, $component);
418
    }
419
420
    protected function validateComponentIndex(int $component): void
421
    {
422
        $components = array_keys($this->components);
423
424
        if ($component !== self::PHP_URL_ALL && ! in_array($component, $components, true)) {
425
            throw new UnknownUrlComponentIndexException($component);
426
        }
427
    }
428
429
    protected function cast(): void
430
    {
431
        Arrayable::of($this->parsed)
432
            ->map(function (&$value, string $key) {
433
                switch ($this->casts[$key] ?? null) {
434
                    case 'array':
435
                        $value = $this->castToArray($value);
436
                        break;
437
438
                    case 'integer':
439
                        $value = $this->castToInteger($value);
440
                        break;
441
442
                    default:
443
                        $value = $this->castToString($value);
444
                }
445
            });
446
    }
447
448
    protected function castToArray($value): array
449
    {
450
        if (empty($value)) {
451
            return [];
452
        }
453
454
        if (is_array($value)) {
455
            return $value;
456
        }
457
458
        $items = [];
459
460
        foreach (explode('&', $value) as $index => $item) {
461
            [$key, $value] = Str::contains($item, '=') ? explode('=', $item) : [$index, $item];
462
463
            $items[$key] = $value;
464
        }
465
466
        return $items;
467
    }
468
469
    protected function castToInteger($value): ?int
470
    {
471
        return empty($value) && ! is_numeric($value) ? null : $value;
472
    }
473
474
    protected function castToString(?string $value): ?string
475
    {
476
        return $value ?: null;
477
    }
478
479
    protected function set(int $component, $value): self
480
    {
481
        $this->validate($component, $value);
482
483
        $name = $this->componentNameByIndex($component);
484
485
        $this->parsed[$name] = $value;
486
487
        $this->cast();
488
489
        return $this;
490
    }
491
492
    protected function get(int $index)
493
    {
494
        $name = $this->componentNameByIndex($index);
495
496
        return Arr::get($this->parsed, $name);
497
    }
498
499
    protected function getValidationType(int $component): array
500
    {
501
        return $this->validate[$component] ?? [];
502
    }
503
504
    protected function validate(int $component, $value): void
505
    {
506
        $type = $this->getValidationType($component);
507
508
        $this->validateType($value, $type);
509
    }
510
}
511