Passed
Push — main ( 35cea8...263c2f )
by Andrey
21:51 queued 20:06
created

Builder   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 525
Duplicated Lines 0 %

Test Coverage

Coverage 99.34%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 139
c 2
b 1
f 0
dl 0
loc 525
ccs 150
cts 151
cp 0.9934
rs 6.96
wmc 53

38 Methods

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

499
        $this->parsed[$name] = $this->castValue(/** @scrutinizer ignore-type */ $name, $value);
Loading history...
500
501 64
        return $this;
502
    }
503
504 185
    protected function get(int $index)
505
    {
506 185
        $name = $this->componentNameByIndex($index);
507
508 185
        return Arr::get($this->parsed, $name);
509
    }
510
511 65
    protected function getValidationType(int $component): array
512
    {
513 65
        return Arr::get($this->validate, $component, []);
514
    }
515
516 65
    protected function validate(int $component, $value): void
517
    {
518 65
        $type = $this->getValidationType($component);
519
520 65
        $this->validateType($value, $type);
521 64
    }
522
523 24
    protected function prepare(): array
524
    {
525
        return [
526 24
            HttpBuilderPrepare::make()->of($this->getScheme())->suffix(':'),
527
528 24
            '//',
529
530 24
            HttpBuilderPrepare::make()->of($this->getUser()),
531 24
            HttpBuilderPrepare::make()->of($this->getPassword())->prefix(':'),
532
533 24
            $this->getUser() || $this->getPassword() ? '@' : '',
534
535 24
            HttpBuilderPrepare::make()->of($this->getHost()),
536 24
            HttpBuilderPrepare::make()->of($this->getPort())->prefix(':'),
537 24
            HttpBuilderPrepare::make()->of($this->getPath()),
538 24
            HttpBuilderPrepare::make()->of($this->getQuery())->prefix('?'),
539 24
            HttpBuilderPrepare::make()->of($this->getFragment())->prefix('#'),
540
        ];
541
    }
542
}
543