Passed
Push — main ( 16ab53...91f55c )
by Andrey
01:26
created

HttpBuilder::parseKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Helldar\Support\Helpers;
4
5
use ArgumentCountError;
6
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...
7
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...
8
use RuntimeException;
9
10
/**
11
 * Based on code by Maksim (Ellrion) Platonov.
12
 *
13
 * @see https://gist.github.com/Ellrion/f51ba0d40ae1d62eeae44fd1adf7b704
14
 *
15
 * @method static HttpBuilder setFragment(array|string $value)
16
 * @method static HttpBuilder setHost(string $value)
17
 * @method static HttpBuilder setPass(string $value)
18
 * @method static HttpBuilder setPath(string $value)
19
 * @method static HttpBuilder setPort(string $value)
20
 * @method static HttpBuilder setQuery(array|string $value)
21
 * @method static HttpBuilder setScheme(string $value)
22
 * @method static HttpBuilder setUser(string $value)
23
 * @method static string|null getFragment()
24
 * @method static string|null getHost()
25
 * @method static string|null getPass()
26
 * @method static string|null getPath()
27
 * @method static string|null getPort()
28
 * @method static string|null getQuery()
29
 * @method static string|null getScheme()
30
 * @method static string|null getUser()
31
 */
32
final class HttpBuilder
33
{
34
    protected $parsed = [];
35
36
    protected $components = [
37
        PHP_URL_SCHEME   => 'scheme',
38
        PHP_URL_HOST     => 'host',
39
        PHP_URL_PORT     => 'port',
40
        PHP_URL_USER     => 'user',
41
        PHP_URL_PASS     => 'pass',
42
        PHP_URL_QUERY    => 'query',
43
        PHP_URL_PATH     => 'path',
44
        PHP_URL_FRAGMENT => 'fragment',
45
    ];
46
47
    /**
48
     * Calling magic methods.
49
     *
50
     * @param  string  $method
51
     * @param  mixed  $args
52
     *
53
     * @return $this|string|null
54
     */
55 46
    public function __call($method, $args)
56
    {
57 46
        if ($this->isGetter($method) || $this->isSetter($method)) {
58 46
            $key = $this->parseKey($method);
59
60 46
            if (! $this->allowKey($key)) {
61 4
                throw new RuntimeException($method . ' method not defined.');
62
            }
63
64
            switch (true) {
65 42
                case $this->isGetter($method):
66 38
                    $this->validateArgumentsCount($method, $args, 0);
67
68 38
                    return $this->get($key);
69
70 24
                case $this->isSetter($method):
71 24
                    $this->validateArgumentsCount($method, $args);
72
73 20
                    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

73
                    return $this->set($key, /** @scrutinizer ignore-type */ ...$args);
Loading history...
74
            }
75
        }
76
77
        throw new RuntimeException("Using an unknown method: \"{$method}\"");
78
    }
79
80
    /**
81
     * Gets the current instance of the object.
82
     *
83
     * @return $this
84
     */
85 8
    public function same(): self
86
    {
87 8
        return $this;
88
    }
89
90
    /**
91
     * Parse a URL.
92
     *
93
     * @param  string  $url
94
     * @param  int  $component
95
     *
96
     * @return $this
97
     */
98 20
    public function parse(string $url, int $component = -1): self
99
    {
100 20
        $component = $this->componentIndex($component);
101 20
        $key       = $this->componentKey($component);
102
103 20
        $component === -1 || empty($key)
104 16
            ? $this->parsed = parse_url($url)
105 4
            : $this->parsed[$key] = parse_url($url, $component);
106
107 20
        return $this;
108
    }
109
110
    /**
111
     * Compiles parameters to URL.
112
     *
113
     * @return string
114
     */
115 10
    public function compile(): string
116
    {
117 10
        return implode('', array_filter($this->prepare()));
118
    }
119
120
    /**
121
     * Prepares data for compilation.
122
     *
123
     * @return array
124
     */
125 10
    protected function prepare(): array
126
    {
127
        return [
128 10
            $this->getScheme() ? $this->getScheme() . '://' : '',
129 10
            $this->getUser(),
130 10
            $this->getPass() ? ':' . $this->getPass() : '',
131 10
            $this->getUser() || $this->getPass() ? '@' : '',
132 10
            $this->getHost(),
133 10
            $this->getPort() ? ':' . $this->getPort() : '',
134 10
            $this->getPath() ? '/' . ltrim($this->getPath(), '/') : '',
135 10
            $this->getQuery() ? '?' . $this->getQuery() : '',
136 10
            $this->getFragment() ? '#' . $this->getFragment() : '',
137
        ];
138
    }
139
140
    /**
141
     * Gets the index of the component.
142
     *
143
     * @param  int  $component
144
     *
145
     * @return int
146
     */
147 20
    protected function componentIndex(int $component = -1): int
148
    {
149 20
        return Arr::getKey($this->components, $component, -1);
150
    }
151
152
    /**
153
     * Gets the key for the component.
154
     *
155
     * @param  int  $component
156
     *
157
     * @return string|null
158
     */
159 20
    protected function componentKey(int $component = -1): ?string
160
    {
161 20
        return Arr::get($this->components, $component);
162
    }
163
164
    /**
165
     * Checks if calling the requested key is allowed.
166
     *
167
     * @param  string|null  $key
168
     *
169
     * @return bool
170
     */
171 46
    protected function allowKey(?string $key): bool
172
    {
173 46
        return in_array($key, $this->components);
174
    }
175
176
    /**
177
     * Checks if the method is a request for information.
178
     *
179
     * @param  string  $method
180
     *
181
     * @return bool
182
     */
183 46
    protected function isGetter(string $method): bool
184
    {
185 46
        return Str::startsWith($method, 'get');
186
    }
187
188
    /**
189
     * Checks if the method is a request to fill information.
190
     *
191
     * @param  string  $method
192
     *
193
     * @return bool
194
     */
195 26
    protected function isSetter(string $method): bool
196
    {
197 26
        return Str::startsWith($method, 'set');
198
    }
199
200
    /**
201
     * Gets the key of the component from the name of the magic method.
202
     *
203
     * @param  string  $method
204
     *
205
     * @return string|null
206
     */
207 46
    protected function parseKey(string $method): ?string
208
    {
209 46
        $search = Str::startsWith($method, 'get') ? 'get' : 'set';
210
211 46
        return Str::lower(Str::after($method, $search));
212
    }
213
214
    /**
215
     * Set the component key with a value.
216
     *
217
     * @param  string  $key
218
     * @param  mixed  $value
219
     *
220
     * @return $this
221
     */
222 20
    protected function set(string $key, $value): self
223
    {
224 20
        $this->parsed[$key] = is_array($value) ? http_build_query($value) : $value;
225
226 20
        return $this;
227
    }
228
229
    /**
230
     * Gets the value of the component.
231
     *
232
     * @param  string  $key
233
     *
234
     * @return string|null
235
     */
236 38
    protected function get(string $key): ?string
237
    {
238 38
        return $this->parsed[$key] ?? null;
239
    }
240
241
    /**
242
     * Checks the number of arguments passed.
243
     *
244
     * @param  string  $method
245
     * @param  array  $args
246
     * @param  int  $need
247
     */
248 42
    protected function validateArgumentsCount(string $method, array $args, int $need = 1): void
249
    {
250 42
        if (count($args) > 1) {
251 4
            throw new ArgumentCountError($method . ' expects at most ' . $need . ' parameter, ' . count($args) . ' given.');
252
        }
253 38
    }
254
}
255