Merge remote master branch

This commit is contained in:
misantron 2018-09-22 16:35:15 +03:00
commit f70e27af56
10 changed files with 388 additions and 115 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
composer.phar composer.phar
composer.lock composer.lock
vendor vendor
.idea/

View File

@ -1,21 +1,27 @@
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints # see http://about.travis-ci.org/docs/user/languages/php/ for more hints
language: php language: php
dist: precise
# list any PHP version you want to test against # list any PHP version you want to test against
php: php:
- 5.3
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0
- 7.1
- 7.2
- nightly
- hhvm - hhvm
matrix: matrix:
allow_failures: allow_failures:
- php: hhvm - php: hhvm
- php: nightly
include:
- php: 5.3
dist: precise
before_script: before_script:
- composer install - composer install
# Script to run tests # Script to run tests
script: ./vendor/bin/phpunit script: composer test

View File

@ -133,12 +133,15 @@ V::lang('ar');
* `in` - Performs in_array check on given array values * `in` - Performs in_array check on given array values
* `notIn` - Negation of `in` rule (not in array of values) * `notIn` - Negation of `in` rule (not in array of values)
* `ip` - Valid IP address * `ip` - Valid IP address
* `ipv4` - Valid IP v4 address
* `ipv6` - Valid IP v6 address
* `email` - Valid email address * `email` - Valid email address
* `emailDNS` - Valid email address with active DNS record * `emailDNS` - Valid email address with active DNS record
* `url` - Valid URL * `url` - Valid URL
* `urlActive` - Valid URL with active DNS record * `urlActive` - Valid URL with active DNS record
* `alpha` - Alphabetic characters only * `alpha` - Alphabetic characters only
* `alphaNum` - Alphabetic and numeric characters only * `alphaNum` - Alphabetic and numeric characters only
* `ascii` - ASCII characters only
* `slug` - URL slug characters (a-z, 0-9, -, \_) * `slug` - URL slug characters (a-z, 0-9, -, \_)
* `regex` - Field matches given regex pattern * `regex` - Field matches given regex pattern
* `date` - Field is a valid date * `date` - Field is a valid date

View File

@ -16,11 +16,19 @@
"php": ">=5.3.2" "php": ">=5.3.2"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.8.35" "phpunit/phpunit": "^4.8.35 || ^5.5 || ^6.5"
},
"suggest": {
"ext-mbstring": "It can support the multiple bytes string length."
}, },
"autoload": { "autoload": {
"psr-0": { "psr-4": {
"Valitron": "src/" "Valitron\\": "src/Valitron"
}
},
"autoload-dev": {
"psr-4": {
"Valitron\\": "tests/Valitron"
} }
}, },
"scripts": { "scripts": {

34
lang/ko.php Normal file
View File

@ -0,0 +1,34 @@
<?php
return array(
'required' => "을(를) 입력해야 합니다.",
'equals' => "은(는) '%s'와(과) 같아야 합니다.",
'different' => "은(는) '%s'와(과) 달라야 합니다.",
'accepted' => "을(를) 동의해야 합니다.",
'numeric' => "은(는) 숫자여야 합니다.",
'integer' => "은(는) 정수여야 합니다.",
'length' => "의 길이는 %d 이어야 합니다.",
'min' => "은(는) %s 이상이어야 합니다.",
'max' => "은(는) %s 이하여야 합니다.",
'in' => "은(는) 올바르지 않은 값을 포함하고 있습니다.",
'notIn' => "은(는) 올바르지 않은 값을 포함하고 있습니다.",
'ip' => "은(는) 올바르지 않은 IP입니다.",
'email' => "은(는) 올바르지 않은 email입니다.",
'url' => "은(는) 올바르지 않은 URL입니다.",
'urlActive' => "은(는) 접속 가능한 도메인이어야 합니다.",
'alpha' => "은(는) 영문자(a-z)로만 이루어져야 합니다.",
'alphaNum' => "은(는) 영문자(a-z)와 숫자(0-9)로만 이루어져야 합니다.",
'slug' => "은(는) 영문자(a-z)와 숫자(0-9), 특수문자 -와 _로만 이루어져야 합니다.",
'regex' => "은(는) 올바르지 않은 문자를 포함하고 있습니다.",
'date' => "은(는) 올바르지 않은 날짜입니다.",
'dateFormat' => "은(는) '%s' 형태의 날짜여야 합니다.",
'dateBefore' => "은(는) '%s' 보다 이전이어야 합니다.",
'dateAfter' => "은(는) '%s' 보다 이후여야 합니다.",
'contains' => "은(는) '%s'을(를) 포함해야 합니다.",
'boolean' => "은(는) boolean이어야 합니다.",
'lengthBetween' => "의 길이는 %d에서 %d 사이여야 합니다.",
'creditCard' => "은(는) 올바른 신용카드 번호가 아닙니다.",
'lengthMin' => "의 길이는 %d 이상이어야 합니다.",
'lengthMax' => "의 길이는 %d을(를) 넘을 수 없습니다.",
'instanceOf' => "는 '%s'의 객체여야 합니다."
);

View File

@ -15,4 +15,12 @@
<directory suffix="Test.php">tests/Valitron</directory> <directory suffix="Test.php">tests/Valitron</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
<blacklist>
<directory suffix=".php">vendor/</directory>
</blacklist>
</filter>
</phpunit> </phpunit>

View File

@ -1,4 +1,5 @@
<?php <?php
namespace Valitron; namespace Valitron;
/** /**
@ -77,6 +78,11 @@ class Validator
*/ */
protected $validUrlPrefixes = array('http://', 'https://', 'ftp://'); protected $validUrlPrefixes = array('http://', 'https://', 'ftp://');
/**
* @var bool
*/
protected $stop_on_first_fail = false;
/** /**
* Setup validation * Setup validation
* *
@ -244,7 +250,7 @@ class Validator
{ {
if (isset($params[0]) && (bool)$params[0]) { if (isset($params[0]) && (bool)$params[0]) {
//strict mode //strict mode
return preg_match('/^-?([0-9])+$/i', $value); return preg_match('/^([0-9]|-[1-9]|-?[1-9][0-9]*)$/i', $value);
} }
return filter_var($value, \FILTER_VALIDATE_INT) !== false; return filter_var($value, \FILTER_VALIDATE_INT) !== false;
@ -377,7 +383,6 @@ class Validator
* @param string $field * @param string $field
* @param mixed $value * @param mixed $value
* @param array $params * @param array $params
* @return bool * @return bool
*/ */
protected function validateBetween($field, $value, $params) protected function validateBetween($field, $value, $params)
@ -520,6 +525,30 @@ class Validator
return filter_var($value, \FILTER_VALIDATE_IP) !== false; return filter_var($value, \FILTER_VALIDATE_IP) !== false;
} }
/**
* Validate that a field is a valid IP v4 address
*
* @param string $field
* @param mixed $value
* @return bool
*/
protected function validateIpv4($field, $value)
{
return filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) !== false;
}
/**
* Validate that a field is a valid IP v6 address
*
* @param string $field
* @param mixed $value
* @return bool
*/
protected function validateIpv6($field, $value)
{
return filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6) !== false;
}
/** /**
* Validate that a field is a valid e-mail address * Validate that a field is a valid e-mail address
* *
@ -532,6 +561,24 @@ class Validator
return filter_var($value, \FILTER_VALIDATE_EMAIL) !== false; return filter_var($value, \FILTER_VALIDATE_EMAIL) !== false;
} }
/**
* Validate that a field contains only ASCII characters
*
* @param $field
* @param $value
* @return bool|false|string
*/
protected function validateAscii($field, $value)
{
// multibyte extension needed
if (function_exists('mb_detect_encoding')) {
return mb_detect_encoding($value, 'ASCII', true);
}
// fallback with regex
return 0 === preg_match('/[^\x00-\x7F]/', $value);
}
/** /**
* Validate that a field is a valid e-mail address and the domain name is active * Validate that a field is a valid e-mail address and the domain name is active
* *
@ -973,16 +1020,14 @@ class Validator
return array(null, array_key_exists($identifier, $data)); return array(null, array_key_exists($identifier, $data));
} }
return array(null, false); return array(null, false);
} } // Match array element
// Match array element
elseif (count($identifiers) === 0) { elseif (count($identifiers) === 0) {
if ($allow_empty) { if ($allow_empty) {
//when empty values are allowed, we only care if the key exists //when empty values are allowed, we only care if the key exists
return array(null, array_key_exists($identifier, $data)); return array(null, array_key_exists($identifier, $data));
} }
return array($data[$identifier], false); return array($data[$identifier], $allow_empty);
} } // We need to go deeper
// We need to go deeper
else { else {
return $this->getPart($data[$identifier], $identifiers, $allow_empty); return $this->getPart($data[$identifier], $identifiers, $allow_empty);
} }
@ -995,9 +1040,10 @@ class Validator
*/ */
public function validate() public function validate()
{ {
$set_to_break = false;
foreach ($this->_validations as $v) { foreach ($this->_validations as $v) {
foreach ($v['fields'] as $field) { foreach ($v['fields'] as $field) {
list($values, $multiple) = $this->getPart($this->_fields, explode('.', $field)); list($values, $multiple) = $this->getPart($this->_fields, explode('.', $field), false);
// Don't validate if the field is not required and the value is empty // Don't validate if the field is not required and the value is empty
if ($this->hasRule('optional', $field) && isset($values)) { if ($this->hasRule('optional', $field) && isset($values)) {
@ -1020,6 +1066,8 @@ class Validator
if (!$multiple) { if (!$multiple) {
$values = array($values); $values = array($values);
} else if (! $this->hasRule('required', $field)){
$values = array_filter($values);
} }
$result = true; $result = true;
@ -1029,13 +1077,27 @@ class Validator
if (!$result) { if (!$result) {
$this->error($field, $v['message'], $v['params']); $this->error($field, $v['message'], $v['params']);
if ($this->stop_on_first_fail) {
$set_to_break = true;
break;
} }
} }
} }
if ($set_to_break) break;
}
return count($this->errors()) === 0; return count($this->errors()) === 0;
} }
/**
* Should the validation stop a rule is failed
* @param bool $stop
*/
public function stopOnFirstFail($stop = true)
{
$this->stop_on_first_fail = (bool)$stop;
}
/** /**
* Returns all rule callbacks, the static and instance ones. * Returns all rule callbacks, the static and instance ones.
* *
@ -1169,7 +1231,8 @@ class Validator
// Get any other arguments passed to function // Get any other arguments passed to function
$params = array_slice(func_get_args(), 2); $params = array_slice(func_get_args(), 2);
if (is_callable($rule) && !(is_string($rule) && $this->hasValidator($rule))) { if (is_callable($rule)
&& !(is_string($rule) && $this->hasValidator($rule))) {
$name = $this->getUniqueRuleName($fields); $name = $this->getUniqueRuleName($fields);
$message = isset($params[0]) ? $params[0] : null; $message = isset($params[0]) ? $params[0] : null;
$this->addInstanceRule($name, $rule, $message); $this->addInstanceRule($name, $rule, $message);

View File

@ -0,0 +1,28 @@
<?php
use Valitron\Validator;
class StopOnFirstFail extends BaseTestCase {
public function testStopOnFirstFail() {
$rules = array(
'myField1' => array(
array('lengthMin', 5, 'message'=>'myField1 must be 5 characters minimum'),
array('url', 'message' => 'myField1 is not a valid url'),
array('urlActive', 'message' => 'myField1 is not an active url')
)
);
$v = new Validator(array(
'myField1' => 'myVal'
));
$v->mapFieldsRules($rules);
$v->stopOnFirstFail(true);
$this->assertFalse($v->validate());
$errors = $v->errors();
$this->assertCount(1, $errors['myField1']);
}
}

View File

@ -1,4 +1,5 @@
<?php <?php
use Valitron\Validator; use Valitron\Validator;
class ValidateTest extends BaseTestCase class ValidateTest extends BaseTestCase
@ -15,12 +16,13 @@ class ValidateTest extends BaseTestCase
$this->assertEquals($v->data(), array('foo' => 'bar')); $this->assertEquals($v->data(), array('foo' => 'bar'));
} }
public function testAccurateErrorCount() public function testAccurateErrorShouldReturnFalse()
{ {
$v = new Validator(array('name' => 'Chester Tester')); $v = new Validator(array('name' => 'Chester Tester'));
$v->rule('required', 'name'); $v->rule('required', 'name');
$this->assertSame(1, count($v->errors('name'))); $this->assertFalse($v->errors('name'));
} }
public function testArrayOfFieldsToValidate() public function testArrayOfFieldsToValidate()
{ {
$v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com')); $v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com'));
@ -158,7 +160,8 @@ class ValidateTest extends BaseTestCase
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
} }
public function testAcceptedNotSet(){ public function testAcceptedNotSet()
{
$v = new Validator(); $v = new Validator();
$v->rule('accepted', 'agree'); $v->rule('accepted', 'agree');
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
@ -189,7 +192,8 @@ class ValidateTest extends BaseTestCase
$this->assertTrue($v->validate()); $this->assertTrue($v->validate());
} }
public function testIntegerStrict(){ public function testIntegerStrict()
{
$v = new Validator(array('num' => ' 41243')); $v = new Validator(array('num' => ' 41243'));
$v->rule('integer', 'num'); $v->rule('integer', 'num');
@ -203,11 +207,37 @@ class ValidateTest extends BaseTestCase
$v->rule('integer', 'num'); $v->rule('integer', 'num');
$this->assertTrue($v->validate()); $this->assertTrue($v->validate());
$v = new Validator(array('num' => '+41243')); $v = new Validator(array('num' => '+41243'));
$v->rule('integer', 'num', true); $v->rule('integer', 'num', true);
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
$v = new Validator(array('num' => '-1'));
$v->rule('integer', 'num', true);
$this->assertTrue($v->validate());
$v = new Validator(array('num' => '-0'));
$v->rule('integer', 'num', true);
$this->assertFalse($v->validate());
$v = new Validator(array('num' => '0'));
$v->rule('integer', 'num', true);
$this->assertTrue($v->validate());
$v = new Validator(array('num' => '+0'));
$v->rule('integer', 'num', true);
$this->assertFalse($v->validate());
$v = new Validator(array('num' => '+1'));
$v->rule('integer', 'num', true);
$this->assertFalse($v->validate());
$v = new Validator(array('num' => '0123'));
$v->rule('integer', 'num', true);
$this->assertFalse($v->validate());
$v = new Validator(array('num' => '-0123'));
$v->rule('integer', 'num', true);
$this->assertFalse($v->validate());
} }
public function testIntegerInvalid() public function testIntegerInvalid()
@ -216,7 +246,6 @@ class ValidateTest extends BaseTestCase
$v->rule('integer', 'num'); $v->rule('integer', 'num');
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
$v = new Validator(array('num' => '--1231')); $v = new Validator(array('num' => '--1231'));
$v->rule('integer', 'num'); $v->rule('integer', 'num');
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
@ -592,6 +621,20 @@ class ValidateTest extends BaseTestCase
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
} }
public function testAsciiValid()
{
$v = new Validator(array('text' => '12345 abcde'));
$v->rule('ascii', 'text');
$this->assertTrue($v->validate());
}
public function testAsciiInvalid()
{
$v = new Validator(array('text' => '12345 abcdé'));
$v->rule('ascii', 'text');
$this->assertFalse($v->validate());
}
public function testIpValid() public function testIpValid()
{ {
$v = new Validator(array('ip' => '127.0.0.1')); $v = new Validator(array('ip' => '127.0.0.1'));
@ -606,6 +649,34 @@ class ValidateTest extends BaseTestCase
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
} }
public function testIpv4Valid()
{
$v = new Validator(array('ip' => '127.0.0.1'));
$v->rule('ipv4', 'ip');
$this->assertTrue($v->validate());
}
public function testIpv4Invalid()
{
$v = new Validator(array('ip' => 'FE80::0202:B3FF:FE1E:8329'));
$v->rule('ipv4', 'ip');
$this->assertFalse($v->validate());
}
public function testIpv6Valid()
{
$v = new Validator(array('ip' => 'FE80::0202:B3FF:FE1E:8329'));
$v->rule('ipv6', 'ip');
$this->assertTrue($v->validate());
}
public function testIpv6Invalid()
{
$v = new Validator(array('ip' => '127.0.0.1'));
$v->rule('ipv6', 'ip');
$this->assertFalse($v->validate());
}
public function testEmailValid() public function testEmailValid()
{ {
$v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com')); $v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com'));
@ -620,13 +691,15 @@ class ValidateTest extends BaseTestCase
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
} }
public function testEmailDnsValid(){ public function testEmailDnsValid()
{
$v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com')); $v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com'));
$v->rule('emailDNS', 'email'); $v->rule('emailDNS', 'email');
$this->assertTrue($v->validate()); $this->assertTrue($v->validate());
} }
public function testEmailDnsInvalid(){ public function testEmailDnsInvalid()
{
$v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.zyx')); $v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.zyx'));
$v->rule('emailDNS', 'email'); $v->rule('emailDNS', 'email');
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
@ -1059,7 +1132,8 @@ class ValidateTest extends BaseTestCase
$this->assertEquals($v1->errors(), $v2->errors()); $this->assertEquals($v1->errors(), $v2->errors());
} }
public function testMalformedBulkRules(){ public function testMalformedBulkRules()
{
$v = new Validator(); $v = new Validator();
$v->rules( $v->rules(
array( array(
@ -1117,7 +1191,9 @@ class ValidateTest extends BaseTestCase
public function testAddRuleClosure() public function testAddRuleClosure()
{ {
$v = new Validator(array('name' => 'Chester Tester')); $v = new Validator(array('name' => 'Chester Tester'));
$v->addRule('testRule', function() { return true; }); $v->addRule('testRule', function () {
return true;
});
$v->rule('testRule', 'name'); $v->rule('testRule', 'name');
$this->assertTrue($v->validate()); $this->assertTrue($v->validate());
} }
@ -1125,7 +1201,9 @@ class ValidateTest extends BaseTestCase
public function testAddRuleClosureReturnsFalse() public function testAddRuleClosureReturnsFalse()
{ {
$v = new Validator(array('name' => 'Chester Tester')); $v = new Validator(array('name' => 'Chester Tester'));
$v->addRule('testRule', function() { return false; }); $v->addRule('testRule', function () {
return false;
});
$v->rule('testRule', 'name'); $v->rule('testRule', 'name');
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
} }
@ -1133,7 +1211,9 @@ class ValidateTest extends BaseTestCase
public function testAddRuleClosureWithFieldArray() public function testAddRuleClosureWithFieldArray()
{ {
$v = new Validator(array('name' => 'Chester Tester', 'email' => 'foo@example.com')); $v = new Validator(array('name' => 'Chester Tester', 'email' => 'foo@example.com'));
$v->addRule('testRule', function() { return true; }); $v->addRule('testRule', function () {
return true;
});
$v->rule('testRule', array('name', 'email')); $v->rule('testRule', array('name', 'email'));
$this->assertTrue($v->validate()); $this->assertTrue($v->validate());
} }
@ -1141,7 +1221,9 @@ class ValidateTest extends BaseTestCase
public function testAddRuleClosureWithArrayAsExtraParameter() public function testAddRuleClosureWithArrayAsExtraParameter()
{ {
$v = new Validator(array('name' => 'Chester Tester')); $v = new Validator(array('name' => 'Chester Tester'));
$v->addRule('testRule', function() { return true; }); $v->addRule('testRule', function () {
return true;
});
$v->rule('testRule', 'name', array('foo', 'bar')); $v->rule('testRule', 'name', array('foo', 'bar'));
$this->assertTrue($v->validate()); $this->assertTrue($v->validate());
} }
@ -1154,8 +1236,16 @@ class ValidateTest extends BaseTestCase
$this->assertTrue($v->validate()); $this->assertTrue($v->validate());
} }
public function sampleObjectCallback() { return true; } public function sampleObjectCallback()
public function sampleObjectCallbackFalse() { return false; } {
return true;
}
public function sampleObjectCallbackFalse()
{
return false;
}
public function testAddRuleCallbackArray() public function testAddRuleCallbackArray()
{ {
$v = new Validator(array('name' => 'Chester Tester')); $v = new Validator(array('name' => 'Chester Tester'));
@ -1522,6 +1612,38 @@ class ValidateTest extends BaseTestCase
$this->assertFalse($v->validate()); $this->assertFalse($v->validate());
} }
/**
* @see https://github.com/vlucas/valitron/issues/262
*/
public function testOptionalArrayPartsAreIgnored()
{
$v = new Validator(array(
'data' => array(
array('foo' => '2018-01-01'),
array('bar' => 1)
)
)
);
$v->rule('date', 'data.*.foo');
$this->assertTrue($v->validate());
}
/**
* @see https://github.com/vlucas/valitron/issues/262
*/
public function testRequiredArrayPartsAreNotIgnored()
{
$v = new Validator(array(
'data' => array(
array('foo' => '2018-01-01'),
array('bar' => 1)
)
)
);
$v->rule('required', 'data.*.foo');
$v->rule('date', 'data.*.foo');
$this->assertFalse($v->validate());
}
} }
function sampleFunctionCallback($field, $value, array $params) function sampleFunctionCallback($field, $value, array $params)