Passed
Push — main ( 5538e6...72ff48 )
by Andrey
15:22 queued 13:48
created

Builder::getHost()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

516
        $this->parsed[$name] = $this->castValue(/** @scrutinizer ignore-type */ $name, $value);
Loading history...
517
518 64
        return $this;
519
    }
520
521 188
    protected function get(int $index)
522
    {
523 188
        $name = $this->componentNameByIndex($index);
524
525 188
        return Arr::get($this->parsed, $name);
526
    }
527
528 65
    protected function getValidationType(int $component): array
529
    {
530 65
        return Arr::get($this->validate, $component, []);
531
    }
532
533 65
    protected function validate(int $component, $value): void
534
    {
535 65
        $type = $this->getValidationType($component);
536
537 65
        $this->validateType($value, $type);
538 64
    }
539
540
    /**
541
     * Based on code by Maksim (Ellrion) Platonov.
542
     *
543
     * @see https://gist.github.com/Ellrion/f51ba0d40ae1d62eeae44fd1adf7b704
544
     *
545
     * @return array
546
     */
547 19
    protected function prepare(): array
548
    {
549
        return [
550 19
            HttpBuilderPrepare::make()->of($this->getScheme())->suffix(':'),
551
552 19
            '//',
553
554 19
            HttpBuilderPrepare::make()->of($this->getUser()),
555 19
            HttpBuilderPrepare::make()->of($this->getPassword())->prefix(':'),
556
557 19
            $this->getUser() || $this->getPassword() ? '@' : '',
558
559 19
            HttpBuilderPrepare::make()->of($this->getHost()),
560 19
            HttpBuilderPrepare::make()->of($this->getPort())->prefix(':'),
561 19
            HttpBuilderPrepare::make()->of($this->getPath()),
562 19
            HttpBuilderPrepare::make()->of($this->getQuery())->prefix('?'),
563 19
            HttpBuilderPrepare::make()->of($this->getFragment())->prefix('#'),
564
        ];
565
    }
566
567 140
    protected function resolveSame(int $component = self::PHP_URL_ALL): self
568
    {
569 140
        return $component === self::PHP_URL_ALL ? new self() : $this;
570
    }
571
}
572