Passed
Push — main ( fb7d84...5d5e24 )
by Andrey
01:21
created

HttpBuilder::__call()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6.0208

Importance

Changes 0
Metric Value
cc 6
eloc 12
c 0
b 0
f 0
nc 5
nop 2
dl 0
loc 23
ccs 11
cts 12
cp 0.9167
crap 6.0208
rs 9.2222
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\Str as StrFacade;
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 HttpBuilder setFragment(array|string $value)
17
 * @method HttpBuilder setHost(string $value)
18
 * @method HttpBuilder setPass(string $value)
19
 * @method HttpBuilder setPath(string $value)
20
 * @method HttpBuilder setPort(string $value)
21
 * @method HttpBuilder setQuery(array|string $value)
22
 * @method HttpBuilder setScheme(string $value)
23
 * @method HttpBuilder setUser(string $value)
24
 * @method string|null getFragment()
25
 * @method string|null getHost()
26
 * @method string|null getPass()
27
 * @method string|null getPath()
28
 * @method string|null getPort()
29
 * @method string|null getQuery()
30
 * @method string|null getScheme()
31
 * @method string|null getUser()
32
 */
33
final class HttpBuilder
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
    /**
49
     * Calling magic methods.
50
     *
51
     * @param  string  $method
52
     * @param  mixed  $args
53
     *
54
     * @return $this|string|null
55
     */
56 56
    public function __call($method, $args)
57
    {
58 56
        if ($this->isGetter($method) || $this->isSetter($method)) {
59 56
            $key = $this->parseKey($method);
60
61 56
            if (! $this->allowKey($key)) {
62 4
                throw new RuntimeException($method . ' method not defined.');
63
            }
64
65
            switch (true) {
66 52
                case $this->isGetter($method):
67 48
                    $this->validateArgumentsCount($method, $args, 0);
68
69 48
                    return $this->get($key);
70
71 24
                case $this->isSetter($method):
72 24
                    $this->validateArgumentsCount($method, $args);
73
74 20
                    return $this->set($key, $args[0]);
75
            }
76
        }
77
78
        throw new RuntimeException("Using an unknown method: \"{$method}\"");
79
    }
80
81
    /**
82
     * Gets the current instance of the object.
83
     *
84
     * @return $this
85
     */
86 8
    public function same(): self
87
    {
88 8
        return $this;
89
    }
90
91
    /**
92
     * Parse a URL.
93
     *
94
     * @param  string  $url
95
     * @param  int  $component
96
     *
97
     * @return $this
98
     */
99 28
    public function parse(string $url, int $component = -1): self
100
    {
101 28
        if ($component === -1) {
102 24
            HttpHelper::validateUrl($url);
103
        }
104
105 26
        $component = $this->componentIndex($component);
106 26
        $key       = $this->componentKey($component);
107
108 26
        $component === -1 || empty($key)
109 22
            ? $this->parsed = parse_url($url)
110 4
            : $this->parsed[$key] = parse_url($url, $component);
111
112 26
        return $this;
113
    }
114
115
    /**
116
     * Filling the builder with parsed data.
117
     *
118
     * @param  array  $parsed
119
     *
120
     * @return $this
121
     */
122 6
    public function raw(array $parsed): self
123
    {
124 6
        foreach ($parsed as $key => $value) {
125 6
            if (! $this->allowKey($key)) {
126 2
                throw new RuntimeException('Filling in the "' . $key . '" key is prohibited.');
127
            }
128
129 4
            $this->set($key, $value);
130
        }
131
132 4
        return $this;
133
    }
134
135
    /**
136
     * Compiles parameters to URL.
137
     *
138
     * @return string
139
     */
140 12
    public function compile(): string
141
    {
142 12
        return implode('', array_filter($this->prepare()));
143
    }
144
145
    /**
146
     * Returns parsed data.
147
     *
148
     * @return null[]|string[]
149
     */
150 4
    public function toArray(): array
151
    {
152
        return [
153 4
            'scheme'   => $this->getScheme(),
154 4
            'host'     => $this->getHost(),
155 4
            'port'     => $this->getPort(),
156 4
            'user'     => $this->getUser(),
157 4
            'pass'     => $this->getPass(),
158 4
            'query'    => $this->getQuery(),
159 4
            'path'     => $this->getPath(),
160 4
            'fragment' => $this->getFragment(),
161
        ];
162
    }
163
164
    /**
165
     * Prepares data for compilation.
166
     *
167
     * @return array
168
     */
169 12
    protected function prepare(): array
170
    {
171
        return [
172 12
            $this->getScheme() ? $this->getScheme() . '://' : '',
173 12
            $this->getUser(),
174 12
            $this->getPass() ? ':' . $this->getPass() : '',
175 12
            $this->getUser() || $this->getPass() ? '@' : '',
176 12
            $this->getHost(),
177 12
            $this->getPort() ? ':' . $this->getPort() : '',
178 12
            $this->getPath() ? '/' . ltrim($this->getPath(), '/') : '',
179 12
            $this->getQuery() ? '?' . $this->getQuery() : '',
180 12
            $this->getFragment() ? '#' . $this->getFragment() : '',
181
        ];
182
    }
183
184
    /**
185
     * Gets the index of the component.
186
     *
187
     * @param  int  $component
188
     *
189
     * @return int
190
     */
191 26
    protected function componentIndex(int $component = -1): int
192
    {
193 26
        return ArrFacade::getKey($this->components, $component, -1);
194
    }
195
196
    /**
197
     * Gets the key for the component.
198
     *
199
     * @param  int  $component
200
     *
201
     * @return string|null
202
     */
203 26
    protected function componentKey(int $component = -1): ?string
204
    {
205 26
        return ArrFacade::get($this->components, $component);
206
    }
207
208
    /**
209
     * Checks if calling the requested key is allowed.
210
     *
211
     * @param  string|null  $key
212
     *
213
     * @return bool
214
     */
215 58
    protected function allowKey(?string $key): bool
216
    {
217 58
        return in_array($key, $this->components);
218
    }
219
220
    /**
221
     * Checks if the method is a request for information.
222
     *
223
     * @param  string  $method
224
     *
225
     * @return bool
226
     */
227 56
    protected function isGetter(string $method): bool
228
    {
229 56
        return StrFacade::startsWith($method, 'get');
230
    }
231
232
    /**
233
     * Checks if the method is a request to fill information.
234
     *
235
     * @param  string  $method
236
     *
237
     * @return bool
238
     */
239 26
    protected function isSetter(string $method): bool
240
    {
241 26
        return StrFacade::startsWith($method, 'set');
242
    }
243
244
    /**
245
     * Gets the key of the component from the name of the magic method.
246
     *
247
     * @param  string  $method
248
     *
249
     * @return string|null
250
     */
251 56
    protected function parseKey(string $method): ?string
252
    {
253 56
        $search = StrFacade::startsWith($method, 'get') ? 'get' : 'set';
254
255 56
        return StrFacade::lower(StrFacade::after($method, $search));
256
    }
257
258
    /**
259
     * Set the component key with a value.
260
     *
261
     * @param  string  $key
262
     * @param  mixed  $value
263
     *
264
     * @return $this
265
     */
266 24
    protected function set(string $key, $value): self
267
    {
268 24
        $this->parsed[$key] = is_array($value) ? http_build_query($value) : $value;
269
270 24
        return $this;
271
    }
272
273
    /**
274
     * Gets the value of the component.
275
     *
276
     * @param  string  $key
277
     *
278
     * @return string|null
279
     */
280 48
    protected function get(string $key): ?string
281
    {
282 48
        return $this->parsed[$key] ?? null;
283
    }
284
285
    /**
286
     * Checks the number of arguments passed.
287
     *
288
     * @param  string  $method
289
     * @param  array  $args
290
     * @param  int  $need
291
     */
292 52
    protected function validateArgumentsCount(string $method, array $args, int $need = 1): void
293
    {
294 52
        if (count($args) > 1) {
295 4
            throw new ArgumentCountError($method . ' expects at most ' . $need . ' parameter, ' . count($args) . ' given.');
296
        }
297 48
    }
298
}
299