Passed
Push — main ( c89000...333101 )
by Andrey
01:24
created

HttpBuilder::prepare()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 3
rs 9.9332
1
<?php
2
3
namespace Helldar\Support\Helpers;
4
5
use ArgumentCountError;
6
use Helldar\Support\Facades\Helpers\Arr as ArrFacade;
7
use Helldar\Support\Facades\Helpers\Http as HttpHelper;
8
use Helldar\Support\Facades\Helpers\Instance as InstanceHelper;
9
use Helldar\Support\Facades\Helpers\Str as StrFacade;
10
use Helldar\Support\Tools\HttpBuilderPrepare;
11
use RuntimeException;
12
13
/**
14
 * Based on code by Maksim (Ellrion) Platonov.
15
 *
16
 * @see https://gist.github.com/Ellrion/f51ba0d40ae1d62eeae44fd1adf7b704
17
 *
18
 * @method HttpBuilder setFragment(array|string $value)
19
 * @method HttpBuilder setHost(string $value)
20
 * @method HttpBuilder setPass(string $value)
21
 * @method HttpBuilder setPath(string $value)
22
 * @method HttpBuilder setPort(string $value)
23
 * @method HttpBuilder setQuery(array|string $value)
24
 * @method HttpBuilder setScheme(string $value)
25
 * @method HttpBuilder setUser(string $value)
26
 * @method string|null getFragment()
27
 * @method string|null getHost()
28
 * @method string|null getPass()
29
 * @method string|null getPath()
30
 * @method string|null getPort()
31
 * @method string|null getQuery()
32
 * @method string|null getScheme()
33
 * @method string|null getUser()
34
 */
35
final class HttpBuilder
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
    /**
51
     * Calling magic methods.
52
     *
53
     * @param  string  $method
54
     * @param  mixed  $args
55
     *
56
     * @return $this|string|null
57
     */
58 56
    public function __call($method, $args)
59
    {
60 56
        if ($this->isGetter($method) || $this->isSetter($method)) {
61 56
            $key = $this->parseKey($method);
62
63 56
            if (! $this->allowKey($key)) {
64 4
                throw new RuntimeException($method . ' method not defined.');
65
            }
66
67
            switch (true) {
68 52
                case $this->isGetter($method):
69 48
                    $this->validateArgumentsCount($method, $args, 0);
70
71 48
                    return $this->get($key);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type null; however, parameter $key of Helldar\Support\Helpers\HttpBuilder::get() 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

71
                    return $this->get(/** @scrutinizer ignore-type */ $key);
Loading history...
72
73 24
                case $this->isSetter($method):
74 24
                    $this->validateArgumentsCount($method, $args);
75
76 20
                    return $this->set($key, $args[0]);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type null; however, parameter $key of Helldar\Support\Helpers\HttpBuilder::set() 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

76
                    return $this->set(/** @scrutinizer ignore-type */ $key, $args[0]);
Loading history...
77
            }
78
        }
79
80
        throw new RuntimeException("Using an unknown method: \"{$method}\"");
81
    }
82
83
    /**
84
     * Gets the current instance of the object.
85
     *
86
     * @return $this
87
     */
88 8
    public function same(): self
89
    {
90 8
        return $this;
91
    }
92
93
    /**
94
     * Parse a URL.
95
     *
96
     * @param  string|null  $url
97
     * @param  int  $component
98
     *
99
     * @return $this
100
     */
101 32
    public function parse(?string $url, int $component = -1): self
102
    {
103 32
        if ($component === -1) {
104 28
            HttpHelper::validateUrl($url);
105
        }
106
107 26
        $component = $this->componentIndex($component);
108 26
        $key       = $this->componentKey($component);
109
110 26
        $component === -1 || empty($key)
111 22
            ? $this->parsed = parse_url($url)
112 4
            : $this->parsed[$key] = parse_url($url, $component);
113
114 26
        return $this;
115
    }
116
117
    /**
118
     * Filling the builder with parsed data.
119
     *
120
     * @param  array  $parsed
121
     *
122
     * @return $this
123
     */
124 6
    public function raw(array $parsed): self
125
    {
126 6
        foreach ($parsed as $key => $value) {
127 6
            if (! $this->allowKey($key)) {
128 2
                throw new RuntimeException('Filling in the "' . $key . '" key is prohibited.');
129
            }
130
131 4
            $this->set($key, $value);
132
        }
133
134 4
        return $this;
135
    }
136
137
    /**
138
     * Compiles parameters to URL.
139
     *
140
     * @return string
141
     */
142 12
    public function compile(): string
143
    {
144 12
        return implode('', array_filter(array_map(function ($value) {
145 12
            return InstanceHelper::of($value, HttpBuilderPrepare::class) ? $value->get() : $value;
146 12
        }, $this->prepare())));
147
    }
148
149
    /**
150
     * Returns parsed data.
151
     *
152
     * @return null[]|string[]
153
     */
154 4
    public function toArray(): array
155
    {
156
        return [
157 4
            'scheme'   => $this->getScheme(),
158 4
            'host'     => $this->getHost(),
159 4
            'port'     => $this->getPort(),
160 4
            'user'     => $this->getUser(),
161 4
            'pass'     => $this->getPass(),
162 4
            'query'    => $this->getQuery(),
163 4
            'path'     => $this->getPath(),
164 4
            'fragment' => $this->getFragment(),
165
        ];
166
    }
167
168
    /**
169
     * Prepares data for compilation.
170
     *
171
     * @return array
172
     */
173 12
    protected function prepare(): array
174
    {
175
        return [
176 12
            HttpBuilderPrepare::make()->of($this->getScheme())->suffix('://'),
177 12
            HttpBuilderPrepare::make()->of($this->getUser()),
178 12
            HttpBuilderPrepare::make()->of($this->getPass())->prefix(':'),
179
180 12
            $this->getUser() || $this->getPass() ? '@' : '',
181
182 12
            HttpBuilderPrepare::make()->of($this->getHost()),
183 12
            HttpBuilderPrepare::make()->of($this->getPort())->prefix(':'),
184 12
            HttpBuilderPrepare::make()->of($this->getPath())->prefix('/'),
185 12
            HttpBuilderPrepare::make()->of($this->getQuery())->prefix('?'),
186 12
            HttpBuilderPrepare::make()->of($this->getFragment())->prefix('#'),
187
        ];
188
    }
189
190
    /**
191
     * Gets the index of the component.
192
     *
193
     * @param  int  $component
194
     *
195
     * @return int
196
     */
197 26
    protected function componentIndex(int $component = -1): int
198
    {
199 26
        return ArrFacade::getKey($this->components, $component, -1);
200
    }
201
202
    /**
203
     * Gets the key for the component.
204
     *
205
     * @param  int  $component
206
     *
207
     * @return string|null
208
     */
209 26
    protected function componentKey(int $component = -1): ?string
210
    {
211 26
        return ArrFacade::get($this->components, $component);
212
    }
213
214
    /**
215
     * Checks if calling the requested key is allowed.
216
     *
217
     * @param  string|null  $key
218
     *
219
     * @return bool
220
     */
221 58
    protected function allowKey(?string $key): bool
222
    {
223 58
        return in_array($key, $this->components);
224
    }
225
226
    /**
227
     * Checks if the method is a request for information.
228
     *
229
     * @param  string  $method
230
     *
231
     * @return bool
232
     */
233 56
    protected function isGetter(string $method): bool
234
    {
235 56
        return StrFacade::startsWith($method, 'get');
236
    }
237
238
    /**
239
     * Checks if the method is a request to fill information.
240
     *
241
     * @param  string  $method
242
     *
243
     * @return bool
244
     */
245 26
    protected function isSetter(string $method): bool
246
    {
247 26
        return StrFacade::startsWith($method, 'set');
248
    }
249
250
    /**
251
     * Gets the key of the component from the name of the magic method.
252
     *
253
     * @param  string  $method
254
     *
255
     * @return string|null
256
     */
257 56
    protected function parseKey(string $method): ?string
258
    {
259 56
        $search = StrFacade::startsWith($method, 'get') ? 'get' : 'set';
260
261 56
        return StrFacade::lower(StrFacade::after($method, $search));
262
    }
263
264
    /**
265
     * Set the component key with a value.
266
     *
267
     * @param  string  $key
268
     * @param  mixed  $value
269
     *
270
     * @return $this
271
     */
272 24
    protected function set(string $key, $value): self
273
    {
274 24
        $this->parsed[$key] = is_array($value) ? http_build_query($value) : $value;
275
276 24
        return $this;
277
    }
278
279
    /**
280
     * Gets the value of the component.
281
     *
282
     * @param  string  $key
283
     *
284
     * @return string|null
285
     */
286 48
    protected function get(string $key): ?string
287
    {
288 48
        return $this->parsed[$key] ?? null;
289
    }
290
291
    /**
292
     * Checks the number of arguments passed.
293
     *
294
     * @param  string  $method
295
     * @param  array  $args
296
     * @param  int  $need
297
     */
298 52
    protected function validateArgumentsCount(string $method, array $args, int $need = 1): void
299
    {
300 52
        if (count($args) > 1) {
301 4
            throw new ArgumentCountError($method . ' expects at most ' . $need . ' parameter, ' . count($args) . ' given.');
302
        }
303 48
    }
304
}
305