Uri   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 311
Duplicated Lines 0 %

Importance

Changes 10
Bugs 0 Features 1
Metric Value
eloc 91
c 10
b 0
f 1
dl 0
loc 311
rs 9.1199
wmc 41

19 Methods

Rating   Name   Duplication   Size   Complexity  
A addQueryParam() 0 18 5
A addPort() 0 11 2
A __construct() 0 8 1
A host() 0 3 1
B fromString() 0 49 7
A queryParams() 0 3 1
A addPath() 0 12 3
B toString() 0 32 7
A path() 0 3 1
A addHost() 0 5 1
A __toString() 0 3 1
A addFragment() 0 12 3
A build() 0 4 1
A scheme() 0 3 1
A addQuery() 0 13 2
A fragment() 0 3 1
A query() 0 3 1
A port() 0 3 1
A addScheme() 0 5 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
2
3
declare(strict_types=1);
4
5
namespace JustSteveKing\UriBuilder;
6
7
use InvalidArgumentException;
8
use JustSteveKing\ParameterBag\ParameterBag;
9
10
use Safe\Exceptions\UrlException;
11
12
use function Safe\parse_url;
13
14
final class Uri
15
{
16
    /**
17
     * @param ParameterBag $query
18
     * @param string $scheme
19
     * @param string $host
20
     * @param int|null $port
21
     * @param string|null $path
22
     * @param string|null $fragment
23
     * @return void
24
     */
25
    private function __construct(
26
        private ParameterBag $query,
27
        private string $scheme = '',
28
        private string $host = '',
29
        private null|int $port = null,
30
        private null|string $path = null,
31
        private null|string $fragment = null,
32
    ) {
33
    }
34
35
    /**
36
     * @return Uri
37
     */
38
    public static function build(): Uri
39
    {
40
        return new Uri(
41
            query: new ParameterBag(),
42
        );
43
    }
44
45
    /**
46
     * @param string $uri
47
     * @return Uri
48
     * @throws UrlException
49
     * @throws InvalidArgumentException
50
     */
51
    public static function fromString(string $uri): Uri
52
    {
53
        $original = $uri;
54
55
        $uri = parse_url($uri);
56
57
        if (
58
            ! is_array($uri)
0 ignored issues
show
introduced by
The condition is_array($uri) is always false.
Loading history...
59
            || ! isset($uri['scheme'], $uri['host'])
60
        ) {
61
            throw new InvalidArgumentException(
62
                message: "URI failed to parse using parse_url, please ensure is valid URL. Passed in [$uri]",
63
            );
64
        }
65
66
        $url = Uri::build()
67
            ->addScheme(
68
                scheme: $uri['scheme'],
69
            )->addHost(
70
                host: $uri['host'],
71
            );
72
73
        if (isset($uri['port'])) {
74
            $url->addPort(
75
                port: $uri['port'],
76
            );
77
        }
78
79
        if (isset($uri['path'])) {
80
            $url->addPath(
81
                path: $uri['path'],
82
            );
83
        }
84
85
        if (isset($uri['query'])) {
86
            $url->addQuery(
87
                query: $uri['query'],
88
            );
89
        }
90
91
        $fragment = parse_url($original, PHP_URL_FRAGMENT);
92
93
        if (! is_null($fragment)) {
94
            $url->addFragment(
95
                fragment: $fragment,
96
            );
97
        }
98
99
        return $url;
100
    }
101
102
    /**
103
     * @param  string $scheme
104
     * @return Uri
105
     */
106
    public function addScheme(string $scheme): Uri
107
    {
108
        $this->scheme = $scheme;
109
110
        return $this;
111
    }
112
113
    /**
114
     * @return string
115
     */
116
    public function scheme(): string
117
    {
118
        return $this->scheme;
119
    }
120
121
    /**
122
     * @param  string $host
123
     * @return Uri
124
     */
125
    public function addHost(string $host): Uri
126
    {
127
        $this->host = $host;
128
129
        return $this;
130
    }
131
132
    /**
133
     * @return string
134
     */
135
    public function host(): string
136
    {
137
        return $this->host;
138
    }
139
140
    /**
141
     * @param null|int $port
142
     * @throws InvalidArgumentException
143
     * @return Uri
144
     */
145
    public function addPort(null|int $port = null): Uri
146
    {
147
        if (is_null($port)) {
148
            throw new InvalidArgumentException(
149
                message: 'Cannot set port to a null value.',
150
            );
151
        }
152
153
        $this->port = $port;
154
155
        return $this;
156
    }
157
158
    /**
159
     * @return null|int
160
     */
161
    public function port(): null|int
162
    {
163
        return $this->port;
164
    }
165
166
    /**
167
     * @param  null|string $path
168
     * @return Uri
169
     */
170
    public function addPath(null|string $path = null): Uri
171
    {
172
        if (is_null($path)) {
173
            return $this;
174
        }
175
176
        $this->path = str_starts_with(
177
            haystack: $path,
178
            needle: '/',
179
        ) ? $path : "/$path";
180
181
        return $this;
182
    }
183
184
    /**
185
     * @return null|string
186
     */
187
    public function path(): null|string
188
    {
189
        return $this->path;
190
    }
191
192
    /**
193
     * @param  null|string $query
194
     * @throws InvalidArgumentException
195
     * @return Uri
196
     */
197
    public function addQuery(null|string $query = null): Uri
198
    {
199
        if (is_null($query)) {
200
            throw new InvalidArgumentException(
201
                message: 'Cannot set query to a null value.',
202
            );
203
        }
204
205
        $this->query = ParameterBag::fromString(
206
            attributes: $query,
207
        );
208
209
        return $this;
210
    }
211
212
    /**
213
     * @return ParameterBag
214
     */
215
    public function query(): ParameterBag
216
    {
217
        return $this->query;
218
    }
219
220
    /**
221
     * @param  string $key
222
     * @param  int|string|bool|null|array  $value
223
     * @param  bool   $covertBoolToString
224
     * @throws InvalidArgumentException
225
     * @return Uri
226
     */
227
    public function addQueryParam(string $key, int|string|bool|null|array $value, bool $covertBoolToString = false): Uri
228
    {
229
        if (! in_array(gettype($value), ['string', 'array', 'int', 'float', 'boolean'])) {
230
            throw new InvalidArgumentException(
231
                message:'Cannot set Query Parameter to: ' . gettype($value),
232
            );
233
        }
234
235
        if ($covertBoolToString && is_bool($value)) {
0 ignored issues
show
introduced by
The condition is_bool($value) is always false.
Loading history...
236
            $value = ($value) ? 'true' : 'false';
237
        }
238
239
        $this->query->set(
240
            key: $key,
241
            value: $value,
242
        );
243
244
        return $this;
245
    }
246
247
    /**
248
     * @return array<string,mixed>
249
     */
250
    public function queryParams(): array
251
    {
252
        return $this->query->all();
253
    }
254
255
    /**
256
     * @param  string $fragment
257
     * @return Uri
258
     */
259
    public function addFragment(string $fragment): Uri
260
    {
261
        if ($fragment === '') {
262
            return $this;
263
        }
264
265
        $this->fragment = str_starts_with(
266
            haystack: $fragment,
267
            needle:'#',
268
        ) ? $fragment : "#{$fragment}";
269
270
        return $this;
271
    }
272
273
    /**
274
     * @return null|string
275
     */
276
    public function fragment(): null|string
277
    {
278
        return $this->fragment;
279
    }
280
281
    /**
282
     * @return string
283
     */
284
    public function __toString(): string
285
    {
286
        return $this->toString();
287
    }
288
289
    /**
290
     * @param bool $encode
291
     * @return string
292
     */
293
    public function toString(bool $encode = false): string
294
    {
295
        $url = "$this->scheme://$this->host";
296
297
        if (! is_null($this->port)) {
298
            $url .= ":$this->port";
299
        }
300
301
        if (! is_null($this->path)) {
302
            $url .= "$this->path";
303
        }
304
305
        if (! empty($this->query->all())) {
306
            $collection = [];
307
            foreach ($this->query->all() as $key => $value) {
308
                $collection[] = "$key=$value";
309
            }
310
311
            $queryParamString = implode('&', $collection);
312
313
            if ($encode) {
314
                $queryParamString = urlencode($queryParamString);
315
            }
316
317
            $url .= "?$queryParamString";
318
        }
319
320
        if (! is_null($this->fragment)) {
321
            $url .= "$this->fragment";
322
        }
323
324
        return $url;
325
    }
326
}
327