1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the Shieldon package. |
4
|
|
|
* |
5
|
|
|
* (c) Terry L. <[email protected]> |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please view the LICENSE |
8
|
|
|
* file that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
declare(strict_types=1); |
12
|
|
|
|
13
|
|
|
namespace Shieldon\Psr7; |
14
|
|
|
|
15
|
|
|
use Psr\Http\Message\UriInterface; |
16
|
|
|
use InvalidArgumentException; |
17
|
|
|
|
18
|
|
|
use function filter_var; |
19
|
|
|
use function gettype; |
20
|
|
|
use function is_integer; |
21
|
|
|
use function is_null; |
22
|
|
|
use function is_string; |
23
|
|
|
use function ltrim; |
24
|
|
|
use function parse_url; |
25
|
|
|
use function rawurlencode; |
26
|
|
|
use function sprintf; |
27
|
|
|
|
28
|
|
|
/* |
29
|
|
|
* Value object representing a URI. |
30
|
|
|
*/ |
31
|
|
|
class Uri implements UriInterface |
32
|
|
|
{ |
33
|
|
|
/** |
34
|
|
|
* foo://example.com:8042/over/there?name=ferret#nose |
35
|
|
|
* \_/ \______________/\_________/ \_________/ \__/ |
36
|
|
|
* | | | | | |
37
|
|
|
* scheme authority path query fragment |
38
|
|
|
*/ |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* The scheme component of the URI. |
42
|
|
|
* For example, https://terryl.in/ |
43
|
|
|
* In this case, "https" is the scheme. |
44
|
|
|
* |
45
|
|
|
* @var string |
46
|
|
|
*/ |
47
|
|
|
protected $scheme; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* The user component of the URI. |
51
|
|
|
* For example, https://jack:[email protected] |
52
|
|
|
* In this case, "jack" is the user. |
53
|
|
|
* |
54
|
|
|
* @var string |
55
|
|
|
*/ |
56
|
|
|
protected $user; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* The password component of the URI. |
60
|
|
|
* For example, http://jack:[email protected] |
61
|
|
|
* In this case, "1234" is the password. |
62
|
|
|
* |
63
|
|
|
* @var string |
64
|
|
|
*/ |
65
|
|
|
protected $pass; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* The host component of the URI. |
69
|
|
|
* For example, https://terryl.in:443/zh/ |
70
|
|
|
* In this case, "terryl.in" is the host. |
71
|
|
|
* |
72
|
|
|
* @var string |
73
|
|
|
*/ |
74
|
|
|
protected $host; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* The port component of the URI. |
78
|
|
|
* For example, https://terryl.in:443 |
79
|
|
|
* In this case, "443" is the port. |
80
|
|
|
* |
81
|
|
|
* @var int|null |
82
|
|
|
*/ |
83
|
|
|
protected $port; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* The path component of the URI. |
87
|
|
|
* For example, https://terryl.in/zh/?paged=2 |
88
|
|
|
* In this case, "/zh/" is the path. |
89
|
|
|
* |
90
|
|
|
* @var string |
91
|
|
|
*/ |
92
|
|
|
protected $path; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* The query component of the URI. |
96
|
|
|
* For example, https://terryl.in/zh/?paged=2 |
97
|
|
|
* In this case, "paged=2" is the query. |
98
|
|
|
* |
99
|
|
|
* @var string |
100
|
|
|
*/ |
101
|
|
|
protected $query; |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* The fragment component of the URI. |
105
|
|
|
* For example, https://terryl.in/#main-container |
106
|
|
|
* In this case, "main-container" is the fragment. |
107
|
|
|
* |
108
|
|
|
* @var string |
109
|
|
|
*/ |
110
|
|
|
protected $fragment; |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Uri constructor. |
114
|
|
|
* |
115
|
|
|
* @param string $uri The URI. |
116
|
|
|
*/ |
117
|
35 |
|
public function __construct($uri = '') |
118
|
|
|
{ |
119
|
35 |
|
$this->assertString($uri, 'uri'); |
120
|
35 |
|
$this->init((array) parse_url($uri)); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* {@inheritdoc} |
125
|
|
|
*/ |
126
|
5 |
|
public function getScheme(): string |
127
|
|
|
{ |
128
|
5 |
|
return $this->scheme; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* {@inheritdoc} |
133
|
|
|
*/ |
134
|
1 |
|
public function getAuthority(): string |
135
|
|
|
{ |
136
|
1 |
|
$authority = ''; |
137
|
|
|
|
138
|
1 |
|
if ($this->getUserInfo()) { |
139
|
1 |
|
$authority .= $this->getUserInfo() . '@'; |
140
|
|
|
} |
141
|
|
|
|
142
|
1 |
|
$authority .= $this->getHost(); |
143
|
|
|
|
144
|
1 |
|
if (!is_null($this->getPort())) { |
145
|
1 |
|
$authority .= ':' . $this->getPort(); |
146
|
|
|
} |
147
|
|
|
|
148
|
1 |
|
return $authority; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* {@inheritdoc} |
153
|
|
|
*/ |
154
|
5 |
|
public function getUserInfo(): string |
155
|
|
|
{ |
156
|
5 |
|
$userInfo = $this->user; |
157
|
|
|
|
158
|
5 |
|
if ($this->pass !== '') { |
159
|
4 |
|
$userInfo .= ':' . $this->pass; |
160
|
|
|
} |
161
|
|
|
|
162
|
5 |
|
return $userInfo; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* {@inheritdoc} |
167
|
|
|
*/ |
168
|
7 |
|
public function getHost(): string |
169
|
|
|
{ |
170
|
7 |
|
return $this->host; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* {@inheritdoc} |
175
|
|
|
*/ |
176
|
6 |
|
public function getPort(): ?int |
177
|
|
|
{ |
178
|
6 |
|
return $this->port; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* {@inheritdoc} |
183
|
|
|
*/ |
184
|
6 |
|
public function getPath(): string |
185
|
|
|
{ |
186
|
6 |
|
return $this->path; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* {@inheritdoc} |
191
|
|
|
*/ |
192
|
6 |
|
public function getQuery(): string |
193
|
|
|
{ |
194
|
6 |
|
return $this->query; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* {@inheritdoc} |
199
|
|
|
*/ |
200
|
5 |
|
public function getFragment(): string |
201
|
|
|
{ |
202
|
5 |
|
return $this->fragment; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* {@inheritdoc} |
207
|
|
|
*/ |
208
|
1 |
|
public function withScheme($scheme): UriInterface |
209
|
|
|
{ |
210
|
1 |
|
$this->assertScheme($scheme); |
211
|
|
|
|
212
|
1 |
|
$scheme = $this->filter('scheme', ['scheme' => $scheme]); |
213
|
|
|
|
214
|
1 |
|
$clone = clone $this; |
215
|
1 |
|
$clone->scheme = $scheme; |
216
|
1 |
|
return $clone; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* {@inheritdoc} |
221
|
|
|
*/ |
222
|
1 |
|
public function withUserInfo($user, $pass = null): UriInterface |
223
|
|
|
{ |
224
|
1 |
|
$this->assertString($user, 'user'); |
225
|
1 |
|
$user = $this->filter('user', ['user' => $user]); |
226
|
|
|
|
227
|
1 |
|
if ($pass) { |
228
|
1 |
|
$this->assertString($pass, 'pass'); |
229
|
1 |
|
$pass = $this->filter('pass', ['pass' => $pass]); |
230
|
|
|
} |
231
|
|
|
|
232
|
1 |
|
$clone = clone $this; |
233
|
1 |
|
$clone->user = $user; |
234
|
1 |
|
$clone->pass = $pass; |
235
|
|
|
|
236
|
1 |
|
return $clone; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* {@inheritdoc} |
241
|
|
|
*/ |
242
|
1 |
|
public function withHost($host): UriInterface |
243
|
|
|
{ |
244
|
1 |
|
$this->assertHost($host); |
245
|
|
|
|
246
|
1 |
|
$host = $this->filter('host', ['host' => $host]); |
247
|
|
|
|
248
|
1 |
|
$clone = clone $this; |
249
|
1 |
|
$clone->host = $host; |
250
|
|
|
|
251
|
1 |
|
return $clone; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* {@inheritdoc} |
256
|
|
|
*/ |
257
|
1 |
|
public function withPort($port): UriInterface |
258
|
|
|
{ |
259
|
1 |
|
$this->assertPort($port); |
260
|
|
|
|
261
|
1 |
|
$port = $this->filter('port', ['port' => $port]); |
262
|
|
|
|
263
|
1 |
|
$clone = clone $this; |
264
|
1 |
|
$clone->port = $port; |
|
|
|
|
265
|
|
|
|
266
|
1 |
|
return $clone; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* {@inheritdoc} |
271
|
|
|
*/ |
272
|
1 |
|
public function withPath($path): UriInterface |
273
|
|
|
{ |
274
|
1 |
|
$this->assertString($path, 'path'); |
275
|
|
|
|
276
|
1 |
|
$path = $this->filter('path', ['path' => $path]); |
277
|
|
|
|
278
|
1 |
|
$clone = clone $this; |
279
|
1 |
|
$clone->path = '/' . rawurlencode(ltrim($path, '/')); |
280
|
|
|
|
281
|
1 |
|
return $clone; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* {@inheritdoc} |
286
|
|
|
*/ |
287
|
1 |
|
public function withQuery($query): UriInterface |
288
|
|
|
{ |
289
|
1 |
|
$this->assertString($query, 'query'); |
290
|
|
|
|
291
|
1 |
|
$query = $this->filter('query', ['query' => $query]); |
292
|
|
|
|
293
|
|
|
// & => %26 |
294
|
|
|
// ? => %3F |
295
|
|
|
|
296
|
1 |
|
$clone = clone $this; |
297
|
1 |
|
$clone->query = $query; |
298
|
|
|
|
299
|
1 |
|
return $clone; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* {@inheritdoc} |
304
|
|
|
*/ |
305
|
1 |
|
public function withFragment($fragment): UriInterface |
306
|
|
|
{ |
307
|
1 |
|
$this->assertString($fragment, 'fragment'); |
308
|
|
|
|
309
|
1 |
|
$fragment = $this->filter('fragment', ['fragment' => $fragment]); |
310
|
|
|
|
311
|
1 |
|
$clone = clone $this; |
312
|
1 |
|
$clone->fragment = $fragment; |
313
|
|
|
|
314
|
1 |
|
return $clone; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* {@inheritdoc} |
319
|
|
|
*/ |
320
|
1 |
|
public function __toString(): string |
321
|
|
|
{ |
322
|
1 |
|
$uri = ''; |
323
|
|
|
|
324
|
|
|
// If a scheme is present, it MUST be suffixed by ":". |
325
|
1 |
|
if ($this->getScheme() !== '') { |
326
|
1 |
|
$uri .= $this->getScheme() . ':'; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
// If an authority is present, it MUST be prefixed by "//". |
330
|
1 |
|
if ($this->getAuthority() !== '') { |
331
|
1 |
|
$uri .= '//' . $this->getAuthority(); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
// If the path is rootless and an authority is present, the path MUST |
335
|
|
|
// be prefixed by "/". |
336
|
1 |
|
$uri .= '/' . ltrim($this->getPath(), '/'); |
337
|
|
|
|
338
|
|
|
// If a query is present, it MUST be prefixed by "?". |
339
|
1 |
|
if ($this->getQuery() !== '') { |
340
|
1 |
|
$uri .= '?' . $this->getQuery(); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
// If a fragment is present, it MUST be prefixed by "#". |
344
|
1 |
|
if ($this->getFragment() !== '') { |
345
|
1 |
|
$uri .= '#' . $this->getFragment(); |
346
|
|
|
} |
347
|
|
|
|
348
|
1 |
|
return $uri; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/* |
352
|
|
|
|-------------------------------------------------------------------------- |
353
|
|
|
| Non PSR-7 Methods. |
354
|
|
|
|-------------------------------------------------------------------------- |
355
|
|
|
*/ |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Initialize. |
359
|
|
|
* |
360
|
|
|
* @param array $data Parsed URL data. |
361
|
|
|
* |
362
|
|
|
* @return void |
363
|
|
|
*/ |
364
|
35 |
|
protected function init(array $data = []): void |
365
|
|
|
{ |
366
|
35 |
|
$components = [ |
367
|
35 |
|
'scheme', |
368
|
35 |
|
'user', |
369
|
35 |
|
'pass', |
370
|
35 |
|
'host', |
371
|
35 |
|
'port', |
372
|
35 |
|
'path', |
373
|
35 |
|
'query', |
374
|
35 |
|
'fragment' |
375
|
35 |
|
]; |
376
|
|
|
|
377
|
35 |
|
foreach ($components as $v) { |
378
|
35 |
|
$this->{$v} = $this->filter($v, $data); |
379
|
|
|
} |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Filter URI components. |
384
|
|
|
* |
385
|
|
|
* Users can provide both encoded and decoded characters. |
386
|
|
|
* Implementations ensure the correct encoding as outlined. |
387
|
|
|
* @see https://tools.ietf.org/html/rfc3986#section-2.2 |
388
|
|
|
* |
389
|
|
|
* @param string $key The part of URI. |
390
|
|
|
* @param array $data Data parsed from a given URL. |
391
|
|
|
* |
392
|
|
|
* @return string|int|null |
393
|
|
|
*/ |
394
|
35 |
|
protected function filter(string $key, $data) |
395
|
|
|
{ |
396
|
35 |
|
$notExists = [ |
397
|
35 |
|
'scheme' => '', |
398
|
35 |
|
'user' => '', |
399
|
35 |
|
'pass' => '', |
400
|
35 |
|
'host' => '', |
401
|
35 |
|
'port' => null, |
402
|
35 |
|
'path' => '', |
403
|
35 |
|
'query' => '', |
404
|
35 |
|
'fragment' => '', |
405
|
35 |
|
]; |
406
|
|
|
|
407
|
35 |
|
if (!isset($data[$key])) { |
408
|
34 |
|
return $notExists[$key]; |
409
|
|
|
} |
410
|
|
|
|
411
|
35 |
|
$value = $data[$key]; |
412
|
|
|
|
413
|
|
|
// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" |
414
|
|
|
// $genDelims = ':/\?#\[\]@'; |
415
|
|
|
|
416
|
|
|
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" |
417
|
|
|
// / "*" / "+" / "," / ";" / "=" |
418
|
35 |
|
$subDelims = '!\$&\'\(\)\*\+,;='; |
419
|
|
|
|
420
|
|
|
// $unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" |
421
|
35 |
|
$unReserved = 'a-zA-Z0-9\-\._~'; |
422
|
|
|
|
423
|
|
|
// Encoded characters, such as "?" encoded to "%3F". |
424
|
35 |
|
$encodePattern = '%(?![A-Fa-f0-9]{2})'; |
425
|
|
|
|
426
|
35 |
|
$regex = ''; |
427
|
|
|
|
428
|
|
|
switch ($key) { |
429
|
35 |
|
case 'host': |
430
|
35 |
|
case 'scheme': |
431
|
21 |
|
return strtolower($value); |
432
|
|
|
break; |
|
|
|
|
433
|
|
|
|
434
|
32 |
|
case 'query': |
435
|
32 |
|
case 'fragment': |
436
|
8 |
|
$specPattern = '%:@\/\?'; |
437
|
8 |
|
$regex = '/(?:[^' . $unReserved . $subDelims . $specPattern . ']+|' . $encodePattern . ')/'; |
438
|
8 |
|
break; |
439
|
|
|
|
440
|
32 |
|
case 'path': |
441
|
31 |
|
$specPattern = '%:@\/'; |
442
|
31 |
|
$regex = '/(?:[^' . $unReserved . $subDelims . $specPattern . ']+|' . $encodePattern . ')/'; |
443
|
31 |
|
break; |
444
|
|
|
|
445
|
11 |
|
case 'user': |
446
|
11 |
|
case 'pass': |
447
|
7 |
|
$regex = '/(?:[^%' . $unReserved . $subDelims . ']+|' . $encodePattern . ')/'; |
448
|
7 |
|
break; |
449
|
|
|
|
450
|
10 |
|
case 'port': |
451
|
10 |
|
if ($this->scheme === 'http' && (int) $value !== 80) { |
452
|
4 |
|
return (int) $value; |
453
|
|
|
} |
454
|
7 |
|
if ($this->scheme === 'https' && (int) $value !== 443) { |
455
|
1 |
|
return (int) $value; |
456
|
|
|
} |
457
|
6 |
|
if ($this->scheme === '') { |
458
|
1 |
|
return (int) $value; |
459
|
|
|
} |
460
|
6 |
|
return null; |
461
|
|
|
|
462
|
|
|
// endswitch |
463
|
|
|
} |
464
|
|
|
|
465
|
31 |
|
if ($regex) { |
466
|
31 |
|
return preg_replace_callback( |
467
|
31 |
|
$regex, |
468
|
31 |
|
function ($match) { |
469
|
1 |
|
return rawurlencode($match[0]); |
470
|
31 |
|
}, |
471
|
31 |
|
$value |
472
|
31 |
|
); |
473
|
|
|
} |
474
|
|
|
|
475
|
|
|
// @codeCoverageIgnoreStart |
476
|
|
|
|
477
|
|
|
return $value; |
478
|
|
|
|
479
|
|
|
// @codeCoverageIgnoreEnd |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* Throw exception for the invalid scheme. |
484
|
|
|
* |
485
|
|
|
* @param string $scheme The scheme string of a URI. |
486
|
|
|
* |
487
|
|
|
* @return void |
488
|
|
|
* |
489
|
|
|
* @throws InvalidArgumentException |
490
|
|
|
*/ |
491
|
2 |
|
protected function assertScheme($scheme): void |
492
|
|
|
{ |
493
|
2 |
|
$this->assertString($scheme, 'scheme'); |
494
|
|
|
|
495
|
2 |
|
$validSchemes = [ |
496
|
2 |
|
0 => '', |
497
|
2 |
|
1 => 'http', |
498
|
2 |
|
2 => 'https', |
499
|
2 |
|
]; |
500
|
|
|
|
501
|
2 |
|
if (!in_array($scheme, $validSchemes)) { |
502
|
1 |
|
throw new InvalidArgumentException( |
503
|
1 |
|
sprintf( |
504
|
1 |
|
'The string "%s" is not a valid scheme.', |
505
|
1 |
|
$scheme |
506
|
1 |
|
) |
507
|
1 |
|
); |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Throw exception for the invalid value. |
513
|
|
|
* |
514
|
|
|
* @param string $value The value to check. |
515
|
|
|
* @param string $name The name of the value. |
516
|
|
|
* |
517
|
|
|
* @return void |
518
|
|
|
* |
519
|
|
|
* @throws InvalidArgumentException |
520
|
|
|
*/ |
521
|
35 |
|
protected function assertString($value, string $name = 'it'): void |
522
|
|
|
{ |
523
|
35 |
|
if (!is_string($value)) { |
|
|
|
|
524
|
1 |
|
throw new InvalidArgumentException( |
525
|
1 |
|
sprintf( |
526
|
1 |
|
ucfirst($name) . ' must be a string, but %s provided.', |
527
|
1 |
|
gettype($value) |
528
|
1 |
|
) |
529
|
1 |
|
); |
530
|
|
|
} |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Throw exception for the invalid host string. |
535
|
|
|
* |
536
|
|
|
* @param string $host The host string to of a URI. |
537
|
|
|
* |
538
|
|
|
* @return void |
539
|
|
|
* |
540
|
|
|
* @throws InvalidArgumentException |
541
|
|
|
*/ |
542
|
3 |
|
protected function assertHost($host): void |
543
|
|
|
{ |
544
|
3 |
|
$this->assertString($host); |
545
|
|
|
|
546
|
3 |
|
if (empty($host)) { |
547
|
|
|
// Note: An empty host value is equivalent to removing the host. |
548
|
|
|
// So that if the host is empty, ignore the following check. |
549
|
1 |
|
return; |
550
|
|
|
} |
551
|
|
|
|
552
|
2 |
|
if (!filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { |
553
|
1 |
|
throw new InvalidArgumentException( |
554
|
1 |
|
sprintf( |
555
|
1 |
|
'"%s" is not a valid host', |
556
|
1 |
|
$host |
557
|
1 |
|
) |
558
|
1 |
|
); |
559
|
|
|
} |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Throw exception for the invalid port. |
564
|
|
|
* |
565
|
|
|
* @param null|int $port The port number to of a URI. |
566
|
|
|
* |
567
|
|
|
* @return void |
568
|
|
|
* |
569
|
|
|
* @throws InvalidArgumentException |
570
|
|
|
*/ |
571
|
3 |
|
protected function assertPort($port): void |
572
|
|
|
{ |
573
|
|
|
if ( |
574
|
3 |
|
!is_null($port) && |
575
|
3 |
|
!is_integer($port) |
|
|
|
|
576
|
|
|
) { |
577
|
1 |
|
throw new InvalidArgumentException( |
578
|
1 |
|
sprintf( |
579
|
1 |
|
'Port must be an integer or a null value, but %s provided.', |
580
|
1 |
|
gettype($port) |
581
|
1 |
|
) |
582
|
1 |
|
); |
583
|
|
|
} |
584
|
|
|
|
585
|
2 |
|
if (!($port > 0 && $port < 65535)) { |
586
|
1 |
|
throw new InvalidArgumentException( |
587
|
1 |
|
sprintf( |
588
|
1 |
|
'Port number should be in a range of 0-65535, but %s provided.', |
589
|
1 |
|
$port |
590
|
1 |
|
) |
591
|
1 |
|
); |
592
|
|
|
} |
593
|
|
|
} |
594
|
|
|
} |
595
|
|
|
|
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 theid
property of an instance of theAccount
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.