Completed
Push — master ( 2430a5...3c523b )
by Joschi
03:35
created

Url   C

Complexity

Total Complexity 78

Size/Duplication

Total Lines 431
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 17
Bugs 0 Features 3
Metric Value
c 17
b 0
f 3
dl 0
loc 431
ccs 165
cts 165
cp 1
rs 5.4563
wmc 78
lcom 1
cbo 3

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 2
A __toString() 0 4 1
A getUrl() 0 4 1
F getUrlInternal() 0 72 23
A getUser() 0 4 2
A getPassword() 0 4 2
A getQueryParams() 0 8 3
A setHost() 0 16 4
A setPort() 0 14 4
A setUser() 0 6 2
A setPassword() 0 6 2
A setQuery() 0 6 1
A setQueryParams() 0 6 1
A setFragment() 0 6 1
A isRemote() 0 4 2
A isAbsolute() 0 4 2
A isAbsoluteLocal() 0 14 3
A setPath() 0 8 2
A setScheme() 0 14 4
C matches() 0 53 16

How to fix   Complexity   

Complex Class

Complex classes like Url 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Url, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object\Domain
8
 * @author      Joschi Kuphal <[email protected]> / @jkphl
9
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
10
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
11
 */
12
13
/***********************************************************************************
14
 *  The MIT License (MIT)
15
 *
16
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
17
 *
18
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
19
 *  this software and associated documentation files (the "Software"), to deal in
20
 *  the Software without restriction, including without limitation the rights to
21
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
22
 *  the Software, and to permit persons to whom the Software is furnished to do so,
23
 *  subject to the following conditions:
24
 *
25
 *  The above copyright notice and this permission notice shall be included in all
26
 *  copies or substantial portions of the Software.
27
 *
28
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
30
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
31
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
32
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34
 ***********************************************************************************/
35
36
namespace Apparat\Object\Domain\Model\Path;
37
38
use Apparat\Object\Application\Utility\ArrayUtility;
39
use Apparat\Object\Domain\Model\Path\Traits\Psr7Trait;
40
use Psr\Http\Message\UriInterface;
41
42
/**
43
 * Object URL
44
 *
45
 * @package Apparat\Object\Domain\Model
46
 */
47
class Url implements UriInterface
48
{
49
    /**
50
     * Use PSR-7 method
51
     */
52
    use Psr7Trait;
53
    /**
54
     * HTTP-Schema
55
     *
56
     * @var string
57
     */
58
    const SCHEME_HTTP = 'http';
59
    /**
60
     * HTTPS-Schema
61
     *
62
     * @var string
63
     */
64
    const SCHEME_HTTPS = 'https';
65
    /**
66
     * Valid schemes
67
     *
68
     * @var array
69
     */
70
    protected static $schemes = [self::SCHEME_HTTP => true, self::SCHEME_HTTPS => true];
71
    /**
72
     * URL parts
73
     *
74
     * @var array
75
     */
76
    protected $urlParts = null;
77
78
    /**
79
     * URL constructor
80
     *
81
     * @param string $url URL
82
     * @throws InvalidArgumentException If the URL is invalid
83
     */
84 52
    public function __construct($url)
85
    {
86
87
        // Parse the URL
88 52
        $this->urlParts = @parse_url($url);
89 52
        if ($this->urlParts === false) {
90 1
            throw new InvalidArgumentException(
91 1
                sprintf('Invalid URL "%s"', $url),
92
                InvalidArgumentException::INVALID_URL
93 1
            );
94
        }
95 51
    }
96
97
    /**
98
     * Return the serialized URL
99
     *
100
     * @return string Serialized URL
101
     */
102 26
    public function __toString()
103
    {
104 26
        return $this->getUrl();
105
    }
106
107
    /**
108
     * Return the full serialized URL
109
     *
110
     * @return string Full URL
111
     */
112 26
    public function getUrl()
113
    {
114 26
        return $this->getUrlInternal();
115
    }
116
117
    /**
118
     * Return the a complete serialized URL
119
     *
120
     * @param array $override Override components
121
     * @return string Serialized URL
122
     */
123 29
    protected function getUrlInternal(array &$override = [])
124
    {
125
        // Prepare the URL scheme
126 29
        $scheme = !empty($this->urlParts['scheme']) ? $this->getScheme().'://' : '';
127 29
        if (isset($override['scheme'])) {
128 1
            $scheme = trim($override['scheme']);
129 1
            if (strlen($scheme)) {
130 1
                $scheme .= '://';
131 1
            }
132 1
        }
133 29
        $override['scheme'] = $scheme;
134
135
        // Prepare the URL user
136 29
        $user = !empty($this->urlParts['user']) ? rawurlencode($this->getUser()) : '';
137 29
        if (isset($override['user'])) {
138 1
            $user = $override['user'];
139 1
        }
140 29
        $override['user'] = $user;
141
142
        // Prepare the URL password
143 29
        $pass = !empty($this->urlParts['pass']) ? ':'.rawurlencode($this->getPassword()) : '';
144 29
        if (isset($override['pass'])) {
145 1
            $pass = ':'.$override['pass'];
146 1
        }
147 29
        if ($user || $pass) {
148 6
            $pass .= '@';
149 6
        }
150 29
        $override['pass'] = $pass;
151
152
        // Prepare the URL host
153 29
        $host = !empty($this->urlParts['host']) ? $this->getHost() : '';
154 29
        if (isset($override['host'])) {
155 1
            $host = $override['host'];
156 1
        }
157 29
        $override['host'] = $host;
158
159
        // Prepare the URL port
160 29
        $port = !empty($this->urlParts['port']) ? ':'.$this->getPort() : '';
161 29
        if (isset($override['port'])) {
162 1
            $port = ':'.$override['port'];
163 1
        }
164 29
        $override['port'] = $port;
165
166
        // Prepare the URL path
167 29
        $path = empty($this->urlParts['path']) ? '' : $this->urlParts['path'];
168 29
        if (isset($override['path'])) {
169 1
            $path = $override['path'];
170 1
        }
171 29
        $override['path'] = $path;
172
173
        // Prepare the URL query
174 29
        $query = !empty($this->urlParts['query']) ? '?'.$this->urlParts['query'] : '';
175 29
        if (isset($override['query'])) {
176 4
            $query = (is_array($override['query']) ? http_build_query($override['query']) : strval($override['query']));
177 4
            if (strlen($query)) {
178 1
                $query = '?'.$query;
179 1
            }
180 4
        }
181 29
        $override['query'] = $query;
182
183
        // Prepare the URL fragment
184 29
        $fragment = !empty($this->urlParts['fragment']) ? '#'.$this->getFragment() : '';
185 29
        if (isset($override['fragment'])) {
186 4
            $fragment = $override['fragment'];
187 4
            if (strlen($fragment)) {
188 1
                $fragment = '#'.$fragment;
189 1
            }
190 4
        }
191 29
        $override['fragment'] = $fragment;
192
193 29
        return "$scheme$user$pass$host$port$path$query$fragment";
194
    }
195
196
    /**
197
     * Return the URL user
198
     *
199
     * @return string|NULL URL user
200
     */
201 11
    public function getUser()
202
    {
203 11
        return isset($this->urlParts['user']) ? $this->urlParts['user'] : null;
204
    }
205
206
    /**
207
     * Return the URL password
208
     *
209
     * @return string|NULL URL password
210
     */
211 11
    public function getPassword()
212
    {
213 11
        return isset($this->urlParts['pass']) ? $this->urlParts['pass'] : null;
214
    }
215
216
    /**
217
     * Return the URL query parameters as list
218
     *
219
     * @return array URL query parameters
220
     */
221 2
    public function getQueryParams()
222
    {
223 2
        $query = [];
224 2
        if (isset($this->urlParts['query']) && !empty($this->urlParts['query'])) {
225 2
            parse_str($this->urlParts['query'], $query);
226 2
        }
227 2
        return ArrayUtility::sortRecursiveByKey((array)$query);
228
    }
229
230
    /**
231
     * Set the URL host
232
     *
233
     * @param string $host URL host
234
     * @return Url New URL
235
     * @throws InvalidArgumentException If the URL host is invalid
236
     */
237 2
    public function setHost($host)
238
    {
239
        // If the hostname is invalid
240 2
        if (preg_match("%[/\?#]%", $host) ||
241 2
            (!filter_var('http://'.$host, FILTER_VALIDATE_URL) && !filter_var($host, FILTER_VALIDATE_IP))
242 2
        ) {
243 1
            throw new InvalidArgumentException(
244 1
                sprintf('Invalid URL host "%s"', $host),
245
                InvalidArgumentException::INVALID_URL_HOST
246 1
            );
247
        }
248
249 2
        $url = clone $this;
250 2
        $url->urlParts['host'] = $host;
251 2
        return $url;
252
    }
253
254
    /**
255
     * Set the URL port
256
     *
257
     * @param int|null $port URL port
258
     * @return Url New URL
259
     * @throws InvalidArgumentException If the URL port is invalid
260
     */
261 2
    public function setPort($port)
262
    {
263
        // If the URL port is invalid
264 2
        if (is_int($port) ? (($port < 0) || ($port > 65535)) : ($port !== null)) {
265 1
            throw new InvalidArgumentException(
266 1
                sprintf('Invalid URL port "%s"', $port),
267
                InvalidArgumentException::INVALID_URL_PORT
268 1
            );
269
        }
270
271 2
        $url = clone $this;
272 2
        $url->urlParts['port'] = $port;
273 2
        return $url;
274
    }
275
276
    /**
277
     * Set the URL user
278
     *
279
     * @param string|NULL $user URL user
280
     * @return Url New URL
281
     */
282 2
    public function setUser($user)
283
    {
284 2
        $url = clone $this;
285 2
        $url->urlParts['user'] = $user ?: null;
286 2
        return $url;
287
    }
288
289
    /**
290
     * Set the URL password
291
     *
292
     * @param string $pass URL password
293
     * @return Url New URL
294
     */
295 2
    public function setPassword($pass)
296
    {
297 2
        $url = clone $this;
298 2
        $url->urlParts['pass'] = $pass ?: null;
299 2
        return $url;
300
    }
301
302
    /**
303
     * Set the URL query
304
     *
305
     * @param array $query URL query
306
     * @return Url New URL
307
     */
308 2
    public function setQuery($query)
309
    {
310 2
        $url = clone $this;
311 2
        $url->urlParts['query'] = trim($query);
312 2
        return $url;
313
    }
314
315
    /**
316
     * Set the URL query parameters
317
     *
318
     * @param array $query URL query parameters
319
     * @return Url New URL
320
     */
321 1
    public function setQueryParams(array $query)
322
    {
323 1
        $url = clone $this;
324 1
        $url->urlParts['query'] = http_build_query($query);
325 1
        return $url;
326
    }
327
328
    /**
329
     * Set the URL fragment
330
     *
331
     * @param string $fragment URL fragment
332
     * @return Url New URL
333
     */
334 2
    public function setFragment($fragment)
335
    {
336 2
        $url = clone $this;
337 2
        $url->urlParts['fragment'] = $fragment;
338 2
        return $url;
339
    }
340
341
    /**
342
     * Test whether this URL is remote
343
     *
344
     * @return bool Remote URL
345
     */
346 1
    public function isRemote()
347
    {
348 1
        return $this->isAbsolute() && !$this->isAbsoluteLocal();
349
    }
350
351
    /**
352
     * Return whether this URL is absolute
353
     *
354
     * @return bool Absolute URL
355
     */
356 46
    public function isAbsolute()
357
    {
358 46
        return !empty($this->urlParts['scheme']) && !empty($this->urlParts['host']);
359
    }
360
361
    /**
362
     * Test whether this URL belongs to the local Apparat instance
363
     *
364
     * @return bool URL belongs to the local Apparat instance
365
     */
366 21
    public function isAbsoluteLocal()
367
    {
368
        // Instantiate the apparat base URL
369 21
        $apparatBaseUrl = new self(getenv('APPARAT_BASE_URL'));
370 21
        $apparatBaseUrlPath = $apparatBaseUrl->getPath();
371 21
        $apparatBaseUrl = $apparatBaseUrl->setScheme(null)->setPath(null);
372
373
        // If the URL matches the Apparat base URL (the scheme is disregarded)
374 21
        return $this->isAbsolute() && $this->matches($apparatBaseUrl) && !strncmp(
375 3
            $apparatBaseUrlPath,
376 3
            $this->getPath(),
377 3
            strlen($apparatBaseUrlPath)
378 21
        );
379
    }
380
381
    /**
382
     * Set the URL path
383
     *
384
     * @param string $path URL path
385
     * @return Url New URL
386
     */
387 23
    public function setPath($path)
388
    {
389 23
        $path = trim($path, '/');
390
391 23
        $url = clone $this;
392 23
        $url->urlParts['path'] = strlen($path) ? '/'.$path : null;
393 23
        return $url;
394
    }
395
396
    /**
397
     * Set the URL scheme
398
     *
399
     * @param string $scheme URL scheme
400
     * @return Url New URL
401
     * @throws InvalidArgumentException If the URL scheme is invalid
402
     */
403 23
    public function setScheme($scheme)
404
    {
405
        // If the URL scheme is not valid
406 23
        if (strlen($scheme) && !array_key_exists(strtolower($scheme), static::$schemes)) {
407 1
            throw new InvalidArgumentException(
408 1
                sprintf('Invalid URL scheme "%s"', $scheme),
409
                InvalidArgumentException::INVALID_URL_SCHEME
410 1
            );
411
        }
412
413 23
        $url = clone $this;
414 23
        $url->urlParts['scheme'] = $scheme ? strtolower($scheme) : null;
415 23
        return $url;
416
    }
417
418
    /**
419
     * Test if this URL matches all available parts of a given URL
420
     *
421
     * @param Url $url Comparison URL
422
     * @return bool This URL matches all available parts of the given URL
423
     */
424 8
    public function matches(Url $url)
425
    {
426
427
        // Test the scheme
428 8
        $urlScheme = $url->getScheme();
429 8
        if (($urlScheme !== null) && ($this->getScheme() !== $urlScheme)) {
430 2
            return false;
431
        }
432
433
        // Test the user
434 8
        $urlUser = $url->getUser();
435 8
        if (($urlUser !== null) && ($this->getUser() !== $urlUser)) {
436 1
            return false;
437
        }
438
439
        // Test the password
440 8
        $urlPassword = $url->getPassword();
441 8
        if (($urlPassword !== null) && ($this->getPassword() !== $urlPassword)) {
442 1
            return false;
443
        }
444
445
        // Test the host
446 8
        $urlHost = $url->getHost();
447 8
        if (($urlHost !== null) && ($this->getHost() !== $urlHost)) {
448 5
            return false;
449
        }
450
451
        // Test the port
452 5
        $urlPort = $url->getPort();
453 5
        if (($urlPort !== null) && ($this->getPort() !== $urlPort)) {
454 1
            return false;
455
        }
456
457
        // Test the path
458 5
        $urlPath = $url->getPath();
459 5
        if (($urlPath !== null) && ($this->getPath() !== $urlPath)) {
460 1
            return false;
461
        }
462
463
        // Test the query
464 5
        $urlQuery = $url->getQuery();
465 5
        if (serialize($this->getQuery()) !== serialize($urlQuery)) {
466 1
            return false;
467
        }
468
469
        // Test the fragment
470 5
        $urlFragment = $url->getFragment();
471 5
        if (($urlFragment !== null) && ($this->getFragment() !== $urlFragment)) {
472 1
            return false;
473
        }
474
475 5
        return true;
476
    }
477
}
478