UrlComposer::parseUrlString()   C
last analyzed

Complexity

Conditions 9
Paths 128

Size

Total Lines 49
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 9.081

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 49
ccs 36
cts 40
cp 0.9
rs 5.3846
cc 9
eloc 25
nc 128
nop 1
crap 9.081
1
<?php
2
3
namespace Retrinko\UrlComposer;
4
5
use Retrinko\UrlComposer\Exceptions\UrlException;
6
7
class UrlComposer
8
{
9
    const SCHEME_HTTP    = 'http';
10
    const PATH_SEPARATOR = '/';
11
12
    /**
13
     * @var string
14
     */
15
    protected $scheme = self::SCHEME_HTTP;
16
    /**
17
     * @var string
18
     */
19
    protected $user = '';
20
    /**
21
     * @var string
22
     */
23
    protected $pass = '';
24
    /**
25
     * @var string
26
     */
27
    protected $host = '';
28
    /**
29
     * @var int
30
     */
31
    protected $port;
32
    /**
33
     * @var array
34
     */
35
    protected $path = [];
36
    /**
37
     * @var array
38
     */
39
    protected $query = [];
40
    /**
41
     * @var string
42
     */
43
    protected $fragment = '';
44
45
    /**
46
     * Url constructor.
47
     *
48
     * @param string $url
49
     *
50
     * @throws UrlException
51
     */
52 13
    public function __construct($url = '')
53
    {
54 13 View Code Duplication
        if ('' != $url && false === filter_var($url, FILTER_VALIDATE_URL))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
55 13
        {
56 2
            throw new UrlException(sprintf('Invalid URL! (%s)', $url));
57
        }
58
59 11
        $this->parseUrlString($url);
60 11
    }
61
62
    /**
63
     * @param string $url
64
     */
65 11
    protected function parseUrlString($url)
66
    {
67
        // Parse URL
68 11
        $scheme = parse_url($url, PHP_URL_SCHEME);
69 11
        if (!is_null($scheme))
70 11
        {
71 8
            $this->scheme = $scheme;
0 ignored issues
show
Documentation Bug introduced by
It seems like $scheme can also be of type false. However, the property $scheme is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
72 8
        }
73
74 11
        $user = parse_url($url, PHP_URL_USER);
75 11
        if (!is_null($user))
76 11
        {
77
            $this->user = $user;
0 ignored issues
show
Documentation Bug introduced by
It seems like $user can also be of type false. However, the property $user is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
78
        }
79
80 11
        $pass = parse_url($url, PHP_URL_PASS);
81 11
        if (!is_null($pass))
82 11
        {
83
            $this->pass = $pass;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pass can also be of type false. However, the property $pass is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
84
        }
85
86 11
        $host = parse_url($url, PHP_URL_HOST);
87 11
        if (!is_null($host))
88 11
        {
89 8
            $this->host = idn_to_utf8($host);
90 8
        }
91
92 11
        $this->port = parse_url($url, PHP_URL_PORT);
0 ignored issues
show
Documentation Bug introduced by
It seems like parse_url($url, PHP_URL_PORT) can also be of type false. However, the property $port is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
93
94 11
        $pathStr = parse_url($url, PHP_URL_PATH);
95 11
        if (!is_null($pathStr) && self::PATH_SEPARATOR != $pathStr)
96 11
        {
97 6
            $pathStr = trim($pathStr, self::PATH_SEPARATOR);
98 6
            $this->path = explode(self::PATH_SEPARATOR, $pathStr);
99 6
        }
100
101 11
        $queryStr = parse_url($url, PHP_URL_QUERY);
102 11
        if (!is_null($queryStr))
103 11
        {
104 1
            parse_str($queryStr, $query);
105 1
            $this->query = $query;
0 ignored issues
show
Documentation Bug introduced by
It seems like $query can be null. However, the property $query is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
106 1
        }
107
108 11
        $fragment = parse_url($url, PHP_URL_FRAGMENT);
109 11
        if (!is_null($fragment))
110 11
        {
111 1
            $this->fragment = $fragment;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fragment can also be of type false. However, the property $fragment is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
112 1
        }
113 11
    }
114
115
    /**
116
     * @param string $string
117
     *
118
     * @return $this
119
     * @throws UrlException
120
     */
121 3
    public function addToPath($string)
122
    {
123 3
        if (!is_string($string))
124 3
        {
125 1
            throw new UrlException('Invalid path part! Path part must be an string.');
126
        }
127
128 2
        $string = filter_var($string, FILTER_SANITIZE_ENCODED);
129 2
        if (false === $string)
130 2
        {
131
            throw new UrlException('Invalid URL chunck!');
132
        }
133
134 2
        if ('' == trim($string))
135 2
        {
136 1
            throw new UrlException('Path part can not be empty!');
137
        }
138
139 1
        $this->path[] = $string;
140
141 1
        return $this;
142
    }
143
144
    /**
145
     * @param string $key
146
     * @param string $value
147
     *
148
     * @return $this
149
     * @throws UrlException
150
     */
151 1
    public function addToQuery($key, $value)
152
    {
153 1
        if ('' == trim($key))
154 1
        {
155 1
            throw new UrlException('Query key can not be empty!');
156
        }
157
        $this->query[$key] = $value;
158
159
        return $this;
160
    }
161
162
    /**
163
     * @param $user
164
     * @param $pass
165
     *
166
     * @return $this
167
     */
168
    public function setUserInfo($user, $pass)
169
    {
170
        return $this->setUser($user)->setPass($pass);
171
    }
172
173
    /**
174
     * Reset auth, path, query & fragment
175
     * @return $this
176
     */
177
    public function reset()
178
    {
179
        $this->resetFragment();
180
        $this->resetPath();
181
        $this->resetQuery();
182
        $this->resetUserInfo();
183
184
        return $this;
185
    }
186
187
    /**
188
     * @return $this
189
     */
190
    public function resetFragment()
191
    {
192
        $this->fragment = null;
193
194
        return $this;
195
    }
196
197
    /**
198
     * @return $this
199
     */
200
    public function resetPath()
201
    {
202
        $this->path = [];
203
204
        return $this;
205
    }
206
207
    /**
208
     * @return $this
209
     */
210
    public function resetQuery()
211
    {
212
        $this->query = [];
213
214
        return $this;
215
    }
216
217
    /**
218
     * @return $this
219
     */
220
    public function resetUserInfo()
221
    {
222
        $this->user = null;
223
        $this->pass = null;
224
225
        return $this;
226
    }
227
228
    /**
229
     * @return string
230
     */
231 8
    public function getScheme()
232
    {
233 8
        return $this->scheme;
234
    }
235
236
    /**
237
     * @param string $scheme
238
     *
239
     * @return $this
240
     */
241 1
    public function setScheme($scheme)
242
    {
243 1
        $this->scheme = $scheme;
244
245 1
        return $this;
246
    }
247
248
    /**
249
     * @return string
250
     */
251 8
    public function getUserInfo()
252
    {
253 8
        if (!empty($this->getPass()) && !empty($this->getUser()))
254 8
        {
255 1
            $userInfo = sprintf('%s:%s', $this->getUser(), $this->getPass());
256 1
        }
257 7
        elseif (empty($this->getPass()) && !empty($this->getUser()))
258
        {
259
            $userInfo = $this->getUser();
260
        }
261
        else
262
        {
263 7
            $userInfo = '';
264
        }
265
266 8
        return $userInfo;
267
    }
268
269
    /**
270
     * @return string
271
     */
272 8
    public function getPass()
273
    {
274 8
        return $this->pass;
275
    }
276
277
    /**
278
     * @param string $pass
279
     *
280
     * @return $this
281
     */
282 1
    public function setPass($pass)
283
    {
284 1
        $this->pass = $pass;
285
286 1
        return $this;
287
    }
288
289
    /**
290
     * @return string
291
     */
292 8
    public function getUser()
293
    {
294 8
        return $this->user;
295
    }
296
297
    /**
298
     * @param string $user
299
     *
300
     * @return $this
301
     */
302 1
    public function setUser($user)
303
    {
304 1
        $this->user = $user;
305
306 1
        return $this;
307
    }
308
309
    /**
310
     * @return string
311
     */
312 8
    public function getHost()
313
    {
314 8
        return $this->host;
315
    }
316
317
    /**
318
     * @param string $host
319
     *
320
     * @return $this
321
     */
322 1
    public function setHost($host)
323
    {
324 1
        $this->host = idn_to_utf8($host);
325
326 1
        return $this;
327
    }
328
329
    /**
330
     * @return int
331
     */
332 8
    public function getPort()
333
    {
334 8
        return $this->port;
335
    }
336
337
    /**
338
     * @param int $port
339
     *
340
     * @return $this
341
     */
342 1
    public function setPort($port)
343
    {
344 1
        $this->port = $port;
345
346 1
        return $this;
347
    }
348
349
    /**
350
     * @param bool $asString
351
     *
352
     * @return array|string
353
     */
354 8
    public function getPath($asString = false)
355
    {
356 8
        $path = $this->path;
357 8
        if (true === $asString)
358 8
        {
359 8
            $path = implode(self::PATH_SEPARATOR, $path);
360 8
        }
361
362 8
        return $path;
363
    }
364
365
    /**
366
     * @param array $path
367
     *
368
     * @return $this
369
     */
370 1
    public function setPath(array $path)
371
    {
372 1
        $this->path = $path;
373
374 1
        return $this;
375
    }
376
377
    /**
378
     * @param bool $asString
379
     *
380
     * @return array|string
381
     */
382 8
    public function getQuery($asString = false)
383
    {
384 8
        $query = $this->query;
385 8
        if (true === $asString)
386 8
        {
387 8
            $query = http_build_query($query);
388 8
        }
389
390 8
        return $query;
391
    }
392
393
    /**
394
     * @param array $query Key-value array
395
     *
396
     * @return $this
397
     */
398 1
    public function setQuery(array $query)
399
    {
400 1
        $this->query = $query;
401
402 1
        return $this;
403
    }
404
405
    /**
406
     * @return string
407
     */
408 8
    public function getFragment()
409
    {
410 8
        return $this->fragment;
411
    }
412
413
    /**
414
     * @param string $fragment
415
     *
416
     * @return $this
417
     */
418 1
    public function setFragment($fragment)
419
    {
420 1
        $this->fragment = $fragment;
421
422 1
        return $this;
423
    }
424
425
    /**
426
     * @return string
427
     */
428 8
    protected function composeAuthority()
429
    {
430 8
        $userInfo = $this->getUserInfo();
431 8
        $port = $this->getPort();
432 8
        $host = $this->getHost();
433 8
        $authority = idn_to_ascii($host);
434 8
        if (!empty($userInfo))
435 8
        {
436 1
            $authority = sprintf('%s@%s', $userInfo, $authority);
437 1
        }
438 8
        if (!is_null($port))
439 8
        {
440 1
            $authority = sprintf('%s:%s', $authority, $port);
441 1
        }
442
443 8
        return $authority;
444
    }
445
446
    /**
447
     * @return string
448
     */
449 2
    public function __toString()
450
    {
451
        try
452
        {
453 2
            $url = $this->compose();
454
        }
455 2
        catch (\Exception $e)
456
        {
457 1
            $url = $e->getMessage();
458
        }
459
460 2
        return $url;
461
    }
462
463
    /**
464
     * @return string
465
     * @throws UrlException
466
     */
467 8
    public function compose()
468
    {
469 8
        $url = sprintf('%s://%s', $this->getScheme(), $this->composeAuthority());
470 8
        if (!empty($this->getPath(true)))
471 8
        {
472 4
            $url = sprintf('%s/%s', $url, $this->getPath(true));
473 4
        }
474 8
        if (!empty($this->getQuery(true)))
475 8
        {
476 2
            $url = sprintf('%s?%s', $url, $this->getQuery(true));
477 2
        }
478 8
        if (!empty($this->getFragment()))
479 8
        {
480 2
            $url = sprintf('%s#%s', $url, $this->getFragment());
481 2
        }
482
483 8 View Code Duplication
        if (false === filter_var($url, FILTER_VALIDATE_URL))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
484 8
        {
485 2
            throw new UrlException(sprintf('URL composition error! Please check your data. ' .
486 2
                                           'The composition result is an invalid URL: "%s"',
487 2
                                           $url));
488
        }
489
490 6
        return $url;
491
    }
492
}