1 | <?php |
||||
2 | |||||
3 | namespace IPLib\Address; |
||||
4 | |||||
5 | use IPLib\ParseStringFlag; |
||||
6 | use IPLib\Range\RangeInterface; |
||||
7 | use IPLib\Range\Subnet; |
||||
8 | use IPLib\Range\Type as RangeType; |
||||
9 | |||||
10 | /** |
||||
11 | * An IPv4 address. |
||||
12 | */ |
||||
13 | class IPv4 implements AddressInterface |
||||
14 | { |
||||
15 | /** |
||||
16 | * The string representation of the address. |
||||
17 | * |
||||
18 | * @var string |
||||
19 | * |
||||
20 | * @example '127.0.0.1' |
||||
21 | */ |
||||
22 | protected $address; |
||||
23 | |||||
24 | /** |
||||
25 | * The byte list of the IP address. |
||||
26 | * |
||||
27 | * @var int[]|null |
||||
28 | */ |
||||
29 | protected $bytes; |
||||
30 | |||||
31 | /** |
||||
32 | * The type of the range of this IP address. |
||||
33 | * |
||||
34 | * @var int|null |
||||
35 | */ |
||||
36 | protected $rangeType; |
||||
37 | |||||
38 | /** |
||||
39 | * A string representation of this address than can be used when comparing addresses and ranges. |
||||
40 | * |
||||
41 | * @var string |
||||
42 | */ |
||||
43 | protected $comparableString; |
||||
44 | |||||
45 | /** |
||||
46 | * An array containing RFC designated address ranges. |
||||
47 | * |
||||
48 | * @var array|null |
||||
49 | */ |
||||
50 | private static $reservedRanges; |
||||
51 | |||||
52 | /** |
||||
53 | * Initializes the instance. |
||||
54 | * |
||||
55 | * @param string $address |
||||
56 | */ |
||||
57 | 977 | protected function __construct($address) |
|||
58 | { |
||||
59 | 977 | $this->address = $address; |
|||
60 | 977 | $this->bytes = null; |
|||
61 | 977 | $this->rangeType = null; |
|||
62 | 977 | $this->comparableString = null; |
|||
63 | 977 | } |
|||
64 | |||||
65 | /** |
||||
66 | * {@inheritdoc} |
||||
67 | * |
||||
68 | * @see \IPLib\Address\AddressInterface::__toString() |
||||
69 | */ |
||||
70 | 258 | public function __toString() |
|||
71 | { |
||||
72 | 258 | return $this->address; |
|||
73 | } |
||||
74 | |||||
75 | /** |
||||
76 | * {@inheritdoc} |
||||
77 | * |
||||
78 | * @see \IPLib\Address\AddressInterface::getNumberOfBits() |
||||
79 | */ |
||||
80 | 60 | public static function getNumberOfBits() |
|||
81 | { |
||||
82 | 60 | return 32; |
|||
83 | } |
||||
84 | |||||
85 | /** |
||||
86 | * @deprecated since 1.17.0: use the parseString() method instead. |
||||
87 | * For upgrading: |
||||
88 | * - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag |
||||
89 | * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag |
||||
90 | * |
||||
91 | * @param string|mixed $address the address to parse |
||||
92 | * @param bool $mayIncludePort |
||||
93 | * @param bool $supportNonDecimalIPv4 |
||||
94 | * |
||||
95 | * @return static|null |
||||
96 | * |
||||
97 | * @see \IPLib\Address\IPv4::parseString() |
||||
98 | * @since 1.1.0 added the $mayIncludePort argument |
||||
99 | * @since 1.10.0 added the $supportNonDecimalIPv4 argument |
||||
100 | */ |
||||
101 | 20 | public static function fromString($address, $mayIncludePort = true, $supportNonDecimalIPv4 = false) |
|||
102 | { |
||||
103 | 20 | return static::parseString($address, 0 | ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0)); |
|||
104 | } |
||||
105 | |||||
106 | /** |
||||
107 | * Parse a string and returns an IPv4 instance if the string is valid, or null otherwise. |
||||
108 | * |
||||
109 | * @param string|mixed $address the address to parse |
||||
110 | * @param int $flags A combination or zero or more flags |
||||
111 | * |
||||
112 | * @return static|null |
||||
113 | * |
||||
114 | * @see \IPLib\ParseStringFlag |
||||
115 | * @since 1.17.0 |
||||
116 | */ |
||||
117 | 1691 | public static function parseString($address, $flags = 0) |
|||
118 | { |
||||
119 | 1691 | if (!is_string($address)) { |
|||
120 | 3 | return null; |
|||
121 | } |
||||
122 | 1688 | $flags = (int) $flags; |
|||
123 | 1688 | $matches = null; |
|||
124 | 1688 | if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) { |
|||
125 | 9 | if (preg_match('/^([12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2})\.in-addr\.arpa\.?$/i', $address, $matches)) { |
|||
126 | 4 | $address = implode('.', array_reverse(explode('.', $matches[1]))); |
|||
127 | 4 | $flags = $flags & ~(ParseStringFlag::IPV4_MAYBE_NON_DECIMAL | ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED); |
|||
128 | } |
||||
129 | } |
||||
130 | 1688 | if ($flags & ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED) { |
|||
131 | 13 | if (strpos($address, '.') === 0) { |
|||
132 | 1 | return null; |
|||
133 | } |
||||
134 | 12 | $lengthNonHex = '{1,11}'; |
|||
135 | 12 | $lengthHex = '{1,8}'; |
|||
136 | 12 | $chunk234Optional = true; |
|||
137 | } else { |
||||
138 | 1675 | if (!strpos($address, '.')) { |
|||
139 | 707 | return null; |
|||
140 | } |
||||
141 | 991 | $lengthNonHex = '{1,3}'; |
|||
142 | 991 | $lengthHex = '{1,2}'; |
|||
143 | 991 | $chunk234Optional = false; |
|||
144 | } |
||||
145 | 1003 | $rxChunk1 = "0?[0-9]{$lengthNonHex}"; |
|||
146 | 1003 | if ($flags & ParseStringFlag::IPV4_MAYBE_NON_DECIMAL) { |
|||
147 | 44 | $rxChunk1 = "(?:0[Xx]0*[0-9A-Fa-f]{$lengthHex})|(?:{$rxChunk1})"; |
|||
148 | 44 | $onlyDecimal = false; |
|||
149 | } else { |
||||
150 | 971 | $onlyDecimal = true; |
|||
151 | } |
||||
152 | 1003 | $rxChunk1 = "0*?({$rxChunk1})"; |
|||
153 | 1003 | $rxChunk234 = "\.{$rxChunk1}"; |
|||
154 | 1003 | if ($chunk234Optional) { |
|||
155 | 12 | $rxChunk234 = "(?:{$rxChunk234})?"; |
|||
156 | } |
||||
157 | 1003 | $rx = "{$rxChunk1}{$rxChunk234}{$rxChunk234}{$rxChunk234}"; |
|||
158 | 1003 | if ($flags & ParseStringFlag::MAY_INCLUDE_PORT) { |
|||
159 | 455 | $rx .= '(?::\d+)?'; |
|||
160 | } |
||||
161 | 1003 | if (!preg_match('/^' . $rx . '$/', $address, $matches)) { |
|||
162 | 42 | return null; |
|||
163 | } |
||||
164 | 974 | $math = new \IPLib\Service\UnsignedIntegerMath(); |
|||
165 | 974 | $nums = array(); |
|||
166 | 974 | $maxChunkIndex = count($matches) - 1; |
|||
167 | 974 | for ($i = 1; $i <= $maxChunkIndex; $i++) { |
|||
168 | 974 | $numBytes = $i === $maxChunkIndex ? 5 - $i : 1; |
|||
169 | 974 | $chunkBytes = $math->getBytes($matches[$i], $numBytes, $onlyDecimal); |
|||
170 | 974 | if ($chunkBytes === null) { |
|||
171 | 12 | return null; |
|||
172 | } |
||||
173 | 968 | $nums = array_merge($nums, $chunkBytes); |
|||
174 | } |
||||
175 | |||||
176 | 962 | return new static(implode('.', $nums)); |
|||
177 | } |
||||
178 | |||||
179 | /** |
||||
180 | * Parse an array of bytes and returns an IPv4 instance if the array is valid, or null otherwise. |
||||
181 | * |
||||
182 | * @param int[]|array $bytes |
||||
183 | * |
||||
184 | * @return static|null |
||||
185 | */ |
||||
186 | 759 | public static function fromBytes(array $bytes) |
|||
187 | { |
||||
188 | 759 | $result = null; |
|||
189 | 759 | if (count($bytes) === 4) { |
|||
190 | 445 | $chunks = array_map( |
|||
191 | function ($byte) { |
||||
192 | 445 | return (is_int($byte) && $byte >= 0 && $byte <= 255) ? (string) $byte : false; |
|||
193 | 445 | }, |
|||
194 | 445 | $bytes |
|||
195 | ); |
||||
196 | 445 | if (in_array(false, $chunks, true) === false) { |
|||
197 | 445 | $result = new static(implode('.', $chunks)); |
|||
198 | } |
||||
199 | } |
||||
200 | |||||
201 | 759 | return $result; |
|||
202 | } |
||||
203 | |||||
204 | /** |
||||
205 | * {@inheritdoc} |
||||
206 | * |
||||
207 | * @see \IPLib\Address\AddressInterface::toString() |
||||
208 | */ |
||||
209 | 651 | public function toString($long = false) |
|||
210 | { |
||||
211 | 651 | if ($long) { |
|||
212 | 22 | return $this->getComparableString(); |
|||
213 | } |
||||
214 | |||||
215 | 651 | return $this->address; |
|||
216 | } |
||||
217 | |||||
218 | /** |
||||
219 | * Get the octal representation of this IP address. |
||||
220 | * |
||||
221 | * @param bool $long |
||||
222 | * |
||||
223 | * @return string |
||||
224 | * |
||||
225 | * @since 1.10.0 |
||||
226 | * |
||||
227 | * @example if $long == false: if the decimal representation is '0.7.8.255': '0.7.010.0377' |
||||
228 | * @example if $long == true: if the decimal representation is '0.7.8.255': '0000.0007.0010.0377' |
||||
229 | */ |
||||
230 | 12 | public function toOctal($long = false) |
|||
231 | { |
||||
232 | 12 | $chunks = array(); |
|||
233 | 12 | foreach ($this->getBytes() as $byte) { |
|||
234 | 12 | if ($long) { |
|||
235 | 12 | $chunks[] = sprintf('%04o', $byte); |
|||
236 | } else { |
||||
237 | 12 | $chunks[] = '0' . decoct($byte); |
|||
238 | } |
||||
239 | } |
||||
240 | |||||
241 | 12 | return implode('.', $chunks); |
|||
242 | } |
||||
243 | |||||
244 | /** |
||||
245 | * Get the hexadecimal representation of this IP address. |
||||
246 | * |
||||
247 | * @param bool $long |
||||
248 | * |
||||
249 | * @return string |
||||
250 | * |
||||
251 | * @since 1.10.0 |
||||
252 | * |
||||
253 | * @example if $long == false: if the decimal representation is '0.9.10.255': '0.9.0xa.0xff' |
||||
254 | * @example if $long == true: if the decimal representation is '0.9.10.255': '0x00.0x09.0x0a.0xff' |
||||
255 | */ |
||||
256 | 12 | public function toHexadecimal($long = false) |
|||
257 | { |
||||
258 | 12 | $chunks = array(); |
|||
259 | 12 | foreach ($this->getBytes() as $byte) { |
|||
260 | 12 | if ($long) { |
|||
261 | 12 | $chunks[] = sprintf('0x%02x', $byte); |
|||
262 | } else { |
||||
263 | 12 | $chunks[] = '0x' . dechex($byte); |
|||
264 | } |
||||
265 | } |
||||
266 | |||||
267 | 12 | return implode('.', $chunks); |
|||
268 | } |
||||
269 | |||||
270 | /** |
||||
271 | * {@inheritdoc} |
||||
272 | * |
||||
273 | * @see \IPLib\Address\AddressInterface::getBytes() |
||||
274 | */ |
||||
275 | 753 | public function getBytes() |
|||
276 | { |
||||
277 | 753 | if ($this->bytes === null) { |
|||
278 | 753 | $this->bytes = array_map( |
|||
279 | function ($chunk) { |
||||
280 | 753 | return (int) $chunk; |
|||
281 | 753 | }, |
|||
282 | 753 | explode('.', $this->address) |
|||
283 | ); |
||||
284 | } |
||||
285 | |||||
286 | 753 | return $this->bytes; |
|||
287 | } |
||||
288 | |||||
289 | /** |
||||
290 | * {@inheritdoc} |
||||
291 | * |
||||
292 | * @see \IPLib\Address\AddressInterface::getBits() |
||||
293 | */ |
||||
294 | 144 | public function getBits() |
|||
295 | { |
||||
296 | 144 | $parts = array(); |
|||
297 | 144 | foreach ($this->getBytes() as $byte) { |
|||
298 | 144 | $parts[] = sprintf('%08b', $byte); |
|||
299 | } |
||||
300 | |||||
301 | 144 | return implode('', $parts); |
|||
302 | } |
||||
303 | |||||
304 | /** |
||||
305 | * {@inheritdoc} |
||||
306 | * |
||||
307 | * @see \IPLib\Address\AddressInterface::getAddressType() |
||||
308 | */ |
||||
309 | 611 | public function getAddressType() |
|||
310 | { |
||||
311 | 611 | return Type::T_IPv4; |
|||
312 | } |
||||
313 | |||||
314 | /** |
||||
315 | * {@inheritdoc} |
||||
316 | * |
||||
317 | * @see \IPLib\Address\AddressInterface::getDefaultReservedRangeType() |
||||
318 | */ |
||||
319 | 173 | public static function getDefaultReservedRangeType() |
|||
320 | { |
||||
321 | 173 | return RangeType::T_PUBLIC; |
|||
322 | } |
||||
323 | |||||
324 | /** |
||||
325 | * {@inheritdoc} |
||||
326 | * |
||||
327 | * @see \IPLib\Address\AddressInterface::getReservedRanges() |
||||
328 | */ |
||||
329 | 341 | public static function getReservedRanges() |
|||
330 | { |
||||
331 | 341 | if (self::$reservedRanges === null) { |
|||
332 | 1 | $reservedRanges = array(); |
|||
333 | foreach (array( |
||||
334 | // RFC 5735 |
||||
335 | 1 | '0.0.0.0/8' => array(RangeType::T_THISNETWORK, array('0.0.0.0/32' => RangeType::T_UNSPECIFIED)), |
|||
336 | // RFC 5735 |
||||
337 | '10.0.0.0/8' => array(RangeType::T_PRIVATENETWORK), |
||||
338 | // RFC 6598 |
||||
339 | '100.64.0.0/10' => array(RangeType::T_CGNAT), |
||||
340 | // RFC 5735 |
||||
341 | '127.0.0.0/8' => array(RangeType::T_LOOPBACK), |
||||
342 | // RFC 5735 |
||||
343 | '169.254.0.0/16' => array(RangeType::T_LINKLOCAL), |
||||
344 | // RFC 5735 |
||||
345 | '172.16.0.0/12' => array(RangeType::T_PRIVATENETWORK), |
||||
346 | // RFC 5735 |
||||
347 | '192.0.0.0/24' => array(RangeType::T_RESERVED), |
||||
348 | // RFC 5735 |
||||
349 | '192.0.2.0/24' => array(RangeType::T_RESERVED), |
||||
350 | // RFC 5735 |
||||
351 | '192.88.99.0/24' => array(RangeType::T_ANYCASTRELAY), |
||||
352 | // RFC 5735 |
||||
353 | '192.168.0.0/16' => array(RangeType::T_PRIVATENETWORK), |
||||
354 | // RFC 5735 |
||||
355 | '198.18.0.0/15' => array(RangeType::T_RESERVED), |
||||
356 | // RFC 5735 |
||||
357 | '198.51.100.0/24' => array(RangeType::T_RESERVED), |
||||
358 | // RFC 5735 |
||||
359 | '203.0.113.0/24' => array(RangeType::T_RESERVED), |
||||
360 | // RFC 5735 |
||||
361 | '224.0.0.0/4' => array(RangeType::T_MULTICAST), |
||||
362 | // RFC 5735 |
||||
363 | '240.0.0.0/4' => array(RangeType::T_RESERVED, array('255.255.255.255/32' => RangeType::T_LIMITEDBROADCAST)), |
||||
364 | ) as $range => $data) { |
||||
365 | 1 | $exceptions = array(); |
|||
366 | 1 | if (isset($data[1])) { |
|||
367 | 1 | foreach ($data[1] as $exceptionRange => $exceptionType) { |
|||
368 | 1 | $exceptions[] = new AssignedRange(Subnet::parseString($exceptionRange), $exceptionType); |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
369 | } |
||||
370 | } |
||||
371 | 1 | $reservedRanges[] = new AssignedRange(Subnet::parseString($range), $data[0], $exceptions); |
|||
372 | } |
||||
373 | 1 | self::$reservedRanges = $reservedRanges; |
|||
374 | } |
||||
375 | |||||
376 | 341 | return self::$reservedRanges; |
|||
377 | } |
||||
378 | |||||
379 | /** |
||||
380 | * {@inheritdoc} |
||||
381 | * |
||||
382 | * @see \IPLib\Address\AddressInterface::getRangeType() |
||||
383 | */ |
||||
384 | 171 | public function getRangeType() |
|||
385 | { |
||||
386 | 171 | if ($this->rangeType === null) { |
|||
387 | 171 | $rangeType = null; |
|||
388 | 171 | foreach (static::getReservedRanges() as $reservedRange) { |
|||
389 | 171 | $rangeType = $reservedRange->getAddressType($this); |
|||
390 | 171 | if ($rangeType !== null) { |
|||
391 | 168 | break; |
|||
392 | } |
||||
393 | } |
||||
394 | 171 | $this->rangeType = $rangeType === null ? static::getDefaultReservedRangeType() : $rangeType; |
|||
395 | } |
||||
396 | |||||
397 | 171 | return $this->rangeType; |
|||
398 | } |
||||
399 | |||||
400 | /** |
||||
401 | * Create an IPv6 representation of this address (in 6to4 notation). |
||||
402 | * |
||||
403 | * @return \IPLib\Address\IPv6 |
||||
404 | */ |
||||
405 | 4 | public function toIPv6() |
|||
406 | { |
||||
407 | 4 | $myBytes = $this->getBytes(); |
|||
408 | |||||
409 | 4 | return IPv6::parseString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::'); |
|||
410 | } |
||||
411 | |||||
412 | /** |
||||
413 | * Create an IPv6 representation of this address (in IPv6 IPv4-mapped notation). |
||||
414 | * |
||||
415 | * @return \IPLib\Address\IPv6 |
||||
416 | * |
||||
417 | * @since 1.11.0 |
||||
418 | */ |
||||
419 | 4 | public function toIPv6IPv4Mapped() |
|||
420 | { |
||||
421 | 4 | return IPv6::fromBytes(array_merge(array(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff), $this->getBytes())); |
|||
422 | } |
||||
423 | |||||
424 | /** |
||||
425 | * {@inheritdoc} |
||||
426 | * |
||||
427 | * @see \IPLib\Address\AddressInterface::getComparableString() |
||||
428 | */ |
||||
429 | 443 | public function getComparableString() |
|||
430 | { |
||||
431 | 443 | if ($this->comparableString === null) { |
|||
432 | 443 | $chunks = array(); |
|||
433 | 443 | foreach ($this->getBytes() as $byte) { |
|||
434 | 443 | $chunks[] = sprintf('%03d', $byte); |
|||
435 | } |
||||
436 | 443 | $this->comparableString = implode('.', $chunks); |
|||
437 | } |
||||
438 | |||||
439 | 443 | return $this->comparableString; |
|||
440 | } |
||||
441 | |||||
442 | /** |
||||
443 | * {@inheritdoc} |
||||
444 | * |
||||
445 | * @see \IPLib\Address\AddressInterface::matches() |
||||
446 | */ |
||||
447 | 11 | public function matches(RangeInterface $range) |
|||
448 | { |
||||
449 | 11 | return $range->contains($this); |
|||
450 | } |
||||
451 | |||||
452 | /** |
||||
453 | * {@inheritdoc} |
||||
454 | * |
||||
455 | * @see \IPLib\Address\AddressInterface::getAddressAtOffset() |
||||
456 | */ |
||||
457 | 29 | public function getAddressAtOffset($n) |
|||
458 | { |
||||
459 | 29 | if (!is_int($n)) { |
|||
460 | 1 | return null; |
|||
461 | } |
||||
462 | |||||
463 | 28 | $boundary = 256; |
|||
464 | 28 | $mod = $n; |
|||
465 | 28 | $bytes = $this->getBytes(); |
|||
466 | 28 | for ($i = count($bytes) - 1; $i >= 0; $i--) { |
|||
467 | 28 | $tmp = ($bytes[$i] + $mod) % $boundary; |
|||
468 | 28 | $mod = (int) floor(($bytes[$i] + $mod) / $boundary); |
|||
469 | 28 | if ($tmp < 0) { |
|||
470 | 13 | $tmp += $boundary; |
|||
471 | } |
||||
472 | |||||
473 | 28 | $bytes[$i] = $tmp; |
|||
474 | } |
||||
475 | |||||
476 | 28 | if ($mod !== 0) { |
|||
477 | 8 | return null; |
|||
478 | } |
||||
479 | |||||
480 | 22 | return static::fromBytes($bytes); |
|||
481 | } |
||||
482 | |||||
483 | /** |
||||
484 | * {@inheritdoc} |
||||
485 | * |
||||
486 | * @see \IPLib\Address\AddressInterface::getNextAddress() |
||||
487 | */ |
||||
488 | 9 | public function getNextAddress() |
|||
489 | { |
||||
490 | 9 | return $this->getAddressAtOffset(1); |
|||
491 | } |
||||
492 | |||||
493 | /** |
||||
494 | * {@inheritdoc} |
||||
495 | * |
||||
496 | * @see \IPLib\Address\AddressInterface::getPreviousAddress() |
||||
497 | */ |
||||
498 | 9 | public function getPreviousAddress() |
|||
499 | { |
||||
500 | 9 | return $this->getAddressAtOffset(-1); |
|||
501 | } |
||||
502 | |||||
503 | /** |
||||
504 | * {@inheritdoc} |
||||
505 | * |
||||
506 | * @see \IPLib\Address\AddressInterface::getReverseDNSLookupName() |
||||
507 | */ |
||||
508 | 14 | public function getReverseDNSLookupName() |
|||
509 | { |
||||
510 | 14 | return implode( |
|||
511 | 14 | '.', |
|||
512 | 14 | array_reverse($this->getBytes()) |
|||
513 | 14 | ) . '.in-addr.arpa'; |
|||
514 | } |
||||
515 | |||||
516 | /** |
||||
517 | * {@inheritdoc} |
||||
518 | * |
||||
519 | * @see \IPLib\Address\AddressInterface::shift() |
||||
520 | */ |
||||
521 | 139 | public function shift($bits) |
|||
522 | { |
||||
523 | 139 | $bits = (int) $bits; |
|||
524 | 139 | if ($bits === 0) { |
|||
525 | 9 | return $this; |
|||
526 | } |
||||
527 | 130 | $absBits = abs($bits); |
|||
528 | 130 | if ($absBits >= 32) { |
|||
529 | 16 | return new self('0.0.0.0'); |
|||
530 | } |
||||
531 | 114 | $pad = str_repeat('0', $absBits); |
|||
0 ignored issues
–
show
It seems like
$absBits can also be of type double ; however, parameter $times of str_repeat() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
532 | 114 | $paddedBits = $this->getBits(); |
|||
533 | 114 | if ($bits > 0) { |
|||
534 | 48 | $paddedBits = $pad . substr($paddedBits, 0, -$bits); |
|||
535 | } else { |
||||
536 | 66 | $paddedBits = substr($paddedBits, $absBits) . $pad; |
|||
0 ignored issues
–
show
It seems like
$absBits can also be of type double ; however, parameter $offset of substr() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
537 | } |
||||
538 | 114 | $bytes = array_map('bindec', str_split($paddedBits, 8)); |
|||
0 ignored issues
–
show
It seems like
str_split($paddedBits, 8) can also be of type true ; however, parameter $array of array_map() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
539 | |||||
540 | 114 | return new static(implode('.', $bytes)); |
|||
541 | } |
||||
542 | |||||
543 | /** |
||||
544 | * {@inheritdoc} |
||||
545 | * |
||||
546 | * @see \IPLib\Address\AddressInterface::add() |
||||
547 | */ |
||||
548 | 28 | public function add(AddressInterface $other) |
|||
549 | { |
||||
550 | 28 | if (!$other instanceof self) { |
|||
551 | 2 | return null; |
|||
552 | } |
||||
553 | 26 | $myBytes = $this->getBytes(); |
|||
554 | 26 | $otherBytes = $other->getBytes(); |
|||
555 | 26 | $sum = array_fill(0, 4, 0); |
|||
556 | 26 | $carry = 0; |
|||
557 | 26 | for ($index = 3; $index >= 0; $index--) { |
|||
558 | 26 | $byte = $myBytes[$index] + $otherBytes[$index] + $carry; |
|||
559 | 26 | if ($byte > 0xFF) { |
|||
560 | 17 | $carry = $byte >> 8; |
|||
561 | 17 | $byte &= 0xFF; |
|||
562 | } else { |
||||
563 | 24 | $carry = 0; |
|||
564 | } |
||||
565 | 26 | $sum[$index] = $byte; |
|||
566 | } |
||||
567 | 26 | if ($carry !== 0) { |
|||
568 | 3 | return null; |
|||
569 | } |
||||
570 | |||||
571 | 23 | return new static(implode('.', $sum)); |
|||
572 | } |
||||
573 | } |
||||
574 |