1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* League.Uri (http://uri.thephpleague.com) |
4
|
|
|
* |
5
|
|
|
* @package League.uri |
6
|
|
|
* @author Ignace Nyamagana Butera <[email protected]> |
7
|
|
|
* @copyright 2013-2015 Ignace Nyamagana Butera |
8
|
|
|
* @license https://github.com/thephpleague/uri/blob/master/LICENSE (MIT License) |
9
|
|
|
* @version 4.2.0 |
10
|
|
|
* @link https://github.com/thephpleague/uri/ |
11
|
|
|
*/ |
12
|
|
|
namespace League\Uri\Components; |
13
|
|
|
|
14
|
|
|
use InvalidArgumentException; |
15
|
|
|
use League\Uri\Interfaces\Host as HostInterface; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Value object representing a URI host component. |
19
|
|
|
* |
20
|
|
|
* @package League.uri |
21
|
|
|
* @author Ignace Nyamagana Butera <[email protected]> |
22
|
|
|
* @since 1.0.0 |
23
|
|
|
*/ |
24
|
|
|
class Host extends AbstractHierarchicalComponent implements HostInterface |
25
|
|
|
{ |
26
|
|
|
use HostIpTrait; |
27
|
|
|
|
28
|
|
|
use HostnameInfoTrait; |
29
|
|
|
|
30
|
|
|
use HostnameTrait; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* HierarchicalComponent delimiter |
34
|
|
|
* |
35
|
|
|
* @var string |
36
|
|
|
*/ |
37
|
|
|
protected static $separator = '.'; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Host literal representation |
41
|
|
|
* |
42
|
|
|
* @var string |
43
|
|
|
*/ |
44
|
|
|
protected $host; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Return a new instance when needed |
48
|
|
|
* |
49
|
|
|
* @param array $data |
50
|
|
|
* |
51
|
|
|
* @return static |
52
|
|
|
*/ |
53
|
45 |
|
protected function newCollectionInstance(array $data) |
54
|
|
|
{ |
55
|
45 |
|
return $this->createFromLabels($data, $this->isAbsolute); |
|
|
|
|
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* return a new instance from an array or a traversable object |
60
|
|
|
* |
61
|
|
|
* @param \Traversable|string[] $data The segments list |
62
|
|
|
* @param int $type one of the constant IS_ABSOLUTE or IS_RELATIVE |
63
|
|
|
* |
64
|
|
|
* @throws InvalidArgumentException If $type is not a recognized constant |
65
|
|
|
* |
66
|
|
|
* @return static |
67
|
|
|
*/ |
68
|
165 |
|
public static function createFromLabels($data, $type = self::IS_RELATIVE) |
69
|
|
|
{ |
70
|
165 |
|
static $type_list = [self::IS_ABSOLUTE => 1, self::IS_RELATIVE => 1]; |
71
|
|
|
|
72
|
165 |
|
if (!isset($type_list[$type])) { |
73
|
3 |
|
throw new InvalidArgumentException('Please verify the submitted constant'); |
74
|
|
|
} |
75
|
|
|
|
76
|
162 |
|
if (empty($data)) { |
77
|
12 |
|
return new static(); |
78
|
|
|
} |
79
|
|
|
|
80
|
150 |
|
if ([''] === $data) { |
81
|
3 |
|
return new static(''); |
82
|
|
|
} |
83
|
|
|
|
84
|
147 |
|
return new static(static::format($data, $type)); |
|
|
|
|
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release |
89
|
|
|
* |
90
|
|
|
* @deprecated deprecated since version 4.2 |
91
|
|
|
* |
92
|
|
|
* return a new instance from an array or a traversable object |
93
|
|
|
* |
94
|
|
|
* @param \Traversable|string[] $data The segments list |
95
|
|
|
* @param int $type one of the constant IS_ABSOLUTE or IS_RELATIVE |
96
|
|
|
* |
97
|
|
|
* @throws InvalidArgumentException If $type is not a recognized constant |
98
|
|
|
* |
99
|
|
|
* @return static |
100
|
|
|
*/ |
101
|
|
|
public static function createFromArray($data, $type = self::IS_RELATIVE) |
102
|
|
|
{ |
103
|
|
|
return self::createFromLabels($data, $type); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* New instance |
108
|
|
|
* |
109
|
|
|
* @param null|string $host |
110
|
|
|
*/ |
111
|
1141 |
|
public function __construct($host = null) |
112
|
|
|
{ |
113
|
1141 |
|
$this->data = $this->validate($host); |
114
|
1075 |
|
$this->setLiteral(); |
115
|
1075 |
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Returns whether or not the host is an IDN |
119
|
|
|
* |
120
|
|
|
* @return bool |
121
|
|
|
*/ |
122
|
9 |
|
public function isIdn() |
123
|
|
|
{ |
124
|
9 |
|
return $this->isIdn; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Returns whether or not the host is an IP address |
129
|
|
|
* |
130
|
|
|
* @return bool |
131
|
|
|
*/ |
132
|
1075 |
|
public function isIp() |
133
|
|
|
{ |
134
|
1075 |
|
return $this->hostAsIpv4 || $this->hostAsIpv6; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Returns whether or not the host is an IPv4 address |
139
|
|
|
* |
140
|
|
|
* @return bool |
141
|
|
|
*/ |
142
|
39 |
|
public function isIpv4() |
143
|
|
|
{ |
144
|
39 |
|
return $this->hostAsIpv4; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Returns whether or not the host is an IPv6 address |
149
|
|
|
* |
150
|
|
|
* @return bool |
151
|
|
|
*/ |
152
|
39 |
|
public function isIpv6() |
153
|
|
|
{ |
154
|
39 |
|
return $this->hostAsIpv6; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Returns whether or not the host has a ZoneIdentifier |
159
|
|
|
* |
160
|
|
|
* @return bool |
161
|
|
|
* |
162
|
|
|
* @see http://tools.ietf.org/html/rfc6874#section-4 |
163
|
|
|
*/ |
164
|
12 |
|
public function hasZoneIdentifier() |
165
|
|
|
{ |
166
|
12 |
|
return $this->hasZoneIdentifier; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Host literal setter |
171
|
|
|
*/ |
172
|
1075 |
|
protected function setLiteral() |
173
|
|
|
{ |
174
|
1075 |
|
$this->host = !$this->isIp() ? $this->__toString() : $this->data[0]; |
175
|
1075 |
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release |
179
|
|
|
* |
180
|
|
|
* @deprecated deprecated since version 4.2 |
181
|
|
|
* |
182
|
|
|
* Returns the instance literal representation |
183
|
|
|
* without encoding |
184
|
|
|
* |
185
|
|
|
* @return string |
186
|
|
|
*/ |
187
|
|
|
public function getLiteral() |
188
|
|
|
{ |
189
|
|
|
return $this->host; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* validate the submitted data |
194
|
|
|
* |
195
|
|
|
* @param string $str |
196
|
|
|
* |
197
|
|
|
* @return array |
198
|
|
|
*/ |
199
|
1141 |
|
protected function validate($str) |
200
|
|
|
{ |
201
|
1141 |
|
if (null === $str) { |
202
|
354 |
|
return []; |
203
|
|
|
} |
204
|
|
|
|
205
|
1021 |
|
$str = $this->validateString($str); |
206
|
1012 |
|
if ('' === $str) { |
207
|
24 |
|
return ['']; |
208
|
|
|
} |
209
|
|
|
|
210
|
1003 |
|
$res = $this->validateIpHost($str); |
211
|
1003 |
|
if (!empty($res)) { |
212
|
120 |
|
return $res; |
213
|
|
|
} |
214
|
|
|
|
215
|
904 |
|
return $this->validateStringHost($str); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Retrieves a single host label. |
220
|
|
|
* |
221
|
|
|
* Retrieves a single host label. If the label offset has not been set, |
222
|
|
|
* returns the default value provided. |
223
|
|
|
* |
224
|
|
|
* @param string $offset the label offset |
225
|
|
|
* @param mixed $default Default value to return if the offset does not exist. |
226
|
|
|
* |
227
|
|
|
* @return mixed |
228
|
|
|
*/ |
229
|
3 |
|
public function getLabel($offset, $default = null) |
230
|
|
|
{ |
231
|
3 |
|
if (isset($this->data[$offset])) { |
232
|
3 |
|
return $this->isIdn ? $this->data[$offset] : idn_to_ascii($this->data[$offset]); |
233
|
|
|
} |
234
|
|
|
|
235
|
3 |
|
return $default; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Returns an array representation of the host |
240
|
|
|
* |
241
|
|
|
* @return array |
242
|
|
|
*/ |
243
|
868 |
|
public function toArray() |
244
|
|
|
{ |
245
|
868 |
|
return $this->convertToAscii($this->data, !$this->isIdn); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @inheritdoc |
250
|
|
|
*/ |
251
|
1051 |
|
public function getContent() |
252
|
|
|
{ |
253
|
1051 |
|
if ([] === $this->data) { |
254
|
369 |
|
return null; |
255
|
|
|
} |
256
|
|
|
|
257
|
925 |
|
if ($this->isIp()) { |
258
|
75 |
|
return $this->formatIp($this->data[0]); |
259
|
|
|
} |
260
|
|
|
|
261
|
862 |
|
return $this->format($this->toArray(), $this->isAbsolute); |
|
|
|
|
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @inheritdoc |
266
|
|
|
*/ |
267
|
2 |
|
public function __debugInfo() |
268
|
|
|
{ |
269
|
2 |
|
return ['host' => $this->getContent()]; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @inheritdoc |
274
|
|
|
*/ |
275
|
15 |
|
public static function __set_state(array $properties) |
276
|
|
|
{ |
277
|
15 |
|
$host = static::createFromLabels($properties['data'], $properties['isAbsolute']); |
278
|
15 |
|
$host->hostnameInfoLoaded = $properties['hostnameInfoLoaded']; |
279
|
15 |
|
$host->hostnameInfo = $properties['hostnameInfo']; |
280
|
|
|
|
281
|
15 |
|
return $host; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Returns a host in his punycode encoded form |
286
|
|
|
* |
287
|
|
|
* This method MUST retain the state of the current instance, and return |
288
|
|
|
* an instance with the host transcoded using to ascii the RFC 3492 rules |
289
|
|
|
* |
290
|
|
|
* @see http://tools.ietf.org/html/rfc3492 |
291
|
|
|
* |
292
|
|
|
* @return static |
293
|
|
|
*/ |
294
|
108 |
|
public function toAscii() |
295
|
|
|
{ |
296
|
108 |
|
if ($this->isIp() || !$this->isIdn) { |
297
|
36 |
|
return $this; |
298
|
|
|
} |
299
|
|
|
|
300
|
75 |
|
return $this->modify($this->format( |
301
|
75 |
|
$this->convertToAscii($this->data, $this->isIdn), |
302
|
75 |
|
$this->isAbsolute |
|
|
|
|
303
|
50 |
|
)); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Returns a host in his IDN form |
308
|
|
|
* |
309
|
|
|
* This method MUST retain the state of the current instance, and return |
310
|
|
|
* an instance with the host in its IDN form using RFC 3492 rules |
311
|
|
|
* |
312
|
|
|
* @see http://tools.ietf.org/html/rfc3492 |
313
|
|
|
* |
314
|
|
|
* @return static |
315
|
|
|
*/ |
316
|
81 |
|
public function toUnicode() |
317
|
|
|
{ |
318
|
81 |
|
if ($this->isIp() || $this->isIdn) { |
319
|
69 |
|
return $this; |
320
|
|
|
} |
321
|
|
|
|
322
|
72 |
|
return $this->modify($this->format($this->data, $this->isAbsolute)); |
|
|
|
|
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Return a formatted host string |
327
|
|
|
* |
328
|
|
|
* @param \Traversable|string[] $data The segments list |
329
|
|
|
* @param bool $type |
330
|
|
|
* |
331
|
|
|
* @throws InvalidArgumentException If $data is invalid |
332
|
|
|
* |
333
|
|
|
* @return string |
334
|
|
|
*/ |
335
|
889 |
|
protected static function format($data, $type) |
336
|
|
|
{ |
337
|
889 |
|
$hostname = implode(static::$separator, array_reverse(static::validateIterator($data))); |
338
|
877 |
|
if (self::IS_ABSOLUTE == $type) { |
339
|
36 |
|
return $hostname.static::$separator; |
340
|
|
|
} |
341
|
|
|
|
342
|
853 |
|
return $hostname; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Return an host without its zone identifier according to RFC6874 |
347
|
|
|
* |
348
|
|
|
* This method MUST retain the state of the current instance, and return |
349
|
|
|
* an instance without the host zone identifier according to RFC6874 |
350
|
|
|
* |
351
|
|
|
* @see http://tools.ietf.org/html/rfc6874#section-4 |
352
|
|
|
* |
353
|
|
|
* @return static |
354
|
|
|
*/ |
355
|
18 |
|
public function withoutZoneIdentifier() |
356
|
|
|
{ |
357
|
18 |
|
if ($this->hasZoneIdentifier) { |
358
|
9 |
|
return $this->modify(substr($this->data[0], 0, strpos($this->data[0], '%'))); |
359
|
|
|
} |
360
|
|
|
|
361
|
9 |
|
return $this; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Validated the Host Label Count |
366
|
|
|
* |
367
|
|
|
* @param array $labels Host labels |
368
|
|
|
* |
369
|
|
|
* @throws InvalidArgumentException If the validation fails |
370
|
|
|
*/ |
371
|
889 |
|
protected function assertLabelsCount(array $labels) |
372
|
|
|
{ |
373
|
889 |
|
if (127 <= count(array_merge($this->data, $labels))) { |
374
|
3 |
|
throw new InvalidArgumentException('Invalid Hostname, verify labels count'); |
375
|
|
|
} |
376
|
886 |
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* set the FQDN property |
380
|
|
|
* |
381
|
|
|
* @param string $str |
382
|
|
|
* |
383
|
|
|
* @return string |
384
|
|
|
*/ |
385
|
904 |
|
protected function setIsAbsolute($str) |
386
|
|
|
{ |
387
|
904 |
|
$this->isAbsolute = self::IS_RELATIVE; |
388
|
904 |
|
if ('.' == mb_substr($str, -1, 1, 'UTF-8')) { |
389
|
42 |
|
$this->isAbsolute = self::IS_ABSOLUTE; |
390
|
42 |
|
$str = mb_substr($str, 0, -1, 'UTF-8'); |
391
|
28 |
|
} |
392
|
|
|
|
393
|
904 |
|
return $str; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* @inheritdoc |
398
|
|
|
*/ |
399
|
30 |
|
public function prepend($component) |
400
|
|
|
{ |
401
|
30 |
|
return $this->createFromLabels( |
402
|
30 |
|
$this->validateComponent($component), |
403
|
30 |
|
$this->isAbsolute |
404
|
30 |
|
)->append($this); |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* @inheritdoc |
409
|
|
|
*/ |
410
|
60 |
|
public function append($component) |
411
|
|
|
{ |
412
|
60 |
|
return $this->createFromLabels(array_merge( |
413
|
60 |
|
$this->validateComponent($component)->toArray(), |
414
|
60 |
|
$this->toArray() |
415
|
60 |
|
), $this->isAbsolute); |
416
|
|
|
} |
417
|
|
|
} |
418
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.