@@ -34,6 +34,9 @@ class Validator
3434 */
3535 public static function isEmail (string $ email ) : bool
3636 {
37+ if ( trim ($ email ) === '' ) {
38+ return false ;
39+ }
3740 return (bool )Stringify::filter ($ email , null , 274 );
3841 }
3942
@@ -48,6 +51,9 @@ public static function isEmail(string $email) : bool
4851 */
4952 public static function isUrl (string $ url ) : bool
5053 {
54+ if ( trim ($ url ) === '' ) {
55+ return false ;
56+ }
5157 return (bool )Stringify::filter ($ url , null , 273 );
5258 }
5359
@@ -90,22 +96,40 @@ public static function isDate($date, bool $time = false) : bool
9096 */
9197 public static function isIp (string $ ip ) : bool
9298 {
93- $ pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ ' ;
94- $ match = Stringify::match ($ pattern , $ ip , $ matches );
95- return $ match || self ::isIpV6 ($ ip );
99+ if ( trim ($ ip ) === '' ) {
100+ return false ;
101+ }
102+ return (bool )Stringify::filter ($ ip , 'ip ' );
103+ }
104+
105+ /**
106+ * Check IPv6 address.
107+ *
108+ * @access public
109+ * @param string $ip
110+ * @return bool
111+ */
112+ public static function isIpV6 (string $ ip ) : bool
113+ {
114+ if ( trim ($ ip ) === '' ) {
115+ return false ;
116+ }
117+ return (bool )Stringify::filter ($ ip , 'ipv6 ' );
96118 }
97119
98120 /**
99121 * Validate MAC.
100122 *
101123 * @access public
102- * @param string $address
124+ * @param string $mac
103125 * @return bool
104126 */
105- public static function isMac (string $ address ) : bool
127+ public static function isMac (string $ mac ) : bool
106128 {
107- $ pattern = "/^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$/i " ;
108- return Stringify::match ($ pattern , $ address , $ matches );
129+ if ( trim ($ mac ) === '' || strlen ($ mac ) !== 17 ) {
130+ return false ;
131+ }
132+ return (bool )Stringify::filter ($ mac , 'mac ' );
109133 }
110134
111135 /**
@@ -117,6 +141,9 @@ public static function isMac(string $address) : bool
117141 */
118142 public static function isStream (string $ path ) : bool
119143 {
144+ if ( trim ($ path ) === '' ) {
145+ return false ;
146+ }
120147 return Stream::isValid ($ path );
121148 }
122149
@@ -166,6 +193,9 @@ public static function isMime(string $file, ?array $types = null) : bool
166193 */
167194 public static function isModule (string $ module ) : bool
168195 {
196+ if ( trim ($ module ) === '' ) {
197+ return false ;
198+ }
169199 return extension_loaded ($ module );
170200 }
171201
@@ -209,58 +239,6 @@ public static function isVersion(string $v1, string $v2, string $operator = '=='
209239 return version_compare ($ v1 , $ v2 , $ operator );
210240 }
211241
212- /**
213- * Check IPv6 address.
214- *
215- * @access public
216- * @param string $ip
217- * @return bool
218- */
219- public static function isIpV6 (string $ ip ) : bool
220- {
221- $ ip = self ::uncompressIpV6 ($ ip );
222- list ($ ipv6 , $ ipv4 ) = self ::splitIpV6 ($ ip );
223-
224- $ ipv6 = explode (': ' , $ ipv6 );
225- $ ipv4 = explode ('. ' , $ ipv4 );
226-
227- if ( count ($ ipv6 ) === 8 && count ($ ipv4 ) === 1 || count ($ ipv6 ) === 6 && count ($ ipv4 ) === 4 ) {
228- foreach ($ ipv6 as $ part ) {
229-
230- if ( $ part === '' ) {
231- return false ;
232- }
233-
234- if ( strlen ($ part ) > 4 ) {
235- return false ;
236- }
237-
238- $ part = ltrim ($ part , '0 ' );
239- if ( $ part === '' ) {
240- $ part = '0 ' ;
241- }
242-
243- $ value = hexdec ($ part );
244- if ( dechex ($ value ) !== strtolower ($ part ) || $ value < 0 || $ value > 0xFFFF ) {
245- return false ;
246- }
247- }
248-
249- if ( count ($ ipv4 ) === 4 ) {
250- foreach ($ ipv4 as $ part ) {
251- $ value = (int )$ part ;
252- if ( (string )$ value !== $ part || $ value < 0 || $ value > 0xFF ) {
253- return false ;
254- }
255- }
256- }
257-
258- return true ;
259- }
260-
261- return false ;
262- }
263-
264242 /**
265243 * Validate cookie value.
266244 *
@@ -311,63 +289,6 @@ public static function isCookieName(string $name, int $length = 255) : bool
311289 return true ;
312290 }
313291
314- /**
315- * Uncompresses IPv6 address.
316- *
317- * @access private
318- * @return string
319- */
320- private static function uncompressIpV6 (string $ ip ) : string
321- {
322- if ( Stringify::subCount ($ ip , ':: ' ) !== 1 ) {
323- return $ ip ;
324- }
325-
326- list ($ ip1 , $ ip2 ) = explode (':: ' , $ ip );
327- $ c1 = ($ ip1 === '' ) ? -1 : Stringify::subCount ($ ip1 , ': ' );
328- $ c2 = ($ ip2 === '' ) ? -1 : Stringify::subCount ($ ip2 , ': ' );
329-
330- if ( strpos ($ ip2 , '. ' ) !== false ) {
331- $ c2 ++;
332- }
333-
334- if ( $ c1 === -1 && $ c2 === -1 ) {
335- $ ip = '0:0:0:0:0:0:0:0 ' ;
336-
337- } elseif ( $ c1 === -1 ) {
338- $ fill = Stringify::repeat ('0: ' , 7 - $ c2 );
339- $ ip = Stringify::replace (':: ' , $ fill , $ ip );
340-
341- } elseif ( $ c2 === -1 ) {
342- $ fill = Stringify::repeat (':0 ' , 7 - $ c1 );
343- $ ip = Stringify::replace (':: ' , $ fill , $ ip );
344-
345- } else {
346- $ fill = ': ' . Stringify::repeat ('0: ' , 6 - $ c2 - $ c1 );
347- $ ip = Stringify::replace (':: ' , $ fill , $ ip );
348- }
349-
350- return $ ip ;
351- }
352-
353- /**
354- * Splits IPv6 address into the IPv6 and IPv4 parts.
355- *
356- * @access private
357- * @param string $ip
358- * @return array
359- */
360- private static function splitIpV6 (string $ ip ) : array
361- {
362- if ( strpos ($ ip , '. ' ) !== false ) {
363- $ pos = (int )strrpos ($ ip , ': ' );
364- $ ipv6 = substr ($ ip , 0 , $ pos );
365- $ ipv4 = substr ($ ip , $ pos + 1 );
366- return [$ ipv6 , $ ipv4 ];
367- }
368- return [$ ip , '' ];
369- }
370-
371292 /**
372293 * Check if two IP addresses are in the same subnet.
373294 *
@@ -397,40 +318,4 @@ public static function isSameSubnet(string $ip1, string $ip2, int $cidr = 24) :
397318 // For IPv6 or other cases, require exact match
398319 return false ;
399320 }
400-
401- /**
402- * Validate and sanitize file path against directory traversal attacks.
403- *
404- * @access public
405- * @param string $path
406- * @param bool $throwException
407- * @return string
408- * @throws \InvalidArgumentException
409- */
410- public static function isPath (string $ path , bool $ throwException = false ) : string
411- {
412- // Normalize path
413- $ path = Stringify::formatPath ($ path );
414-
415- // Check for directory traversal patterns
416- $ dangerous = ['../ ' , '..\/ ' , '../ ' , '.. \\' ];
417- foreach ($ dangerous as $ pattern ) {
418- if ( Stringify::contains ($ path , $ pattern ) ) {
419- if ( $ throwException ) {
420- throw new \InvalidArgumentException ("Path contains directory traversal: {$ path }" );
421- }
422- return '' ;
423- }
424- }
425-
426- // Check for null bytes (path truncation attack)
427- if ( Stringify::contains ($ path , "\0" ) ) {
428- if ( $ throwException ) {
429- throw new \InvalidArgumentException ("Path contains null byte: {$ path }" );
430- }
431- return '' ;
432- }
433-
434- return $ path ;
435- }
436321}
0 commit comments