3838 */
3939class IpAddress {
4040 /**
41- * @param string $ip IP to normalized
41+ * @param string $ip IP to normalize
4242 */
4343 public function __construct (
4444 private string $ ip ,
4545 ) {
4646 }
4747
4848 /**
49- * Return the given subnet for an IPv4 address and mask bits
49+ * Return the given subnet for an IPv6 address (64 first bits)
5050 */
51- private function getIPv4Subnet (string $ ip , int $ maskBits = 32 ): string {
52- $ binary = \inet_pton ($ ip );
53- for ($ i = 32 ; $ i > $ maskBits ; $ i -= 8 ) {
54- $ j = \intdiv ($ i , 8 ) - 1 ;
55- $ k = \min (8 , $ i - $ maskBits );
56- $ mask = (0xff - ((2 ** $ k ) - 1 ));
57- $ int = \unpack ('C ' , $ binary [$ j ]);
58- $ binary [$ j ] = \pack ('C ' , $ int [1 ] & $ mask );
59- }
60- return \inet_ntop ($ binary ).'/ ' .$ maskBits ;
61- }
62-
63- /**
64- * Return the given subnet for an IPv6 address and mask bits
65- */
66- private function getIPv6Subnet (string $ ip , int $ maskBits = 48 ): string {
51+ private function getIPv6Subnet (string $ ip ): string {
6752 if ($ ip [0 ] === '[ ' && $ ip [-1 ] === '] ' ) { // If IP is with brackets, for example [::1]
6853 $ ip = substr ($ ip , 1 , strlen ($ ip ) - 2 );
6954 }
7055 $ pos = strpos ($ ip , '% ' ); // if there is an explicit interface added to the IP, e.g. fe80::ae2d:d1e7:fe1e:9a8d%enp2s0
7156 if ($ pos !== false ) {
7257 $ ip = substr ($ ip , 0 , $ pos - 1 );
7358 }
59+
7460 $ binary = \inet_pton ($ ip );
75- for ($ i = 128 ; $ i > $ maskBits ; $ i -= 8 ) {
76- $ j = \intdiv ($ i , 8 ) - 1 ;
77- $ k = \min (8 , $ i - $ maskBits );
78- $ mask = (0xff - ((2 ** $ k ) - 1 ));
79- $ int = \unpack ('C ' , $ binary [$ j ]);
80- $ binary [$ j ] = \pack ('C ' , $ int [1 ] & $ mask );
81- }
82- return \inet_ntop ($ binary ).'/ ' .$ maskBits ;
61+ $ mask = inet_pton ('FFFF:FFFF:FFFF:FFFF:: ' );
62+
63+ return inet_ntop ($ binary & $ mask ).'/64 ' ;
8364 }
8465
8566 /**
@@ -93,50 +74,30 @@ private function getEmbeddedIpv4(string $ipv6): ?string {
9374 if (!$ binary ) {
9475 return null ;
9576 }
96- for ($ i = 0 ; $ i <= 9 ; $ i ++) {
97- if (unpack ('C ' , $ binary [$ i ])[1 ] !== 0 ) {
98- return null ;
99- }
100- }
10177
102- for ($ i = 10 ; $ i <= 11 ; $ i ++) {
103- if (unpack ('C ' , $ binary [$ i ])[1 ] !== 255 ) {
104- return null ;
105- }
106- }
107-
108- $ binary4 = '' ;
109- for ($ i = 12 ; $ i < 16 ; $ i ++) {
110- $ binary4 .= $ binary [$ i ];
78+ $ mask = inet_pton ('::FFFF:FFFF ' );
79+ if (($ binary & ~$ mask ) !== inet_pton ('::FFFF:0.0.0.0 ' )) {
80+ return null ;
11181 }
11282
113- return inet_ntop ($ binary4 );
83+ return inet_ntop (substr ( $ binary , - 4 ) );
11484 }
11585
11686
11787 /**
11888 * Gets either the /32 (IPv4) or the /64 (IPv6) subnet of an IP address
11989 */
12090 public function getSubnet (): string {
121- if (\preg_match ('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ ' , $ this ->ip )) {
122- return $ this ->getIPv4Subnet (
123- $ this ->ip ,
124- 32
125- );
91+ if (filter_var ($ this ->ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 )) {
92+ return $ this ->ip .'/32 ' ;
12693 }
12794
12895 $ ipv4 = $ this ->getEmbeddedIpv4 ($ this ->ip );
12996 if ($ ipv4 !== null ) {
130- return $ this ->getIPv4Subnet (
131- $ ipv4 ,
132- 32
133- );
97+ return $ ipv4 .'/32 ' ;
13498 }
13599
136- return $ this ->getIPv6Subnet (
137- $ this ->ip ,
138- 64
139- );
100+ return $ this ->getIPv6Subnet ($ this ->ip );
140101 }
141102
142103 /**
0 commit comments