Completed
Push — master ( 99686d...4efdc3 )
by Joschi
02:54
created

Url::getUrlInternal()   F

Complexity

Conditions 23
Paths > 20000

Size

Total Lines 80
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 54
CRAP Score 23
Metric Value
dl 0
loc 80
ccs 54
cts 54
cp 1
rs 2.2424
cc 23
eloc 50
nc 46656
nop 1
crap 23

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
40
/**
41
 * Object URL
42
 *
43
 * @package Apparat\Object\Domain\Model
44
 */
45
class Url
46
{
47
    /**
48
     * HTTP-Schema
49
     *
50
     * @var string
51
     */
52
    const SCHEME_HTTP = 'http';
53
    /**
54
     * HTTPS-Schema
55
     *
56
     * @var string
57
     */
58
    const SCHEME_HTTPS = 'https';
59
    /**
60
     * Valid schemes
61
     *
62
     * @var array
63
     */
64
    protected static $schemes = [self::SCHEME_HTTP => true, self::SCHEME_HTTPS => true];
65
    /**
66
     * URL parts
67
     *
68
     * @var array
69
     */
70
    protected $urlParts = null;
71
72
    /*******************************************************************************
73
     * PUBLIC METHODS
74
     *******************************************************************************/
75
76
    /**
77
     * URL constructor
78
     *
79
     * @param string $url URL
80
     * @throws InvalidArgumentException If the URL is invalid
81
     */
82 34
    public function __construct($url)
83
    {
84
85
        // Parse the URL
86 34
        $this->urlParts = @parse_url($url);
87 34
        if ($this->urlParts === false) {
88 1
            throw new InvalidArgumentException(
89 1
                sprintf('Invalid URL "%s"', $url),
90
                InvalidArgumentException::INVALID_URL
91 1
            );
92
        }
93 33
    }
94
95
    /**
96
     * Return the serialized URL
97
     *
98
     * @return string Serialized URL
99
     */
100 8
    public function __toString()
101
    {
102 8
        return $this->getUrl();
103
    }
104
105
    /**
106
     * Return the full serialized URL
107
     *
108
     * @return string Full URL
109
     */
110 8
    public function getUrl()
111
    {
112 8
        return $this->getUrlInternal();
113
    }
114
115
    /**
116
     * Return the a complete serialized URL
117
     *
118
     * @param array $override Override components
119
     * @return string Serialized URL
120
     */
121 11
    protected function getUrlInternal(array &$override = [])
122
    {
123
        // Prepare the URL scheme
124 11
        if (isset($override['scheme'])) {
125 1
            $scheme = trim($override['scheme']);
126 1
            if (strlen($scheme)) {
127 1
                $scheme .= '://';
128 1
            }
129 1
        } else {
130 10
            $scheme = !empty($this->urlParts['scheme']) ? $this->getScheme().'://' : '';
131
        }
132 11
        $override['scheme'] = $scheme;
133
134
        // Prepare the URL user
135 11
        if (isset($override['user'])) {
136 1
            $user = $override['user'];
137 1
        } else {
138 10
            $user = !empty($this->urlParts['user']) ? rawurlencode($this->getUser()) : '';
139
        }
140 11
        $override['user'] = $user;
141
142
        // Prepare the URL password
143 11
        if (isset($override['pass'])) {
144 1
            $pass = ':'.$override['pass'];
145 1
        } else {
146 10
            $pass = !empty($this->urlParts['pass']) ? ':'.rawurlencode($this->getPassword()) : '';
147
        }
148 11
        if ($user || $pass) {
149 5
            $pass .= '@';
150 5
        }
151 11
        $override['pass'] = $pass;
152
153
        // Prepare the URL host
154 11
        if (isset($override['host'])) {
155 1
            $host = $override['host'];
156 1
        } else {
157 10
            $host = !empty($this->urlParts['host']) ? $this->getHost() : '';
158
        }
159 11
        $override['host'] = $host;
160
161
        // Prepare the URL port
162 11
        if (isset($override['port'])) {
163 1
            $port = ':'.$override['port'];
164 1
        } else {
165 10
            $port = !empty($this->urlParts['port']) ? ':'.$this->getPort() : '';
166
        }
167 11
        $override['port'] = $port;
168
169
        // Prepare the URL path
170 11
        if (isset($override['path'])) {
171 1
            $path = $override['path'];
172 1
        } else {
173 10
            $path = empty($this->urlParts['path']) ? '' : $this->urlParts['path'];
174
        }
175 11
        $override['path'] = $path;
176
177
        // Prepare the URL query
178 11
        if (isset($override['query'])) {
179 4
            $query = (is_array($override['query']) ? http_build_query($override['query']) : strval($override['query']));
180 4
            if (strlen($query)) {
181 1
                $query = '?'.$query;
182 1
            }
183 4
        } else {
184 8
            $query = !empty($this->urlParts['query']) ? '?'.$this->urlParts['query'] : '';
185
        }
186 11
        $override['query'] = $query;
187
188
        // Prepare the URL fragment
189 11
        if (isset($override['fragment'])) {
190 4
            $fragment = $override['fragment'];
191 4
            if (strlen($fragment)) {
192 1
                $fragment = '#'.$fragment;
193 1
            }
194 4
        } else {
195 8
            $fragment = !empty($this->urlParts['fragment']) ? '#'.$this->getFragment() : '';
196
        }
197 11
        $override['fragment'] = $fragment;
198
199 11
        return "$scheme$user$pass$host$port$path$query$fragment";
200
    }
201
202
    /**
203
     * Return the URL scheme
204
     *
205
     * @return string URL scheme
206
     */
207 14
    public function getScheme()
208
    {
209 14
        return isset($this->urlParts['scheme']) ? $this->urlParts['scheme'] : null;
210
    }
211
212
    /**
213
     * Return the URL user
214
     *
215
     * @return string|NULL URL user
216
     */
217 10
    public function getUser()
218
    {
219 10
        return isset($this->urlParts['user']) ? $this->urlParts['user'] : null;
220
    }
221
222
    /**
223
     * Return the URL password
224
     *
225
     * @return string|NULL URL password
226
     */
227 10
    public function getPassword()
228
    {
229 10
        return isset($this->urlParts['pass']) ? $this->urlParts['pass'] : null;
230
    }
231
232
    /**
233
     * Return the URL host
234
     *
235
     * @return string URL host
236
     */
237 14
    public function getHost()
238
    {
239 14
        return isset($this->urlParts['host']) ? $this->urlParts['host'] : null;
240
    }
241
242
    /**
243
     * Return the URL port
244
     *
245
     * @return int URL port
246
     */
247 10
    public function getPort()
248
    {
249 10
        return isset($this->urlParts['port']) ? $this->urlParts['port'] : null;
250
    }
251
252
    /**
253
     * Return the URL fragment
254
     *
255
     * @return string URL fragment
256
     */
257 7
    public function getFragment()
258
    {
259 7
        return isset($this->urlParts['fragment']) ? $this->urlParts['fragment'] : null;
260
    }
261
262
    /**
263
     * Set the URL host
264
     *
265
     * @param string $host URL host
266
     * @return Url New URL
267
     * @throws InvalidArgumentException If the URL host is invalid
268
     */
269 1
    public function setHost($host)
270
    {
271
        // If the hostname is invalid
272 1
        if (preg_match("%[/\?#]%", $host) || (!filter_var('http://'.$host, FILTER_VALIDATE_URL) && !filter_var(
273 1
                    $host,
274
                    FILTER_VALIDATE_IP
275 1
                ))
276 1
        ) {
277 1
            throw new InvalidArgumentException(
278 1
                sprintf('Invalid URL host "%s"', $host),
279
                InvalidArgumentException::INVALID_URL_HOST
280 1
            );
281
        }
282
283 1
        $url = clone $this;
284 1
        $url->urlParts['host'] = $host;
285 1
        return $url;
286
    }
287
288
    /**
289
     * Set the URL port
290
     *
291
     * @param int $port URL port
292
     * @return Url New URL
293
     * @throws InvalidArgumentException If the URL port is invalid
294
     */
295 1
    public function setPort($port)
296
    {
297
        // If the URL port is invalid
298 1
        if (!is_int($port) || ($port < 0) || ($port > 65535)) {
299 1
            throw new InvalidArgumentException(
300 1
                sprintf('Invalid URL port "%s"', $port),
301
                InvalidArgumentException::INVALID_URL_PORT
302 1
            );
303
        }
304
305 1
        $url = clone $this;
306 1
        $url->urlParts['port'] = $port;
307 1
        return $url;
308
    }
309
310
    /**
311
     * Set the URL user
312
     *
313
     * @param string|NULL $user URL user
314
     * @return Url New URL
315
     */
316 1
    public function setUser($user)
317
    {
318 1
        $url = clone $this;
319 1
        $url->urlParts['user'] = $user ?: null;
320 1
        return $url;
321
    }
322
323
    /**
324
     * Set the URL password
325
     *
326
     * @param string $pass URL password
327
     * @return Url New URL
328
     */
329 1
    public function setPassword($pass)
330
    {
331 1
        $url = clone $this;
332 1
        $url->urlParts['pass'] = $pass ?: null;
333 1
        return $url;
334
    }
335
336
    /**
337
     * Set the URL query
338
     *
339
     * @param array $query URL query
340
     * @return Url New URL
341
     */
342 1
    public function setQuery(array $query)
343
    {
344 1
        $url = clone $this;
345 1
        $url->urlParts['query'] = http_build_query($query);
346 1
        return $url;
347
    }
348
349
    /**
350
     * Set the URL fragment
351
     *
352
     * @param string $fragment URL fragment
353
     * @return Url New URL
354
     */
355 1
    public function setFragment($fragment)
356
    {
357 1
        $url = clone $this;
358 1
        $url->urlParts['fragment'] = $fragment;
359 1
        return $url;
360
    }
361
362
    /**
363
     * Test whether this URL is remote
364
     *
365
     * @return bool Remote URL
366
     */
367 1
    public function isRemote()
368
    {
369 1
        return $this->isAbsolute() && !$this->isAbsoluteLocal();
370
    }
371
372
    /**
373
     * Return whether this URL is absolute
374
     *
375
     * @return bool Absolute URL
376
     */
377 31
    public function isAbsolute()
378
    {
379 31
        return !empty($this->urlParts['scheme']) && !empty($this->urlParts['host']);
380
    }
381
382
    /**
383
     * Test whether this URL belongs to the local Apparat instance
384
     *
385
     * @return bool URL belongs to the local Apparat instance
386
     */
387 12
    public function isAbsoluteLocal()
388
    {
389
        // Instantiate the apparat base URL
390 12
        $apparatBaseUrl = new self(getenv('APPARAT_BASE_URL'));
391 12
        $apparatBaseUrlPath = $apparatBaseUrl->getPath();
392 12
        $apparatBaseUrl = $apparatBaseUrl->setScheme(null)->setPath(null);
393
394
        // If the URL matches the Apparat base URL (the scheme is disregarded)
395 12
        return $this->isAbsolute() && $this->matches($apparatBaseUrl) && !strncmp(
396 3
            $apparatBaseUrlPath,
397 3
            $this->getPath(), strlen($apparatBaseUrlPath)
398 12
        );
399
    }
400
401
    /**
402
     * Return the URL path
403
     *
404
     * @return string URL path
405
     */
406 20
    public function getPath()
407
    {
408 20
        return $this->urlParts['path'];
409
    }
410
411
    /**
412
     * Set the URL path
413
     *
414
     * @param string $path URL path
415
     * @return Url New URL
416
     */
417 13
    public function setPath($path)
418
    {
419 13
        $path = trim($path, '/');
420
421 13
        $url = clone $this;
422 13
        $url->urlParts['path'] = strlen($path) ? '/'.$path : null;
423 13
        return $url;
424
    }
425
426
    /**
427
     * Set the URL scheme
428
     *
429
     * @param string $scheme URL scheme
430
     * @return Url New URL
431
     * @throws InvalidArgumentException If the URL scheme is invalid
432
     */
433 13
    public function setScheme($scheme)
434
    {
435
        // If the URL scheme is not valid
436 13
        if (strlen($scheme) && !array_key_exists($scheme, static::$schemes)) {
437 1
            throw new InvalidArgumentException(
438 1
                sprintf('Invalid URL scheme "%s"', $scheme),
439
                InvalidArgumentException::INVALID_URL_SCHEME
440 1
            );
441
        }
442
443 13
        $url = clone $this;
444 13
        $url->urlParts['scheme'] = $scheme;
445 13
        return $url;
446
    }
447
448
    /**
449
     * Test if this URL matches all available parts of a given URL
450
     *
451
     * @param Url $url Comparison URL
452
     * @return bool This URL matches all available parts of the given URL
453
     */
454 8
    public function matches(Url $url)
455
    {
456
457
        // Test the scheme
458 8
        $urlScheme = $url->getScheme();
459 8
        if (($urlScheme !== null) && ($this->getScheme() !== $urlScheme)) {
460 2
            return false;
461
        }
462
463
        // Test the user
464 8
        $urlUser = $url->getUser();
465 8
        if (($urlUser !== null) && ($this->getUser() !== $urlUser)) {
466 1
            return false;
467
        }
468
469
        // Test the password
470 8
        $urlPassword = $url->getPassword();
471 8
        if (($urlPassword !== null) && ($this->getPassword() !== $urlPassword)) {
472 1
            return false;
473
        }
474
475
        // Test the host
476 8
        $urlHost = $url->getHost();
477 8
        if (($urlHost !== null) && ($this->getHost() !== $urlHost)) {
478 5
            return false;
479
        }
480
481
        // Test the port
482 5
        $urlPort = $url->getPort();
483 5
        if (($urlPort !== null) && ($this->getPort() !== $urlPort)) {
484 1
            return false;
485
        }
486
487
        // Test the path
488 5
        $urlPath = $url->getPath();
489 5
        if (($urlPath !== null) && ($this->getPath() !== $urlPath)) {
490 1
            return false;
491
        }
492
493
        // Test the query
494 5
        $urlQuery = $url->getQuery();
495 5
        if (serialize($this->getQuery()) !== serialize($urlQuery)) {
496 1
            return false;
497
        }
498
499
        // Test the fragment
500 5
        $urlFragment = $url->getFragment();
501 5
        if (($urlFragment !== null) && ($this->getFragment() !== $urlFragment)) {
502 1
            return false;
503
        }
504
505 5
        return true;
506
    }
507
508
    /*******************************************************************************
509
     * PRIVATE METHODS
510
     *******************************************************************************/
511
512
    /**
513
     * Return the URL query
514
     *
515
     * @return array URL query
516
     */
517 7
    public function getQuery()
518
    {
519 7
        $query = [];
520 7
        if (isset($this->urlParts['query']) && !empty($this->urlParts['query'])) {
521 4
            @parse_str($this->urlParts['query'], $query);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
522 4
        }
523 7
        return ArrayUtility::sortRecursiveByKey($query);
0 ignored issues
show
Bug introduced by
It seems like $query can also be of type null; however, Apparat\Object\Applicati...y::sortRecursiveByKey() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
524
    }
525
}
526