Builder   C
last analyzed

Complexity

Total Complexity 55

Size/Duplication

Total Lines 538
Duplicated Lines 0 %

Test Coverage

Coverage 99.35%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 141
c 3
b 1
f 0
dl 0
loc 538
ccs 154
cts 155
cp 0.9935
rs 6
wmc 55

39 Methods

Rating   Name   Duplication   Size   Complexity  
A set() 0 9 1
A removeFragment() 0 3 1
A withQuery() 0 3 1
A withScheme() 0 3 1
A getQuery() 0 7 3
A getSubDomain() 0 5 2
A getDomain() 0 3 1
A getPassword() 0 3 1
A withHost() 0 3 1
A prepare() 0 17 3
A removeQuery() 0 7 1
A getPort() 0 3 1
A toArray() 0 11 1
A getBaseUrl() 0 6 1
A getUser() 0 3 1
A __toString() 0 3 1
A getHost() 0 3 1
A componentNameByIndex() 0 5 1
A fromPsr() 0 21 1
A getAuthority() 0 7 1
A resolveSame() 0 3 2
A withPort() 0 3 1
A getFragment() 0 3 1
A withPath() 0 3 1
A parsed() 0 13 1
A same() 0 3 1
A getUserInfo() 0 6 1
A validateComponentIndex() 0 6 3
A validate() 0 5 1
A get() 0 5 1
A getValidationType() 0 3 1
A getPath() 0 7 2
A withFragment() 0 3 1
A toUrl() 0 12 2
A getScheme() 0 3 1
A parse() 0 13 4
A withUserInfo() 0 5 1
A toPsr() 0 3 1
A putQuery() 0 12 4

How to fix   Complexity   

Complex Class

Complex classes like Builder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Builder, and based on these observations, apply Extract Interface, too.

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

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