@@ -75,34 +75,34 @@ public static function checkIp(string $requestIp, string|array $ips): bool
75
75
public static function checkIp4 (string $ requestIp , string $ ip ): bool
76
76
{
77
77
$ cacheKey = $ requestIp .'- ' .$ ip .'-v4 ' ;
78
- if (isset ( self ::$ checkedIps [ $ cacheKey] )) {
79
- return self :: $ checkedIps [ $ cacheKey ] ;
78
+ if (null !== $ cacheValue = self ::getCacheResult ( $ cacheKey )) {
79
+ return $ cacheValue ;
80
80
}
81
81
82
82
if (!filter_var ($ requestIp , \FILTER_VALIDATE_IP , \FILTER_FLAG_IPV4 )) {
83
- return self ::$ checkedIps [ $ cacheKey] = false ;
83
+ return self ::setCacheResult ( $ cacheKey, false ) ;
84
84
}
85
85
86
86
if (str_contains ($ ip , '/ ' )) {
87
87
[$ address , $ netmask ] = explode ('/ ' , $ ip , 2 );
88
88
89
89
if ('0 ' === $ netmask ) {
90
- return self ::$ checkedIps [ $ cacheKey] = false !== filter_var ($ address , \FILTER_VALIDATE_IP , \FILTER_FLAG_IPV4 );
90
+ return self ::setCacheResult ( $ cacheKey, false !== filter_var ($ address , \FILTER_VALIDATE_IP , \FILTER_FLAG_IPV4 ) );
91
91
}
92
92
93
93
if ($ netmask < 0 || $ netmask > 32 ) {
94
- return self ::$ checkedIps [ $ cacheKey] = false ;
94
+ return self ::setCacheResult ( $ cacheKey, false ) ;
95
95
}
96
96
} else {
97
97
$ address = $ ip ;
98
98
$ netmask = 32 ;
99
99
}
100
100
101
101
if (false === ip2long ($ address )) {
102
- return self ::$ checkedIps [ $ cacheKey] = false ;
102
+ return self ::setCacheResult ( $ cacheKey, false ) ;
103
103
}
104
104
105
- return self ::$ checkedIps [ $ cacheKey] = 0 === substr_compare (sprintf ('%032b ' , ip2long ($ requestIp )), sprintf ('%032b ' , ip2long ($ address )), 0 , $ netmask );
105
+ return self ::setCacheResult ( $ cacheKey, 0 === substr_compare (sprintf ('%032b ' , ip2long ($ requestIp )), sprintf ('%032b ' , ip2long ($ address )), 0 , $ netmask) );
106
106
}
107
107
108
108
/**
@@ -120,8 +120,8 @@ public static function checkIp4(string $requestIp, string $ip): bool
120
120
public static function checkIp6 (string $ requestIp , string $ ip ): bool
121
121
{
122
122
$ cacheKey = $ requestIp .'- ' .$ ip .'-v6 ' ;
123
- if (isset ( self ::$ checkedIps [ $ cacheKey] )) {
124
- return self :: $ checkedIps [ $ cacheKey ] ;
123
+ if (null !== $ cacheValue = self ::getCacheResult ( $ cacheKey )) {
124
+ return $ cacheValue ;
125
125
}
126
126
127
127
if (!((\extension_loaded ('sockets ' ) && \defined ('AF_INET6 ' )) || @inet_pton ('::1 ' ))) {
@@ -130,26 +130,26 @@ public static function checkIp6(string $requestIp, string $ip): bool
130
130
131
131
// Check to see if we were given a IP4 $requestIp or $ip by mistake
132
132
if (!filter_var ($ requestIp , \FILTER_VALIDATE_IP , \FILTER_FLAG_IPV6 )) {
133
- return self ::$ checkedIps [ $ cacheKey] = false ;
133
+ return self ::setCacheResult ( $ cacheKey, false ) ;
134
134
}
135
135
136
136
if (str_contains ($ ip , '/ ' )) {
137
137
[$ address , $ netmask ] = explode ('/ ' , $ ip , 2 );
138
138
139
139
if (!filter_var ($ address , \FILTER_VALIDATE_IP , \FILTER_FLAG_IPV6 )) {
140
- return self ::$ checkedIps [ $ cacheKey] = false ;
140
+ return self ::setCacheResult ( $ cacheKey, false ) ;
141
141
}
142
142
143
143
if ('0 ' === $ netmask ) {
144
144
return (bool ) unpack ('n* ' , @inet_pton ($ address ));
145
145
}
146
146
147
147
if ($ netmask < 1 || $ netmask > 128 ) {
148
- return self ::$ checkedIps [ $ cacheKey] = false ;
148
+ return self ::setCacheResult ( $ cacheKey, false ) ;
149
149
}
150
150
} else {
151
151
if (!filter_var ($ ip , \FILTER_VALIDATE_IP , \FILTER_FLAG_IPV6 )) {
152
- return self ::$ checkedIps [ $ cacheKey] = false ;
152
+ return self ::setCacheResult ( $ cacheKey, false ) ;
153
153
}
154
154
155
155
$ address = $ ip ;
@@ -160,19 +160,19 @@ public static function checkIp6(string $requestIp, string $ip): bool
160
160
$ bytesTest = unpack ('n* ' , @inet_pton ($ requestIp ));
161
161
162
162
if (!$ bytesAddr || !$ bytesTest ) {
163
- return self ::$ checkedIps [ $ cacheKey] = false ;
163
+ return self ::setCacheResult ( $ cacheKey, false ) ;
164
164
}
165
165
166
166
for ($ i = 1 , $ ceil = ceil ($ netmask / 16 ); $ i <= $ ceil ; ++$ i ) {
167
167
$ left = $ netmask - 16 * ($ i - 1 );
168
168
$ left = ($ left <= 16 ) ? $ left : 16 ;
169
169
$ mask = ~(0xFFFF >> $ left ) & 0xFFFF ;
170
170
if (($ bytesAddr [$ i ] & $ mask ) != ($ bytesTest [$ i ] & $ mask )) {
171
- return self ::$ checkedIps [ $ cacheKey] = false ;
171
+ return self ::setCacheResult ( $ cacheKey, false ) ;
172
172
}
173
173
}
174
174
175
- return self ::$ checkedIps [ $ cacheKey] = true ;
175
+ return self ::setCacheResult ( $ cacheKey, true ) ;
176
176
}
177
177
178
178
/**
@@ -214,4 +214,28 @@ public static function isPrivateIp(string $requestIp): bool
214
214
{
215
215
return self ::checkIp ($ requestIp , self ::PRIVATE_SUBNETS );
216
216
}
217
+
218
+ private static function getCacheResult (string $ cacheKey ): ?bool
219
+ {
220
+ if (isset (self ::$ checkedIps [$ cacheKey ])) {
221
+ // Move the item last in cache (LRU)
222
+ $ value = self ::$ checkedIps [$ cacheKey ];
223
+ unset(self ::$ checkedIps [$ cacheKey ]);
224
+ self ::$ checkedIps [$ cacheKey ] = $ value ;
225
+
226
+ return self ::$ checkedIps [$ cacheKey ];
227
+ }
228
+
229
+ return null ;
230
+ }
231
+
232
+ private static function setCacheResult (string $ cacheKey , bool $ result ): bool
233
+ {
234
+ if (1000 < \count (self ::$ checkedIps )) {
235
+ // stop memory leak if there are many keys
236
+ self ::$ checkedIps = \array_slice (self ::$ checkedIps , 500 , null , true );
237
+ }
238
+
239
+ return self ::$ checkedIps [$ cacheKey ] = $ result ;
240
+ }
217
241
}
0 commit comments