From 0a4100b8c787ef444c4dab6bb80a1f1dadfaea9a Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 22 May 2025 22:19:10 +0200 Subject: [PATCH] Create function 'IpAddress::mask' --- src/IpAddress.php | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/IpAddress.php diff --git a/src/IpAddress.php b/src/IpAddress.php new file mode 100644 index 0000000..3a175d5 --- /dev/null +++ b/src/IpAddress.php @@ -0,0 +1,111 @@ +> $maskBitsIpv4) << $maskBitsIpv4; + + $packedIp = \pack('N', $ipInt32 & $mask); + + $prefixLength = self::IPV4_LENGTH_BITS - $maskBitsIpv4; + } + // for IPv6 addresses + elseif ($ipLengthInBytes === self::IPV6_LENGTH_BYTES) { + // if the IP address is an IPv4-mapped IPv6 address + if (\substr($packedIp, 0, 12) === "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") { + // the last 4 bytes are the IPv4 address, so mask bits as per IPv4 option + $maskBitsIpv6 = $maskBitsIpv4; + } + + if ($maskBitsIpv6 === 0) { + return $ip; + } + elseif ($maskBitsIpv6 === self::IPV6_LENGTH_BITS) { + return '::'; + } + + $maskBytesIpv6 = (int) \ceil($maskBitsIpv6 / 8); + $maskBitsInFirstByteIpv6 = $maskBitsIpv6 % 8; + + // work byte by byte for IPv6 due to lack of 128-bit integers + + for ($i = 0; $i < $maskBytesIpv6; $i++) { + // start from the rightmost byte + $byteIndex = $ipLengthInBytes - $i - 1; + + // if we are at the first byte and it should only be masked partially (i.e. masking 1-7 bits there) + if ($i === ($maskBytesIpv6 - 1) && $maskBitsInFirstByteIpv6 !== 0) { + $firstByteMask = (0xFF >> $maskBitsInFirstByteIpv6) << $maskBitsInFirstByteIpv6; + $packedIp[$byteIndex] = \chr(\ord($packedIp[$byteIndex]) & $firstByteMask); + } + // when masking a full first byte or any byte after the first byte + else { + $packedIp[$byteIndex] = "\x00"; + } + } + + $prefixLength = self::IPV6_LENGTH_BITS - $maskBitsIpv6; + } + // for addresses with invalid lengths in bytes + else { + return null; + } + + $ip = \inet_ntop($packedIp); + + if ($includePrefixLength) { + return $ip . '/' . $prefixLength; + } + else { + return $ip; + } + } + +}