Passed
Pull Request — main (#6)
by
unknown
12:23
created

Uri   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 382
Duplicated Lines 0 %

Importance

Changes 10
Bugs 0 Features 1
Metric Value
eloc 100
dl 0
loc 382
rs 8.8
c 10
b 0
f 1
wmc 45

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A build() 0 4 1
A addPort() 0 11 2
A host() 0 3 1
B fromString() 0 49 8
A addPath() 0 12 3
A addHost() 0 5 1
A scheme() 0 3 1
A port() 0 3 1
A addScheme() 0 5 1
A addQueryParam() 0 18 5
A appendToPath() 0 13 3
A queryParams() 0 3 1
B toString() 0 32 7
A path() 0 3 1
A __toString() 0 3 1
A addFragment() 0 12 3
A addQuery() 0 13 2
A fragment() 0 3 1
A query() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Uri 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 Uri, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace JustSteveKing\UriBuilder;
4
5
use InvalidArgumentException;
6
use JustSteveKing\ParameterBag\ParameterBag;
7
8
final class Uri
9
{
10
    /**
11
     * Uri constructor.
12
     *
13
     * @param ParameterBag $query
14
     * @param string $scheme
15
     * @param string $host
16
     * @param int|null $port
17
     * @param string|null $path
18
     * @param string|null $fragment
19
     *
20
     * @return void
21
     */
22
    private function __construct(
23
        private ParameterBag $query,
24
        private string $scheme = '',
25
        private string $host = '',
26
        private null|int $port = null,
27
        private null|string $path = null,
28
        private null|string $fragment = null,
29
    ){}
30
31
    /**
32
     * Build a new Uri Builder.
33
     *
34
     * @return Uri
35
     */
36
    public static function build(): Uri
37
    {
38
        return new Uri(
39
            query: new ParameterBag(),
40
        );
41
    }
42
43
    /**
44
     * Build a new Uri Builder from a string.
45
     *
46
     * @param  string $uri
47
     *
48
     * @throws InvalidArgumentException
49
     *
50
     * @return self
51
     */
52
    public static function fromString(string $uri): self
53
    {
54
        $original = $uri;
55
56
        $uri = parse_url($uri);
57
58
        if (
59
            ! is_array($uri)
0 ignored issues
show
introduced by
The condition is_array($uri) is always true.
Loading history...
60
            || ! isset($uri['scheme'], $uri['host'])
61
        ) {
62
            throw new InvalidArgumentException(
63
                message: "URI failed to parse using parse_url, please ensure is valid URL. Passed in [$uri]",
64
            );
65
        }
66
67
        $url = static::build()
68
            ->addScheme(
69
                scheme: $uri['scheme'],
70
            )->addHost(
71
                host: $uri['host'],
72
            );
73
74
        if (isset($uri['port'])) {
75
            $url->addPort(
76
                port: $uri['port'],
77
            );
78
        }
79
80
        if (isset($uri['path'])) {
81
            $url->addPath(
82
                path: $uri['path'],
83
            );
84
        }
85
86
        if (isset($uri['query'])) {
87
            $url->addQuery(
88
                query: $uri['query'],
89
            );
90
        }
91
92
        $fragment = parse_url($original, PHP_URL_FRAGMENT);
93
94
        if (! is_null($fragment) && $fragment !== false) {
95
            $url->addFragment(
96
                fragment: $fragment,
97
            );
98
        }
99
100
        return $url;
101
    }
102
103
    /**
104
     * Add Scheme.
105
     *
106
     * @param  string $scheme
107
     *
108
     *
109
     * @return self
110
     */
111
    public function addScheme(string $scheme): self
112
    {
113
        $this->scheme = $scheme;
114
115
        return $this;
116
    }
117
118
    /**
119
     * Get the Uri Scheme.
120
     *
121
     * @return string
122
     */
123
    public function scheme(): string
124
    {
125
        return $this->scheme;
126
    }
127
128
    /**
129
     * Set the Uri Host.
130
     *
131
     * @param  string $host
132
     *
133
     *
134
     * @return self
135
     */
136
    public function addHost(string $host): self
137
    {
138
        $this->host = $host;
139
140
        return $this;
141
    }
142
143
    /**
144
     * Get the Uri Host.
145
     *
146
     * @return string
147
     */
148
    public function host(): string
149
    {
150
        return $this->host;
151
    }
152
153
    /**
154
     * Set the Uri Port.
155
     *
156
     * @param null|int $port
157
     *
158
     * @throws InvalidArgumentException
159
     *
160
     * @return $this
161
     */
162
    public function addPort(null|int $port = null): self
163
    {
164
        if (is_null($port)) {
165
            throw new InvalidArgumentException(
166
                message: 'Cannot set port to a null value.',
167
            );
168
        }
169
170
        $this->port = $port;
171
172
        return $this;
173
    }
174
175
    /**
176
     * Get the Uri Port.
177
     *
178
     * @return null|int
179
     */
180
    public function port(): null|int
181
    {
182
        return $this->port;
183
    }
184
185
    /**
186
     * Set the Uri Path.
187
     *
188
     * @param  null|string $path
189
     * @return self
190
     */
191
    public function addPath(null|string $path = null): self
192
    {
193
        if (is_null($path)) {
194
            return $this;
195
        }
196
197
        $this->path = str_starts_with(
198
            haystack: $path,
199
            needle: '/',
200
        ) ? $path : "/$path";
201
202
        return $this;
203
    }
204
205
    /**
206
     * Appends given path to current path value. Auto adds a slash if it needs to be added.
207
     *
208
     * @param  string $path
209
     * @return self
210
     */
211
    public function appendToPath(string $path): self
212
    {
213
        $shouldPrefixSlash = str_ends_with(
214
                haystack: $this->path,
0 ignored issues
show
Bug introduced by
It seems like $this->path can also be of type null; however, parameter $haystack of str_ends_with() 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

214
                /** @scrutinizer ignore-type */ haystack: $this->path,
Loading history...
215
                needle: '/',
216
            ) === false && str_starts_with(
217
                haystack: $path,
218
                needle: '/',
219
            ) === false;
220
221
        $this->path .= $shouldPrefixSlash ? "/$path" : "$path";
222
223
        return $this;
224
    }
225
226
    /**
227
     * Get the Uri Path.
228
     *
229
     * @return null|string
230
     */
231
    public function path(): null|string
232
    {
233
        return $this->path;
234
    }
235
236
    /**
237
     * Set the Uri Query.
238
     *
239
     * @param  null|string $query
240
     *
241
     * @throws InvalidArgumentException
242
     *
243
     * @return self
244
     */
245
    public function addQuery(null|string $query = null): self
246
    {
247
        if (is_null($query)) {
248
            throw new InvalidArgumentException(
249
                message: 'Cannot set query to a null value.',
250
            );
251
        }
252
253
        $this->query = ParameterBag::fromString(
254
            attributes: $query,
255
        );
256
257
        return $this;
258
    }
259
260
    /**
261
     * Get the Uri Query.
262
     *
263
     * @return ParameterBag
264
     */
265
    public function query(): ParameterBag
266
    {
267
        return $this->query;
268
    }
269
270
    /**
271
     * Set a Query Param.
272
     *
273
     * @param  string $key
274
     * @param  mixed  $value
275
     * @param  bool   $covertBoolToString
276
     *
277
     * @throws InvalidArgumentException
278
     *
279
     * @return self
280
     */
281
    public function addQueryParam(string $key, mixed $value, bool $covertBoolToString = false): self
282
    {
283
        if (! in_array(gettype($value), ['string', 'array', 'int', 'float', 'boolean'])) {
284
            throw new InvalidArgumentException(
285
                message:'Cannot set Query Parameter to: ' . gettype($value),
286
            );
287
        }
288
289
        if ($covertBoolToString && is_bool($value)) {
290
            $value = ($value) ? 'true' : 'false';
291
        }
292
293
        $this->query->set(
294
            key: $key,
295
            value: $value,
296
        );
297
298
        return $this;
299
    }
300
301
    /**
302
     * Get the Uri Query Parameters.
303
     *
304
     * @return array
305
     */
306
    public function queryParams(): array
307
    {
308
        return $this->query->all();
309
    }
310
311
    /**
312
     * Set the Uri Fragment.
313
     *
314
     * @param  string $fragment
315
     * @return self
316
     */
317
    public function addFragment(string $fragment): self
318
    {
319
        if ($fragment === '') {
320
            return $this;
321
        }
322
323
        $this->fragment = str_starts_with(
324
            haystack: $fragment,
325
            needle:'#',
326
        ) ? $fragment : "#{$fragment}";
327
328
        return $this;
329
    }
330
331
    /**
332
     * Set the Uri Fragment.
333
     *
334
     * @return null|string
335
     */
336
    public function fragment(): null|string
337
    {
338
        return $this->fragment;
339
    }
340
341
    /**
342
     * Turn Uri to String - proxies to toString().
343
     *
344
     * @return string
345
     */
346
    public function __toString(): string
347
    {
348
        return $this->toString();
349
    }
350
351
    /**
352
     * Turn Uri to String.
353
     *
354
     * @param bool $encode
355
     *
356
     * @return string
357
     */
358
    public function toString(bool $encode = false): string
359
    {
360
        $url = "$this->scheme://$this->host";
361
362
        if (! is_null($this->port)) {
363
            $url .= ":$this->port";
364
        }
365
366
        if (! is_null($this->path)) {
367
            $url .= "$this->path";
368
        }
369
370
        if (! empty($this->query->all())) {
371
            $collection = [];
372
            foreach ($this->query->all() as $key => $value) {
373
                $collection[] = "$key=$value";
374
            }
375
376
            $queryParamString = implode('&', $collection);
377
378
            if ($encode) {
379
                $queryParamString = urlencode($queryParamString);
380
            }
381
382
            $url .= "?$queryParamString";
383
        }
384
385
        if (! is_null($this->fragment)) {
386
            $url .= "$this->fragment";
387
        }
388
389
        return $url;
390
    }
391
}
392