Url::setPort()   A
last analyzed

Complexity

Conditions 4
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

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