1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace IPLib\Address; |
4
|
|
|
|
5
|
|
|
use IPLib\Range\RangeInterface; |
6
|
|
|
use IPLib\Range\Subnet; |
7
|
|
|
use IPLib\Range\Type as RangeType; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* An IPv6 address. |
11
|
|
|
*/ |
12
|
|
|
class IPv6 implements AddressInterface |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* The long string representation of the address. |
16
|
|
|
* |
17
|
|
|
* @var string |
18
|
|
|
* |
19
|
|
|
* @example '0000:0000:0000:0000:0000:0000:0000:0001' |
20
|
|
|
*/ |
21
|
|
|
protected $longAddress; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* The long string representation of the address. |
25
|
|
|
* |
26
|
|
|
* @var string|null |
27
|
|
|
* |
28
|
|
|
* @example '::1' |
29
|
|
|
*/ |
30
|
|
|
protected $shortAddress; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* The byte list of the IP address. |
34
|
|
|
* |
35
|
|
|
* @var int[]|null |
36
|
|
|
*/ |
37
|
|
|
protected $bytes; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* The word list of the IP address. |
41
|
|
|
* |
42
|
|
|
* @var int[]|null |
43
|
|
|
*/ |
44
|
|
|
protected $words; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The type of the range of this IP address. |
48
|
|
|
* |
49
|
|
|
* @var int|null |
50
|
|
|
*/ |
51
|
|
|
protected $rangeType; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* An array containing RFC designated address ranges. |
55
|
|
|
* |
56
|
|
|
* @var array|null |
57
|
|
|
*/ |
58
|
|
|
private static $reservedRanges = null; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Initializes the instance. |
62
|
|
|
* |
63
|
|
|
* @param string $longAddress |
64
|
|
|
*/ |
65
|
494 |
|
public function __construct($longAddress) |
66
|
|
|
{ |
67
|
494 |
|
$this->longAddress = $longAddress; |
68
|
494 |
|
$this->shortAddress = null; |
69
|
494 |
|
$this->bytes = null; |
70
|
494 |
|
$this->words = null; |
71
|
494 |
|
$this->rangeType = null; |
72
|
494 |
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* {@inheritdoc} |
76
|
|
|
* |
77
|
|
|
* @see \IPLib\Address\AddressInterface::__toString() |
78
|
|
|
*/ |
79
|
24 |
|
public function __toString() |
80
|
|
|
{ |
81
|
24 |
|
return $this->toString(); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* {@inheritdoc} |
86
|
|
|
* |
87
|
|
|
* @see \IPLib\Address\AddressInterface::getNumberOfBits() |
88
|
|
|
*/ |
89
|
21 |
|
public static function getNumberOfBits() |
90
|
|
|
{ |
91
|
21 |
|
return 128; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Parse a string and returns an IPv6 instance if the string is valid, or null otherwise. |
96
|
|
|
* |
97
|
|
|
* @param string|mixed $address the address to parse |
98
|
|
|
* @param bool $mayIncludePort set to false to avoid parsing addresses with ports |
99
|
|
|
* @param bool $mayIncludeZoneID set to false to avoid parsing addresses with zone IDs (see RFC 4007) |
100
|
|
|
* |
101
|
|
|
* @return static|null |
102
|
|
|
*/ |
103
|
548 |
|
public static function fromString($address, $mayIncludePort = true, $mayIncludeZoneID = true) |
104
|
|
|
{ |
105
|
548 |
|
$result = null; |
106
|
548 |
|
if (is_string($address) && strpos($address, ':') !== false && strpos($address, ':::') === false) { |
107
|
507 |
|
$matches = null; |
108
|
507 |
|
if ($mayIncludePort && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) { |
109
|
2 |
|
$address = $matches[1]; |
110
|
|
|
} |
111
|
507 |
|
if ($mayIncludeZoneID) { |
112
|
507 |
|
$percentagePos = strpos($address, '%'); |
113
|
507 |
|
if ($percentagePos > 0) { |
114
|
4 |
|
$address = substr($address, 0, $percentagePos); |
115
|
|
|
} |
116
|
|
|
} |
117
|
507 |
|
if (preg_match('/^((?:[0-9a-f]*:+)+)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $address, $matches)) { |
118
|
9 |
|
$address6 = static::fromString($matches[1] . '0:0', false); |
119
|
9 |
|
if ($address6 !== null) { |
120
|
6 |
|
$address4 = IPv4::fromString($matches[2], false); |
121
|
6 |
|
if ($address4 !== null) { |
122
|
6 |
|
$bytes4 = $address4->getBytes(); |
123
|
6 |
|
$address6->longAddress = substr($address6->longAddress, 0, -9) . sprintf('%02x%02x:%02x%02x', $bytes4[0], $bytes4[1], $bytes4[2], $bytes4[3]); |
124
|
9 |
|
$result = $address6; |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
} else { |
128
|
507 |
|
if (strpos($address, '::') === false) { |
129
|
385 |
|
$chunks = explode(':', $address); |
130
|
|
|
} else { |
131
|
136 |
|
$chunks = array(); |
132
|
136 |
|
$parts = explode('::', $address); |
133
|
136 |
|
if (count($parts) === 2) { |
134
|
134 |
|
$before = ($parts[0] === '') ? array() : explode(':', $parts[0]); |
135
|
134 |
|
$after = ($parts[1] === '') ? array() : explode(':', $parts[1]); |
136
|
134 |
|
$missing = 8 - count($before) - count($after); |
137
|
134 |
|
if ($missing >= 0) { |
138
|
132 |
|
$chunks = $before; |
139
|
132 |
|
if ($missing !== 0) { |
140
|
131 |
|
$chunks = array_merge($chunks, array_fill(0, $missing, '0')); |
141
|
|
|
} |
142
|
132 |
|
$chunks = array_merge($chunks, $after); |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
} |
146
|
507 |
|
if (count($chunks) === 8) { |
147
|
498 |
|
$nums = array_map( |
148
|
498 |
|
function ($chunk) { |
149
|
498 |
|
return preg_match('/^[0-9A-Fa-f]{1,4}$/', $chunk) ? hexdec($chunk) : false; |
150
|
498 |
|
}, |
151
|
498 |
|
$chunks |
152
|
|
|
); |
153
|
498 |
|
if (!in_array(false, $nums, true)) { |
154
|
490 |
|
$longAddress = implode( |
155
|
490 |
|
':', |
156
|
490 |
|
array_map( |
157
|
490 |
|
function ($num) { |
158
|
490 |
|
return sprintf('%04x', $num); |
159
|
490 |
|
}, |
160
|
490 |
|
$nums |
161
|
|
|
) |
162
|
|
|
); |
163
|
490 |
|
$result = new static($longAddress); |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
|
169
|
548 |
|
return $result; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Parse an array of bytes and returns an IPv6 instance if the array is valid, or null otherwise. |
174
|
|
|
* |
175
|
|
|
* @param int[]|array $bytes |
176
|
|
|
* |
177
|
|
|
* @return static|null |
178
|
|
|
*/ |
179
|
319 |
|
public static function fromBytes(array $bytes) |
180
|
|
|
{ |
181
|
319 |
|
$result = null; |
182
|
319 |
|
if (count($bytes) === 16) { |
183
|
299 |
|
$address = ''; |
184
|
299 |
|
for ($i = 0; $i < 16; $i++) { |
185
|
299 |
|
if ($i !== 0 && $i % 2 === 0) { |
186
|
299 |
|
$address .= ':'; |
187
|
|
|
} |
188
|
299 |
|
$byte = $bytes[$i]; |
189
|
299 |
|
if (is_int($byte) && $byte >= 0 && $byte <= 255) { |
190
|
299 |
|
$address .= sprintf('%02x', $byte); |
191
|
|
|
} else { |
192
|
|
|
$address = null; |
193
|
|
|
break; |
194
|
|
|
} |
195
|
|
|
} |
196
|
299 |
|
if ($address !== null) { |
197
|
299 |
|
$result = new static($address); |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
|
201
|
319 |
|
return $result; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Parse an array of words and returns an IPv6 instance if the array is valid, or null otherwise. |
206
|
|
|
* |
207
|
|
|
* @param int[]|array $words |
208
|
|
|
* |
209
|
|
|
* @return static|null |
210
|
|
|
*/ |
211
|
56 |
|
public static function fromWords(array $words) |
212
|
|
|
{ |
213
|
56 |
|
$result = null; |
214
|
56 |
|
if (count($words) === 8) { |
215
|
36 |
|
$chunks = array(); |
216
|
36 |
|
for ($i = 0; $i < 8; $i++) { |
217
|
36 |
|
$word = $words[$i]; |
218
|
36 |
|
if (is_int($word) && $word >= 0 && $word <= 0xffff) { |
219
|
36 |
|
$chunks[] = sprintf('%04x', $word); |
220
|
|
|
} else { |
221
|
|
|
$chunks = null; |
222
|
|
|
break; |
223
|
|
|
} |
224
|
|
|
} |
225
|
36 |
|
if ($chunks !== null) { |
226
|
36 |
|
$result = new static(implode(':', $chunks)); |
227
|
|
|
} |
228
|
|
|
} |
229
|
|
|
|
230
|
56 |
|
return $result; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* {@inheritdoc} |
235
|
|
|
* |
236
|
|
|
* @see \IPLib\Address\AddressInterface::toString() |
237
|
|
|
*/ |
238
|
330 |
|
public function toString($long = false) |
239
|
|
|
{ |
240
|
330 |
|
if ($long) { |
241
|
55 |
|
$result = $this->longAddress; |
242
|
|
|
} else { |
243
|
307 |
|
if ($this->shortAddress === null) { |
244
|
307 |
|
if (strpos($this->longAddress, '0000:0000:0000:0000:0000:ffff:') === 0) { |
245
|
7 |
|
$lastBytes = array_slice($this->getBytes(), -4); |
246
|
7 |
|
$this->shortAddress = '::ffff:' . implode('.', $lastBytes); |
247
|
|
|
} else { |
248
|
300 |
|
$chunks = array_map( |
249
|
300 |
|
function ($word) { |
250
|
300 |
|
return dechex($word); |
251
|
300 |
|
}, |
252
|
300 |
|
$this->getWords() |
253
|
|
|
); |
254
|
300 |
|
$shortAddress = implode(':', $chunks); |
255
|
300 |
|
$matches = null; |
256
|
300 |
|
for ($i = 8; $i > 1; $i--) { |
257
|
300 |
|
$search = '(?:^|:)' . rtrim(str_repeat('0:', $i), ':') . '(?:$|:)'; |
258
|
300 |
|
if (preg_match('/^(.*?)' . $search . '(.*)$/', $shortAddress, $matches)) { |
259
|
242 |
|
$shortAddress = $matches[1] . '::' . $matches[2]; |
260
|
242 |
|
break; |
261
|
|
|
} |
262
|
|
|
} |
263
|
300 |
|
$this->shortAddress = $shortAddress; |
264
|
|
|
} |
265
|
|
|
} |
266
|
307 |
|
$result = $this->shortAddress; |
267
|
|
|
} |
268
|
|
|
|
269
|
330 |
|
return $result; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* {@inheritdoc} |
274
|
|
|
* |
275
|
|
|
* @see \IPLib\Address\AddressInterface::getBytes() |
276
|
|
|
*/ |
277
|
327 |
|
public function getBytes() |
278
|
|
|
{ |
279
|
327 |
|
if ($this->bytes === null) { |
280
|
327 |
|
$bytes = array(); |
281
|
327 |
|
foreach ($this->getWords() as $word) { |
282
|
327 |
|
$bytes[] = $word >> 8; |
283
|
327 |
|
$bytes[] = $word & 0xff; |
284
|
|
|
} |
285
|
327 |
|
$this->bytes = $bytes; |
286
|
|
|
} |
287
|
|
|
|
288
|
327 |
|
return $this->bytes; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* {@inheritdoc} |
293
|
|
|
* |
294
|
|
|
* @see \IPLib\Address\AddressInterface::getBits() |
295
|
|
|
*/ |
296
|
31 |
View Code Duplication |
public function getBits() |
|
|
|
|
297
|
|
|
{ |
298
|
31 |
|
$parts = array(); |
299
|
31 |
|
foreach ($this->getBytes() as $byte) { |
300
|
31 |
|
$parts[] = sprintf('%08b', $byte); |
301
|
|
|
} |
302
|
|
|
|
303
|
31 |
|
return implode('', $parts); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Get the word list of the IP address. |
308
|
|
|
* |
309
|
|
|
* @return int[] |
310
|
|
|
*/ |
311
|
476 |
|
public function getWords() |
312
|
|
|
{ |
313
|
476 |
|
if ($this->words === null) { |
314
|
476 |
|
$this->words = array_map( |
315
|
476 |
|
function ($chunk) { |
316
|
476 |
|
return hexdec($chunk); |
317
|
476 |
|
}, |
318
|
476 |
|
explode(':', $this->longAddress) |
319
|
|
|
); |
320
|
|
|
} |
321
|
|
|
|
322
|
476 |
|
return $this->words; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* {@inheritdoc} |
327
|
|
|
* |
328
|
|
|
* @see \IPLib\Address\AddressInterface::getAddressType() |
329
|
|
|
*/ |
330
|
273 |
|
public function getAddressType() |
331
|
|
|
{ |
332
|
273 |
|
return Type::T_IPv6; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* {@inheritdoc} |
337
|
|
|
* |
338
|
|
|
* @see \IPLib\Address\AddressInterface::getDefaultReservedRangeType() |
339
|
|
|
*/ |
340
|
100 |
|
public static function getDefaultReservedRangeType() |
341
|
|
|
{ |
342
|
100 |
|
return RangeType::T_RESERVED; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* {@inheritdoc} |
347
|
|
|
* |
348
|
|
|
* @see \IPLib\Address\AddressInterface::getReservedRanges() |
349
|
|
|
*/ |
350
|
116 |
|
public static function getReservedRanges() |
351
|
|
|
{ |
352
|
116 |
|
if (self::$reservedRanges === null) { |
353
|
1 |
|
$reservedRanges = array(); |
354
|
|
|
foreach (array( |
355
|
|
|
// RFC 4291 |
356
|
1 |
|
'::/128' => array(RangeType::T_UNSPECIFIED), |
357
|
|
|
// RFC 4291 |
358
|
|
|
'::1/128' => array(RangeType::T_LOOPBACK), |
359
|
|
|
// RFC 4291 |
360
|
|
|
'100::/8' => array(RangeType::T_DISCARD, array('100::/64' => RangeType::T_DISCARDONLY)), |
361
|
|
|
//'2002::/16' => array(RangeType::), |
362
|
|
|
// RFC 4291 |
363
|
|
|
'2000::/3' => array(RangeType::T_PUBLIC), |
364
|
|
|
// RFC 4193 |
365
|
|
|
'fc00::/7' => array(RangeType::T_PRIVATENETWORK), |
366
|
|
|
// RFC 4291 |
367
|
|
|
'fe80::/10' => array(RangeType::T_LINKLOCAL_UNICAST), |
368
|
|
|
// RFC 4291 |
369
|
|
|
'ff00::/8' => array(RangeType::T_MULTICAST), |
370
|
|
|
// RFC 4291 |
371
|
|
|
//'::/8' => array(RangeType::T_RESERVED), |
372
|
|
|
// RFC 4048 |
373
|
|
|
//'200::/7' => array(RangeType::T_RESERVED), |
374
|
|
|
// RFC 4291 |
375
|
|
|
//'400::/6' => array(RangeType::T_RESERVED), |
376
|
|
|
// RFC 4291 |
377
|
|
|
//'800::/5' => array(RangeType::T_RESERVED), |
378
|
|
|
// RFC 4291 |
379
|
|
|
//'1000::/4' => array(RangeType::T_RESERVED), |
380
|
|
|
// RFC 4291 |
381
|
|
|
//'4000::/3' => array(RangeType::T_RESERVED), |
382
|
|
|
// RFC 4291 |
383
|
|
|
//'6000::/3' => array(RangeType::T_RESERVED), |
384
|
|
|
// RFC 4291 |
385
|
|
|
//'8000::/3' => array(RangeType::T_RESERVED), |
386
|
|
|
// RFC 4291 |
387
|
|
|
//'a000::/3' => array(RangeType::T_RESERVED), |
388
|
|
|
// RFC 4291 |
389
|
|
|
//'c000::/3' => array(RangeType::T_RESERVED), |
390
|
|
|
// RFC 4291 |
391
|
|
|
//'e000::/4' => array(RangeType::T_RESERVED), |
392
|
|
|
// RFC 4291 |
393
|
|
|
//'f000::/5' => array(RangeType::T_RESERVED), |
394
|
|
|
// RFC 4291 |
395
|
|
|
//'f800::/6' => array(RangeType::T_RESERVED), |
396
|
|
|
// RFC 4291 |
397
|
|
|
//'fe00::/9' => array(RangeType::T_RESERVED), |
398
|
|
|
// RFC 3879 |
399
|
|
|
//'fec0::/10' => array(RangeType::T_RESERVED), |
400
|
|
|
) as $range => $data) { |
401
|
1 |
|
$exceptions = array(); |
402
|
1 |
View Code Duplication |
if (isset($data[1])) { |
|
|
|
|
403
|
1 |
|
foreach ($data[1] as $exceptionRange => $exceptionType) { |
404
|
1 |
|
$exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType); |
405
|
|
|
} |
406
|
|
|
} |
407
|
1 |
|
$reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions); |
408
|
|
|
} |
409
|
1 |
|
self::$reservedRanges = $reservedRanges; |
410
|
|
|
} |
411
|
|
|
|
412
|
116 |
|
return self::$reservedRanges; |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* {@inheritdoc} |
417
|
|
|
* |
418
|
|
|
* @see \IPLib\Address\AddressInterface::getRangeType() |
419
|
|
|
*/ |
420
|
59 |
|
public function getRangeType() |
421
|
|
|
{ |
422
|
59 |
|
if ($this->rangeType === null) { |
423
|
59 |
|
$ipv4 = $this->toIPv4(); |
424
|
59 |
View Code Duplication |
if ($ipv4 !== null) { |
|
|
|
|
425
|
6 |
|
$this->rangeType = $ipv4->getRangeType(); |
426
|
|
|
} else { |
427
|
53 |
|
$rangeType = null; |
428
|
53 |
|
foreach (static::getReservedRanges() as $reservedRange) { |
429
|
53 |
|
$rangeType = $reservedRange->getAddressType($this); |
430
|
53 |
|
if ($rangeType !== null) { |
431
|
53 |
|
break; |
432
|
|
|
} |
433
|
|
|
} |
434
|
53 |
|
$this->rangeType = $rangeType === null ? static::getDefaultReservedRangeType() : $rangeType; |
435
|
|
|
} |
436
|
|
|
} |
437
|
|
|
|
438
|
59 |
|
return $this->rangeType; |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* Create an IPv4 representation of this address (if possible, otherwise returns null). |
443
|
|
|
* |
444
|
|
|
* @return \IPLib\Address\IPv4|null |
445
|
|
|
*/ |
446
|
76 |
|
public function toIPv4() |
447
|
|
|
{ |
448
|
76 |
View Code Duplication |
if (strpos($this->longAddress, '2002:') === 0) { |
|
|
|
|
449
|
|
|
// 6to4 |
450
|
19 |
|
return IPv4::fromBytes(array_slice($this->getBytes(), 2, 4)); |
451
|
|
|
} |
452
|
57 |
View Code Duplication |
if (strpos($this->longAddress, '0000:0000:0000:0000:0000:ffff:') === 0) { |
|
|
|
|
453
|
|
|
// IPv4-mapped IPv6 addresses |
454
|
4 |
|
return IPv4::fromBytes(array_slice($this->getBytes(), -4)); |
455
|
|
|
} |
456
|
|
|
|
457
|
53 |
|
return null; |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Render this IPv6 address in the "mixed" IPv6 (first 12 bytes) + IPv4 (last 4 bytes) mixed syntax. |
462
|
|
|
* |
463
|
|
|
* @param bool $ipV6Long render the IPv6 part in "long" format? |
464
|
|
|
* @param bool $ipV4Long render the IPv4 part in "long" format? |
465
|
|
|
* |
466
|
|
|
* @return string |
467
|
|
|
* |
468
|
|
|
* @example '::13.1.68.3' |
469
|
|
|
* @example '0000:0000:0000:0000:0000:0000:13.1.68.3' when $ipV6Long is true |
470
|
|
|
* @example '::013.001.068.003' when $ipV4Long is true |
471
|
|
|
* @example '0000:0000:0000:0000:0000:0000:013.001.068.003' when $ipV6Long and $ipV4Long are true |
472
|
|
|
* |
473
|
|
|
* @see https://tools.ietf.org/html/rfc4291#section-2.2 point 3. |
474
|
|
|
*/ |
475
|
6 |
|
public function toMixedIPv6IPv4String($ipV6Long = false, $ipV4Long = false) |
476
|
|
|
{ |
477
|
6 |
|
$myBytes = $this->getBytes(); |
478
|
6 |
|
$ipv6Bytes = array_merge(array_slice($myBytes, 0, 12), array(0xff, 0xff, 0xff, 0xff)); |
479
|
6 |
|
$ipv6String = static::fromBytes($ipv6Bytes)->toString($ipV6Long); |
480
|
6 |
|
$ipv4Bytes = array_slice($myBytes, 12, 4); |
481
|
6 |
|
$ipv4String = IPv4::fromBytes($ipv4Bytes)->toString($ipV4Long); |
482
|
|
|
|
483
|
6 |
|
return preg_replace('/((ffff:ffff)|(\d+(\.\d+){3}))$/i', $ipv4String, $ipv6String); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* {@inheritdoc} |
488
|
|
|
* |
489
|
|
|
* @see \IPLib\Address\AddressInterface::getComparableString() |
490
|
|
|
*/ |
491
|
171 |
|
public function getComparableString() |
492
|
|
|
{ |
493
|
171 |
|
return $this->longAddress; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* {@inheritdoc} |
498
|
|
|
* |
499
|
|
|
* @see \IPLib\Address\AddressInterface::matches() |
500
|
|
|
*/ |
501
|
13 |
|
public function matches(RangeInterface $range) |
502
|
|
|
{ |
503
|
13 |
|
return $range->contains($this); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* {@inheritdoc} |
508
|
|
|
* |
509
|
|
|
* @see \IPLib\Address\AddressInterface::getAddressAtOffset() |
510
|
|
|
*/ |
511
|
8 |
View Code Duplication |
public function getAddressAtOffset($n) |
|
|
|
|
512
|
|
|
{ |
513
|
8 |
|
if (!is_int($n)) { |
514
|
8 |
|
return null; |
515
|
8 |
|
} |
516
|
8 |
|
|
517
|
5 |
|
$boundary = 0x10000; |
518
|
1 |
|
$mod = $n; |
519
|
1 |
|
$words = $this->getWords(); |
520
|
|
|
for ($i = count($words) - 1; $i >= 0; $i--) { |
521
|
5 |
|
$tmp = ($words[$i] + $mod) % $boundary; |
522
|
|
|
$mod = (int) floor(($words[$i] + $mod) / $boundary); |
523
|
8 |
|
if ($tmp < 0) { |
524
|
8 |
|
$tmp += $boundary; |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
$words[$i] = $tmp; |
528
|
8 |
|
} |
529
|
|
|
|
530
|
|
|
if ($mod !== 0) { |
531
|
|
|
return null; |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
return static::fromWords($words); |
535
|
|
|
} |
536
|
8 |
|
|
537
|
|
|
/** |
538
|
8 |
|
* {@inheritdoc} |
539
|
8 |
|
* |
540
|
8 |
|
* @see \IPLib\Address\AddressInterface::getNextAddress() |
541
|
8 |
|
*/ |
542
|
5 |
|
public function getNextAddress() |
543
|
1 |
|
{ |
544
|
1 |
|
return $this->getAddressAtOffset(1); |
545
|
|
|
} |
546
|
5 |
|
|
547
|
|
|
/** |
548
|
8 |
|
* {@inheritdoc} |
549
|
8 |
|
* |
550
|
|
|
* @see \IPLib\Address\AddressInterface::getPreviousAddress() |
551
|
|
|
*/ |
552
|
|
|
public function getPreviousAddress() |
553
|
8 |
|
{ |
554
|
|
|
return $this->getAddressAtOffset(-1); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
/** |
558
|
|
|
* {@inheritdoc} |
559
|
|
|
* |
560
|
|
|
* @see \IPLib\Address\AddressInterface::getReverseDNSLookupName() |
561
|
22 |
|
*/ |
562
|
|
|
public function getReverseDNSLookupName() |
563
|
22 |
|
{ |
564
|
22 |
|
return implode( |
565
|
22 |
|
'.', |
566
|
22 |
|
array_reverse(str_split(str_replace(':', '', $this->toString(true)), 1)) |
567
|
|
|
) . '.ip6.arpa'; |
568
|
|
|
} |
569
|
|
|
} |
570
|
|
|
|
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.