Implenting IPv6 validation using filter_var(), with a regex-based fallback if the aforementioned function does not exist (PHP < 5.2). Fixes #38.

This commit is contained in:
Joël Perras 2009-12-22 23:52:06 -05:00
parent 850b95900b
commit 8eb9f9e8fd
2 changed files with 82 additions and 7 deletions

View file

@ -53,7 +53,6 @@ class Validation extends Object {
* @access private * @access private
*/ */
var $__pattern = array( var $__pattern = array(
'ip' => '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])',
'hostname' => '(?:[a-z0-9][-a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)' 'hostname' => '(?:[a-z0-9][-a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)'
); );
@ -548,10 +547,15 @@ class Validation extends Object {
* @return boolean Success * @return boolean Success
* @access public * @access public
*/ */
function ip($check) { function ip($check, $type = 'IPv4') {
if (function_exists('filter_var')) {
return filter_var($check, FILTER_VALIDATE_IP);
}
$_this =& Validation::getInstance(); $_this =& Validation::getInstance();
$_this->check = $check; $_this->check = $check;
$_this->regex = '/^' . $_this->__pattern['ip'] . '$/'; $_this->__populateIp();
$_this->regex = '/^' . $_this->__pattern[$type] . '$/';
return $_this->_check(); return $_this->_check();
} }
@ -604,8 +608,8 @@ class Validation extends Object {
/** /**
* Validate a multiple select. * Validate a multiple select.
* *
* Valid Options * Valid Options
* *
* - in => provide a list of choices that selections must be made from * - in => provide a list of choices that selections must be made from
* - max => maximun number of non-zero choices that can be made * - max => maximun number of non-zero choices that can be made
* - min => minimum number of non-zero choices that can be made * - min => minimum number of non-zero choices that can be made
@ -794,7 +798,7 @@ class Validation extends Object {
* The regex checks for the following component parts: * The regex checks for the following component parts:
* *
* - a valid, optional, scheme * - a valid, optional, scheme
* - a valid ip address OR * - a valid ip address OR
* a valid domain name as defined by section 2.3.1 of http://www.ietf.org/rfc/rfc1035.txt * a valid domain name as defined by section 2.3.1 of http://www.ietf.org/rfc/rfc1035.txt
* with an optional port number * with an optional port number
* - an optional valid path * - an optional valid path
@ -808,10 +812,11 @@ class Validation extends Object {
*/ */
function url($check, $strict = false) { function url($check, $strict = false) {
$_this =& Validation::getInstance(); $_this =& Validation::getInstance();
$_this->__populateIp();
$_this->check = $check; $_this->check = $check;
$validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~') . '\/0-9a-z]|(%[0-9a-f]{2}))'; $validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~') . '\/0-9a-z]|(%[0-9a-f]{2}))';
$_this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') . $_this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') .
'(?:' . $_this->__pattern['ip'] . '|' . $_this->__pattern['hostname'] . ')(?::[1-9][0-9]{0,3})?' . '(?:' . $_this->__pattern['IPv4'] . '|' . $_this->__pattern['hostname'] . ')(?::[1-9][0-9]{0,3})?' .
'(?:\/?|\/' . $validChars . '*)?' . '(?:\/?|\/' . $validChars . '*)?' .
'(?:\?' . $validChars . '*)?' . '(?:\?' . $validChars . '*)?' .
'(?:#' . $validChars . '*)?$/i'; '(?:#' . $validChars . '*)?$/i';
@ -945,6 +950,36 @@ class Validation extends Object {
return ($sum % 10 == 0); return ($sum % 10 == 0);
} }
/*
* Lazily popualate the IP address patterns used for validations
*
* @return void
*/
function __populateIp() {
if (!isset($this->__pattern['IPv6'])) {
$pattern = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}';
$pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})';
$pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})';
$pattern .= '(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)';
$pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}';
$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|';
$pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}';
$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})';
$pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)';
$pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]';
$pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})';
$pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?';
$this->__pattern['IPv6'] = $pattern;
}
if (!isset($this->__pattern['IPv4'])) {
$pattern = '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])';
$this->__pattern['IPv4'] = $pattern;
}
}
/** /**
* Reset internal variables for another validation run. * Reset internal variables for another validation run.
* *

View file

@ -1734,6 +1734,46 @@ class ValidationTest extends CakeTestCase {
$this->assertFalse(Validation::ip('127.0.0.256')); $this->assertFalse(Validation::ip('127.0.0.256'));
} }
/**
* testIp v6
*
* @access public
* @return void
*/
function testIpv6() {
$this->assertTrue(Validation::ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'IPv6'));
$this->assertTrue(Validation::ip('2001:db8:85a3:0:0:8a2e:370:7334', 'IPv6'));
$this->assertTrue(Validation::ip('2001:db8:85a3::8a2e:370:7334', 'IPv6'));
$this->assertTrue(Validation::ip('2001:0db8:0000:0000:0000:0000:1428:57ab', 'IPv6'));
$this->assertTrue(Validation::ip('2001:0db8:0000:0000:0000::1428:57ab', 'IPv6'));
$this->assertTrue(Validation::ip('2001:0db8:0:0:0:0:1428:57ab', 'IPv6'));
$this->assertTrue(Validation::ip('2001:0db8:0:0::1428:57ab', 'IPv6'));
$this->assertTrue(Validation::ip('2001:0db8::1428:57ab', 'IPv6'));
$this->assertTrue(Validation::ip('2001:db8::1428:57ab', 'IPv6'));
$this->assertTrue(Validation::ip('0000:0000:0000:0000:0000:0000:0000:0001', 'IPv6'));
$this->assertTrue(Validation::ip('::1', 'IPv6'));
$this->assertTrue(Validation::ip('::ffff:12.34.56.78', 'IPv6'));
$this->assertTrue(Validation::ip('::ffff:0c22:384e', 'IPv6'));
$this->assertTrue(Validation::ip('2001:0db8:1234:0000:0000:0000:0000:0000', 'IPv6'));
$this->assertTrue(Validation::ip('2001:0db8:1234:ffff:ffff:ffff:ffff:ffff', 'IPv6'));
$this->assertTrue(Validation::ip('2001:db8:a::123', 'IPv6'));
$this->assertTrue(Validation::ip('fe80::', 'IPv6'));
$this->assertTrue(Validation::ip('::ffff:192.0.2.128', 'IPv6'));
$this->assertTrue(Validation::ip('::ffff:c000:280', 'IPv6'));
$this->assertFalse(Validation::ip('123', 'IPv6'));
$this->assertFalse(Validation::ip('ldkfj', 'IPv6'));
$this->assertFalse(Validation::ip('2001::FFD3::57ab', 'IPv6'));
$this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:37023:7334', 'IPv6'));
$this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:370k:7334', 'IPv6'));
$this->assertFalse(Validation::ip('1:2:3:4:5:6:7:8:9', 'IPv6'));
$this->assertFalse(Validation::ip('1::2::3', 'IPv6'));
$this->assertFalse(Validation::ip('1:::3:4:5', 'IPv6'));
$this->assertFalse(Validation::ip('1:2:3::4:5:6:7:8:9', 'IPv6'));
$this->assertFalse(Validation::ip('::ffff:2.3.4', 'IPv6'));
$this->assertFalse(Validation::ip('::ffff:257.1.2.3', 'IPv6'));
}
/** /**
* testMaxLength method * testMaxLength method
* *