Passed
Pull Request — main (#22)
by Andrey
20:42 queued 05:40
created

HttpBuilder::prepare()   C

Complexity

Conditions 9
Paths 256

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 10
nc 256
nop 0
dl 0
loc 12
rs 6.5222
c 1
b 0
f 0
1
<?php
2
3
namespace Helldar\Support\Helpers;
4
5
use ArgumentCountError;
6
use Helldar\Support\Concerns\Makeable;
7
use Helldar\Support\Facades\Helpers\Arr;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Helldar\Support\Helpers\Arr. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Helldar\Support\Facades\Helpers\Str;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Helldar\Support\Helpers\Str. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
use RuntimeException;
10
11
/**
12
 * Based on code by Maksim (Ellrion) Platonov.
13
 *
14
 * @see https://gist.github.com/Ellrion/f51ba0d40ae1d62eeae44fd1adf7b704
15
 *
16
 * @method string|null getFragment()
17
 * @method string|null getHost()
18
 * @method string|null getPass()
19
 * @method string|null getPath()
20
 * @method string|null getPort()
21
 * @method string|null getQuery()
22
 * @method string|null getScheme()
23
 * @method string|null getUser()
24
 * @method HttpBuilder setFragment(string $value)
25
 * @method HttpBuilder setHost(string $value)
26
 * @method HttpBuilder setPass(string $value)
27
 * @method HttpBuilder setPath(string $value)
28
 * @method HttpBuilder setPort(string $value)
29
 * @method HttpBuilder setQuery(array|string $value)
30
 * @method HttpBuilder setScheme(string $value)
31
 * @method HttpBuilder setUser(string $value)
32
 */
33
final class HttpBuilder
34
{
35
    use Makeable;
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
    public function __call($method, $args)
51
    {
52
        if ($this->isGetter($method) || $this->isSetter($method)) {
53
            $key = $this->parseKey($method);
54
55
            if (! $this->allowKey($key)) {
56
                throw new RuntimeException($method . ' method not defined.');
57
            }
58
59
            switch (true) {
60
                case $this->isGetter($method):
61
                    $this->validateArgumentsCount($method, $args, 0);
62
63
                    return $this->get($key);
64
65
                case $this->isSetter($method):
66
                    $this->validateArgumentsCount($method, $args);
67
68
                    return $this->set($key, ...$args);
0 ignored issues
show
Bug introduced by
$args is expanded, but the parameter $value of Helldar\Support\Helpers\HttpBuilder::set() does not expect variable arguments. ( Ignorable by Annotation )

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

68
                    return $this->set($key, /** @scrutinizer ignore-type */ ...$args);
Loading history...
69
            }
70
        }
71
    }
72
73
    public function same(): self
74
    {
75
        return $this;
76
    }
77
78
    public function parse(string $url, int $component = -1): self
79
    {
80
        $component = $this->componentIndex($component);
81
        $key       = $this->componentKey($component);
82
83
        $component === -1 || empty($key)
84
            ? $this->parsed       = parse_url($url)
85
            : $this->parsed[$key] = parse_url($url, $component);
86
87
        return $this;
88
    }
89
90
    public function compile(): string
91
    {
92
        return implode('', array_filter($this->prepare()));
93
    }
94
95
    protected function prepare(): array
96
    {
97
        return [
98
            $this->getScheme() ? $this->getScheme() . '://' : '',
99
            $this->getUser(),
100
            $this->getPass() ? ':' . $this->getPass() : '',
101
            $this->getUser() || $this->getPass() ? '@' : '',
102
            $this->getHost(),
103
            $this->getPort() ? ':' . $this->getPort() : '',
104
            $this->getPath() ? '/' . ltrim($this->getPath(), '/') : '',
105
            $this->getQuery() ? '?' . $this->getQuery() : '',
106
            $this->getFragment() ? '#' . $this->getFragment() : '',
107
        ];
108
    }
109
110
    protected function value(string $key): ?string
111
    {
112
        return Arr::get($this->parsed, $key);
113
    }
114
115
    protected function componentIndex(int $component = -1): int
116
    {
117
        return Arr::getKey($this->components, $component, -1);
118
    }
119
120
    protected function componentKey(int $component = -1): ?string
121
    {
122
        return Arr::get($this->components, $component);
123
    }
124
125
    protected function allowKey(?string $key): bool
126
    {
127
        return in_array($key, $this->components);
128
    }
129
130
    protected function isGetter(string $method): bool
131
    {
132
        return Str::startsWith($method, 'get');
133
    }
134
135
    protected function isSetter(string $method): bool
136
    {
137
        return Str::startsWith($method, 'set');
138
    }
139
140
    protected function parseKey(string $method): ?string
141
    {
142
        $search = Str::startsWith($method, 'get') ? 'get' : 'set';
143
144
        return Str::lower(Str::after($method, $search));
145
    }
146
147
    protected function set(string $key, $value): self
148
    {
149
        $this->parsed[$key] = is_array($value) ? http_build_query($value) : $value;
150
151
        return $this;
152
    }
153
154
    protected function get(string $key): ?string
155
    {
156
        return $this->parsed[$key] ?? null;
157
    }
158
159
    protected function validateArgumentsCount(string $method, $args, int $need = 1): void
160
    {
161
        if (count($args) > 1) {
162
            throw new ArgumentCountError($method . ' expects at most ' . $need . ' parameter, ' . count($args) . ' given.');
163
        }
164
    }
165
}
166