From 69be152f3a58b23805d2e4d1e64edc284e3351d5 Mon Sep 17 00:00:00 2001 From: misantron Date: Thu, 24 Mar 2016 22:08:52 +0300 Subject: [PATCH 01/15] English and russian translations fix --- lang/en.php | 10 +++++----- lang/ru.php | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lang/en.php b/lang/en.php index 57053eb..6dba5a5 100644 --- a/lang/en.php +++ b/lang/en.php @@ -7,14 +7,14 @@ return array( 'accepted' => "must be accepted", 'numeric' => "must be numeric", 'integer' => "must be an integer (0-9)", - 'length' => "must be at least %d long", + 'length' => "must be %d characters long", 'min' => "must be at least %s", 'max' => "must be no more than %s", 'in' => "contains invalid value", 'notIn' => "contains invalid value", 'ip' => "is not a valid IP address", 'email' => "is not a valid email address", - 'url' => "not a URL", + 'url' => "is not a valid URL", 'urlActive' => "must be an active domain", 'alpha' => "must contain only letters a-z", 'alphaNum' => "must contain only letters a-z and/or numbers 0-9", @@ -28,7 +28,7 @@ return array( 'boolean' => "must be a boolean", 'lengthBetween' => "must be between %d and %d characters", 'creditCard' => "must be a valid credit card number", - "lengthMin" => "must contain greater than %d characters", - "lengthMax" => "must contain less than %d characters", - "instanceOf" => "must be an instance of '%s'" + 'lengthMin' => "must at least %d characters long", + 'lengthMax' => "must not exceed %d characters", + 'instanceOf' => "must be an instance of '%s'" ); diff --git a/lang/ru.php b/lang/ru.php index 78fe98f..2ce1cad 100644 --- a/lang/ru.php +++ b/lang/ru.php @@ -6,7 +6,7 @@ return array( 'different' => "должно отличаться от '%s'", 'accepted' => "должно быть указано", 'numeric' => "должно содержать числовое значение", - 'integer' => "должно быть числом", + 'integer' => "должно быть числом (0-9)", 'length' => "должно быть длиннее, чем %d", 'min' => "должно быть больше, чем %s", 'max' => "должно быть меньше, чем %s", @@ -14,7 +14,7 @@ return array( 'notIn' => "содержит неверное значение", 'ip' => "не является валидным IP адресом", 'email' => "не является валидным email адресом", - 'url' => "не является ссылкой", + 'url' => "не является валидной ссылкой", 'urlActive' => "содержит не активную ссылку", 'alpha' => "должно содержать только латинские символы", 'alphaNum' => "должно содержать только латинские символы и/или цифры", @@ -28,6 +28,7 @@ return array( 'boolean' => "должно содержать логическое значение", 'lengthBetween' => "должно содержать от %d до %d символов", 'creditCard' => "должно быть номером кредитной карты", - "lengthMin" => "должно содержать более %d символов", - "lengthMax" => "должно содержать менее %d символов" + 'lengthMin' => "должно содержать более %d символов", + 'lengthMax' => "должно содержать менее %d символов", + 'instanceOf' => "должно быть объектом класса '%s'" ); From d3660fd6f179054cf956bfd196de55c704e4876e Mon Sep 17 00:00:00 2001 From: "Martin Gazdag [office]" Date: Wed, 29 Jun 2016 15:32:56 +0200 Subject: [PATCH 02/15] Added Slovak language. --- lang/sk.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 lang/sk.php diff --git a/lang/sk.php b/lang/sk.php new file mode 100644 index 0000000..8894716 --- /dev/null +++ b/lang/sk.php @@ -0,0 +1,34 @@ + "je povinná položka", + 'equals' => "musí byť rovnaký ako '%s'", + 'different' => "musí byť rôzny od '%s'", + 'accepted' => "musí byť akceptovaný", + 'numeric' => "musí byť číslo", + 'integer' => "musí byť celé číslo (0-9)", + 'length' => "musí byť dlhý aspoň %d", + 'min' => "musí byť dlhý minimálne %s", + 'max' => "musí byť maximálne %s", + 'in' => "obsahuje nepovolenú hodnotu", + 'notIn' => "obsahuje nepovolenú hodnotu", + 'ip' => "nie je korektná IP adresa", + 'email' => "nie je korektný e-mail", + 'url' => "nie je URL", + 'urlActive' => "musí byť aktívna URL", + 'alpha' => "musí obsahovať len písmená a-z", + 'alphaNum' => "musí obsahovať len písmená a-z a/alebo čísla 0-9", + 'slug' => "musí obsahovať len písmená a-z, čísla 0-9, pomlčky alebo podtržítka", + 'regex' => "obsahuje nepovolené znaky", + 'date' => "nie je korektný formáť", + 'dateFormat' => "musí byť dátum formátu '%s'", + 'dateBefore' => "musí byť dátum pred '%s'", + 'dateAfter' => "musí byť dátum po '%s'", + 'contains' => "musí obsahovať %s", + 'boolean' => "musí byť pravdivostná hodnota (boolean)", + 'lengthBetween' => "musí byť %d až %d znakov dlhý", + 'creditCard' => "musí byť korektné číslo kreditnej karty", + "lengthMin" => "musí byť aspoň %d znakov dlhý", + "lengthMax" => "musí byť najviac %d znakov dlhý", + "instanceOf" => "musí byť inštanciou '%s'" +); From a9169771ef1b25b619cc4412576b504b9356d66b Mon Sep 17 00:00:00 2001 From: Helge Sverre Date: Sun, 24 Jul 2016 11:23:39 +0200 Subject: [PATCH 03/15] Added norwegian language file --- lang/no.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 lang/no.php diff --git a/lang/no.php b/lang/no.php new file mode 100644 index 0000000..44a8304 --- /dev/null +++ b/lang/no.php @@ -0,0 +1,34 @@ + "er nødvendig", + 'equals' => "må være de samme som '%s'", + 'different' => "må være annerledes enn '%s'", + 'accepted' => "må aksepteres", + 'numeric' => "må være numerisk", + 'integer' => "må være et heltall (0-9)", + 'length' => "må være %d tegn", + 'min' => "må være minst %s", + 'max' => "må ikke være mer enn %s", + 'in' => "inneholder ugyldig verdi", + 'notIn' => "inneholder ugyldig verdi", + 'ip' => "er ikkje ein gyldig IP Adresse", + 'email' => "er ikkje ein gyldig E-post adresse", + 'url' => "er ikkje ein gyldig URL", + 'urlActive' => "må være eit aktivt domene", + 'alpha' => "må bare innholde bokstaver a-z", + 'alphaNum' => "må bare innholde bokstaver a-z og/eller tall 0-9", + 'slug' => "må bare innholde bokstaver a-z og/eller tall 0-9, bindestreker og understreker", + 'regex' => "inneholder ulovlige tegn", + 'date' => "er ikkje ein gylid dato", + 'dateFormat' => "må være ein dato med formatet '%s'", + 'dateBefore' => "må være ein dato før '%s'", + 'dateAfter' => "må være ein dato etter '%s'", + 'contains' => "må inneholde %s", + 'boolean' => "må være ein boolsk verdi", + 'lengthBetween' => "må være imellom %d og %d tegn", + 'creditCard' => "må være et gyldig kredittkortnummer", + 'lengthMin' => "må være minst %d tegn", + 'lengthMax' => "må ikkje overstige %d tegn", + 'instanceOf' => "må være ein instans av '%s'" +); From fc6f5c138e256a974395bed353cc347fd38f746f Mon Sep 17 00:00:00 2001 From: Willem Wollebrants Date: Sun, 31 Jul 2016 11:57:12 +0200 Subject: [PATCH 04/15] Update en.php add a verb to the lengthMin error --- lang/en.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.php b/lang/en.php index 6dba5a5..3765c96 100644 --- a/lang/en.php +++ b/lang/en.php @@ -28,7 +28,7 @@ return array( 'boolean' => "must be a boolean", 'lengthBetween' => "must be between %d and %d characters", 'creditCard' => "must be a valid credit card number", - 'lengthMin' => "must at least %d characters long", + 'lengthMin' => "must be at least %d characters long", 'lengthMax' => "must not exceed %d characters", 'instanceOf' => "must be an instance of '%s'" ); From 94317636d96cc76f5f18de3d77b3c006777ffa5d Mon Sep 17 00:00:00 2001 From: Willem Wollebrants Date: Wed, 17 Aug 2016 21:44:17 +0200 Subject: [PATCH 05/15] Added withData method for immutable reusability --- src/Valitron/Validator.php | 15 +++++++++++++++ tests/Valitron/ValidateTest.php | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 4496e2d..7c76738 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -1085,4 +1085,19 @@ class Validator } } } + + /** + * Replace data on cloned instance + * + * @param array $data + * @param array $fields + * @return Valitron + */ + public function withData($data, $fields = array()) + { + $clone = clone $this; + $clone->reset(); + $clone->_fields = !empty($fields) ? array_intersect_key($data, array_flip($fields)) : $data; + return $clone; + } } diff --git a/tests/Valitron/ValidateTest.php b/tests/Valitron/ValidateTest.php index ac3ffca..459e5c1 100644 --- a/tests/Valitron/ValidateTest.php +++ b/tests/Valitron/ValidateTest.php @@ -1155,7 +1155,20 @@ class ValidateTest extends BaseTestCase $v = new Validator(array()); $v->rule('optional', 'address')->rule('email', 'address'); $this->assertTrue($v->validate()); - } + } + + public function testWithData() + { + $v = new Validator(array()); + $v->rule('required', 'name'); + //validation failed, so must have errors + $this->assertFalse($v->validate()); + $this->assertNotEmpty($v->errors()); + //create copy with different data + $v2 = $v->withData(array('name' => 'Chester Tester')); + $this->assertTrue($v2->validate()); + $this->assertEmpty($v2->errors()); + } } function sampleFunctionCallback($field, $value, array $params) { From df78fe188c53cf29ffae7773e899fd9318113c9a Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Sun, 6 Nov 2016 01:46:17 +0100 Subject: [PATCH 06/15] Added support for instance rules Instance rules are not shared among other validator objects (thus they are not static). This is useful if you have a rule you do not want your other validators to use (because it is very special to this validator). The parameter given to addInstanceRule is identically to addRule: public function addInstanceRule(string $name, callable $callback [, string message]) Instead of appending to the static variables $_rules, and $_ruleMessages, it now appends to $_instanceRules and $_instanceRuleMessages. A new getter for the rules and ruleMessage has also been added: public array getRules() public array getRuleMessages() All existing code has been updated to use getRules(), and getRuleMessages() instead of $_rules, and $_ruleMessages. --- src/Valitron/Validator.php | 78 +++++++++++++++++-- .../Valitron/ValidateAddInstanceRuleTest.php | 56 +++++++++++++ 2 files changed, 126 insertions(+), 8 deletions(-) create mode 100644 tests/Valitron/ValidateAddInstanceRuleTest.php diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 7c76738..f57cc08 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -37,6 +37,21 @@ class Validator */ protected $_labels = array(); + /** + * Contains all rules that are available to the current valitron instance. + * + * @var array + */ + protected $_instanceRules = array(); + + /** + * Contains all rule messages that are available to the current valitron + * instance + * + * @var array + */ + protected $_instanceRuleMessage = array(); + /** * @var string */ @@ -503,7 +518,7 @@ class Validator foreach ($this->validUrlPrefixes as $prefix) { if (strpos($value, $prefix) !== false) { $host = parse_url(strtolower($value), PHP_URL_HOST); - + return checkdnsrr($host, 'A') || checkdnsrr($host, 'AAAA') || checkdnsrr($host, 'CNAME'); } } @@ -921,8 +936,9 @@ class Validator } // Callback is user-specified or assumed method on class - if (isset(static::$_rules[$v['rule']])) { - $callback = static::$_rules[$v['rule']]; + $errors = $this->getRules(); + if (isset($errors[$v['rule']])) { + $callback = $errors[$v['rule']]; } else { $callback = array($this, 'validate' . ucfirst($v['rule'])); } @@ -945,6 +961,26 @@ class Validator return count($this->errors()) === 0; } + /** + * Returns all rule callbacks, the static and instance ones. + * + * @return array + */ + protected function getRules() + { + return array_merge($this->_instanceRules, static::$_rules); + } + + /** + * Returns all rule message, the static and instance ones. + * + * @return array + */ + protected function getRuleMessages() + { + return array_merge($this->_instanceRuleMessage, static::$_ruleMessages); + } + /** * Determine whether a field is being validated by the given rule. * @@ -952,6 +988,7 @@ class Validator * @param string $field The name of the field * @return boolean */ + protected function hasRule($name, $field) { foreach ($this->_validations as $validation) { @@ -965,6 +1002,31 @@ class Validator return false; } + protected static function assertRuleCallback($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Second argument must be a valid callback. Given argument was not callable.'); + } + } + + + /** + * Adds a new validation rule callback that is tied to the current + * instance only. + * + * @param string $name + * @param mixed $callback + * @param string $message + * @throws \InvalidArgumentException + */ + public function addInstanceRule($name, $callback, $message = self::ERROR_DEFAULT) + { + static::assertRuleCallback($callback); + + $this->_instanceRules[$name] = $callback; + $this->_instanceRuleMessage[$name] = $message; + } + /** * Register new validation rule callback * @@ -975,9 +1037,7 @@ class Validator */ public static function addRule($name, $callback, $message = self::ERROR_DEFAULT) { - if (!is_callable($callback)) { - throw new \InvalidArgumentException('Second argument must be a valid callback. Given argument was not callable.'); - } + static::assertRuleCallback($callback); static::$_rules[$name] = $callback; static::$_ruleMessages[$name] = $message; @@ -993,7 +1053,8 @@ class Validator */ public function rule($rule, $fields) { - if (!isset(static::$_rules[$rule])) { + $errors = $this->getRules(); + if (!isset($errors[$rule])) { $ruleMethod = 'validate' . ucfirst($rule); if (!method_exists($this, $ruleMethod)) { throw new \InvalidArgumentException("Rule '" . $rule . "' has not been registered with " . __CLASS__ . "::addRule()."); @@ -1001,7 +1062,8 @@ class Validator } // Ensure rule has an accompanying message - $message = isset(static::$_ruleMessages[$rule]) ? static::$_ruleMessages[$rule] : self::ERROR_DEFAULT; + $msgs = $this->getRuleMessages(); + $message = isset($msgs[$rule]) ? $msgs[$rule] : self::ERROR_DEFAULT; // Get any other arguments passed to function $params = array_slice(func_get_args(), 2); diff --git a/tests/Valitron/ValidateAddInstanceRuleTest.php b/tests/Valitron/ValidateAddInstanceRuleTest.php new file mode 100644 index 0000000..796902c --- /dev/null +++ b/tests/Valitron/ValidateAddInstanceRuleTest.php @@ -0,0 +1,56 @@ +validate(); + foreach ($v->errors() as $label => $messages) + { + foreach ($messages as $theMessage) + { + $msg .= "\n\t{$label}: {$theMessage}"; + } + } + + $this->assertTrue($v->validate(), $msg); + } + + public function testAddInstanceRule() + { + $v = new Validator(array( + "foo" => "bar", + "fuzz" => "bazz", + )); + + $v->addInstanceRule("fooRule", function($field, $value) + { + return $field !== "foo" || $value !== "barz"; + }); + + Validator::addRule("fuzzerRule", function($field, $value) + { + return $field !== "fuzz" || $value === "bazz"; + }); + + $v->rule("required", array("foo", "fuzz")); + $v->rule("fuzzerRule", "fuzz"); + $v->rule("fooRule", "foo"); + + + $this->assertValid($v); + } + + public function testAddInstanceRuleFail() + { + $v = new Validator(array("foo" => "bar")); + $v->addInstanceRule("fooRule", function($field) + { + return $field === "for"; + }); + $v->rule("fooRule", "foo"); + $this->assertFalse($v->validate()); + } +} From 7a5fd669906258d66a4657d543dbc84a7f120c25 Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Sun, 6 Nov 2016 01:46:25 +0100 Subject: [PATCH 07/15] Validator::rule now supports closures Validator::rule now allows the rule to be a closure. This allows us to add an anonymous rule that is only valid for the specified field(s): $someObject = new MyObject(); $v->rule(function($field, $value) use ($someObject) return $value === $someObject->someState(); }, ["shared_secret", "other"]) ->label("invalid state!"); This is especially useful if you have either a) have a rule which you are not likely to be reusing or b) you need access to some object which cannot be easily connstructed (because it requires IO access, etc). Either way, this is really only convenience method because it saves you a call to Validator::addInstanceRule. Internally, it does exactly that: it 1) determines a valid rule name, 2) calls addInstanceRule() with the name determined in 1) and the Closure, and then 3) replaces the $rule parameter (which is the Closure object) by the name of the rule. 1) is determined like this: if multiple parameters were given, they are joined together into one string glued with a underscore. '_rule' is then appended so that the name becomes all_params_rule. If the name is already used by any other rule, it will append up to 5 pseudo-random digits to the rule. In the unlikely event that this rule is also used, step 1) will be repeated until a unique rule name is found. --- src/Valitron/Validator.php | 48 ++++++++++++++++--- .../Valitron/ValidateAddInstanceRuleTest.php | 47 +++++++++++++++++- 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index f57cc08..54f6bf5 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -1019,7 +1019,7 @@ class Validator * @param string $message * @throws \InvalidArgumentException */ - public function addInstanceRule($name, $callback, $message = self::ERROR_DEFAULT) + public function addInstanceRule($name, $callback, $message = null) { static::assertRuleCallback($callback); @@ -1035,24 +1035,63 @@ class Validator * @param string $message * @throws \InvalidArgumentException */ - public static function addRule($name, $callback, $message = self::ERROR_DEFAULT) + public static function addRule($name, $callback, $message = null) { + if ($message === null) + { + $message = static::ERROR_DEFAULT; + } + static::assertRuleCallback($callback); static::$_rules[$name] = $callback; static::$_ruleMessages[$name] = $message; } + public function getUniqueRuleName($fields) + { + if (is_array($fields)) + { + $fields = implode("_", $fields); + } + + $orgName = "{$fields}_rule"; + $name = $orgName; + $rules = $this->getRules(); + while (isset($rules[$name])) + { + $name = $orgName . "_" . rand(0, 10000); + } + + return $name; + } + /** * Convenience method to add a single validation rule * - * @param string $rule + * @param string|callback $rule * @param array $fields * @return $this * @throws \InvalidArgumentException */ public function rule($rule, $fields) { + // Get any other arguments passed to function + $params = array_slice(func_get_args(), 2); + + // Note: we cannot use is_callable here since max, int, and many + // other string can also be callables but aren't really rule callbacks. + // + // If a closure is used, we can be sure that the user actually + // wants $rule to be a custom rule check. + if (is_object($rule) && ($rule instanceof \Closure)) + { + $name = $this->getUniqueRuleName($fields); + $msg = isset($params[0]) ? $params[0] : null; + $this->addInstanceRule($name, $rule, $msg); + $rule = $name; + } + $errors = $this->getRules(); if (!isset($errors[$rule])) { $ruleMethod = 'validate' . ucfirst($rule); @@ -1065,9 +1104,6 @@ class Validator $msgs = $this->getRuleMessages(); $message = isset($msgs[$rule]) ? $msgs[$rule] : self::ERROR_DEFAULT; - // Get any other arguments passed to function - $params = array_slice(func_get_args(), 2); - $this->_validations[] = array( 'rule' => $rule, 'fields' => (array) $fields, diff --git a/tests/Valitron/ValidateAddInstanceRuleTest.php b/tests/Valitron/ValidateAddInstanceRuleTest.php index 796902c..c1f759e 100644 --- a/tests/Valitron/ValidateAddInstanceRuleTest.php +++ b/tests/Valitron/ValidateAddInstanceRuleTest.php @@ -39,7 +39,6 @@ class ValidateAddInstanceRuleTest extends BaseTestCase $v->rule("fuzzerRule", "fuzz"); $v->rule("fooRule", "foo"); - $this->assertValid($v); } @@ -53,4 +52,50 @@ class ValidateAddInstanceRuleTest extends BaseTestCase $v->rule("fooRule", "foo"); $this->assertFalse($v->validate()); } + + public function testAddAddRuleWithCallback() + { + $v = new Validator(array("foo" => "bar")); + $v->rule(function($field, $value) { + return $field === "foo" && $value === "bar"; + }, "foo"); + + $this->assertValid($v); + } + + public function testAddAddRuleWithCallbackFail() + { + $v = new Validator(array("foo" => "baz")); + $v->rule(function($field, $value) { + return $field === "foo" && $value === "bar"; + }, "foo"); + + $this->assertFalse($v->validate()); + } + + public function testAddAddRuleWithCallbackFailMessage() + { + $v = new Validator(array("foo" => "baz")); + $v->rule(function($field, $value) { + return $field === "foo" && $value === "bar"; + }, "foo", "test error message"); + + $this->assertFalse($v->validate()); + $errors = $v->errors(); + $this->assertArrayHasKey("foo", $errors); + $this->assertCount(1, $errors["foo"]); + $this->assertEquals("Foo test error message", $errors["foo"][0]); + } + + public function testUniqueRuleName() + { + $v = new Validator(array()); + $args = array("foo", "bar"); + $this->assertEquals("foo_bar_rule", $v->getUniqueRuleName($args)); + $this->assertEquals("foo_rule", $v->getUniqueRuleName("foo")); + + $v->addInstanceRule("foo_rule", function() {}); + $u = $v->getUniqueRuleName("foo"); + $this->assertRegExp("/^foo_rule_[0-9]{1,5}$/", $u); + } } From 74a882969ecaa4e8d4deb1c2259274efa68ac0a8 Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Sun, 6 Nov 2016 01:46:28 +0100 Subject: [PATCH 08/15] Indent fixed --- src/Valitron/Validator.php | 64 +++---- .../Valitron/ValidateAddInstanceRuleTest.php | 158 +++++++++--------- 2 files changed, 111 insertions(+), 111 deletions(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 54f6bf5..20dc88e 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -1037,10 +1037,10 @@ class Validator */ public static function addRule($name, $callback, $message = null) { - if ($message === null) - { - $message = static::ERROR_DEFAULT; - } + if ($message === null) + { + $message = static::ERROR_DEFAULT; + } static::assertRuleCallback($callback); @@ -1048,23 +1048,23 @@ class Validator static::$_ruleMessages[$name] = $message; } - public function getUniqueRuleName($fields) - { - if (is_array($fields)) - { - $fields = implode("_", $fields); - } + public function getUniqueRuleName($fields) + { + if (is_array($fields)) + { + $fields = implode("_", $fields); + } - $orgName = "{$fields}_rule"; - $name = $orgName; - $rules = $this->getRules(); - while (isset($rules[$name])) - { - $name = $orgName . "_" . rand(0, 10000); - } + $orgName = "{$fields}_rule"; + $name = $orgName; + $rules = $this->getRules(); + while (isset($rules[$name])) + { + $name = $orgName . "_" . rand(0, 10000); + } - return $name; - } + return $name; + } /** * Convenience method to add a single validation rule @@ -1079,19 +1079,19 @@ class Validator // Get any other arguments passed to function $params = array_slice(func_get_args(), 2); - // Note: we cannot use is_callable here since max, int, and many - // other string can also be callables but aren't really rule callbacks. - // - // If a closure is used, we can be sure that the user actually - // wants $rule to be a custom rule check. - if (is_object($rule) && ($rule instanceof \Closure)) - { - $name = $this->getUniqueRuleName($fields); - $msg = isset($params[0]) ? $params[0] : null; - $this->addInstanceRule($name, $rule, $msg); - $rule = $name; - } - + // Note: we cannot use is_callable here since max, int, and many + // other string can also be callables but aren't really rule callbacks. + // + // If a closure is used, we can be sure that the user actually + // wants $rule to be a custom rule check. + if (is_object($rule) && ($rule instanceof \Closure)) + { + $name = $this->getUniqueRuleName($fields); + $msg = isset($params[0]) ? $params[0] : null; + $this->addInstanceRule($name, $rule, $msg); + $rule = $name; + } + $errors = $this->getRules(); if (!isset($errors[$rule])) { $ruleMethod = 'validate' . ucfirst($rule); diff --git a/tests/Valitron/ValidateAddInstanceRuleTest.php b/tests/Valitron/ValidateAddInstanceRuleTest.php index c1f759e..cc10d0b 100644 --- a/tests/Valitron/ValidateAddInstanceRuleTest.php +++ b/tests/Valitron/ValidateAddInstanceRuleTest.php @@ -3,99 +3,99 @@ use Valitron\Validator; class ValidateAddInstanceRuleTest extends BaseTestCase { - protected function assertValid($v) - { - $msg = "\tErrors:\n"; - $status = $v->validate(); - foreach ($v->errors() as $label => $messages) - { - foreach ($messages as $theMessage) - { - $msg .= "\n\t{$label}: {$theMessage}"; - } - } + protected function assertValid($v) + { + $msg = "\tErrors:\n"; + $status = $v->validate(); + foreach ($v->errors() as $label => $messages) + { + foreach ($messages as $theMessage) + { + $msg .= "\n\t{$label}: {$theMessage}"; + } + } - $this->assertTrue($v->validate(), $msg); - } + $this->assertTrue($v->validate(), $msg); + } - public function testAddInstanceRule() - { - $v = new Validator(array( - "foo" => "bar", - "fuzz" => "bazz", - )); + public function testAddInstanceRule() + { + $v = new Validator(array( + "foo" => "bar", + "fuzz" => "bazz", + )); - $v->addInstanceRule("fooRule", function($field, $value) - { - return $field !== "foo" || $value !== "barz"; - }); + $v->addInstanceRule("fooRule", function($field, $value) + { + return $field !== "foo" || $value !== "barz"; + }); - Validator::addRule("fuzzerRule", function($field, $value) - { - return $field !== "fuzz" || $value === "bazz"; - }); + Validator::addRule("fuzzerRule", function($field, $value) + { + return $field !== "fuzz" || $value === "bazz"; + }); - $v->rule("required", array("foo", "fuzz")); - $v->rule("fuzzerRule", "fuzz"); - $v->rule("fooRule", "foo"); + $v->rule("required", array("foo", "fuzz")); + $v->rule("fuzzerRule", "fuzz"); + $v->rule("fooRule", "foo"); - $this->assertValid($v); - } + $this->assertValid($v); + } - public function testAddInstanceRuleFail() - { - $v = new Validator(array("foo" => "bar")); - $v->addInstanceRule("fooRule", function($field) - { - return $field === "for"; - }); - $v->rule("fooRule", "foo"); - $this->assertFalse($v->validate()); - } + public function testAddInstanceRuleFail() + { + $v = new Validator(array("foo" => "bar")); + $v->addInstanceRule("fooRule", function($field) + { + return $field === "for"; + }); + $v->rule("fooRule", "foo"); + $this->assertFalse($v->validate()); + } - public function testAddAddRuleWithCallback() - { - $v = new Validator(array("foo" => "bar")); - $v->rule(function($field, $value) { - return $field === "foo" && $value === "bar"; - }, "foo"); + public function testAddAddRuleWithCallback() + { + $v = new Validator(array("foo" => "bar")); + $v->rule(function($field, $value) { + return $field === "foo" && $value === "bar"; + }, "foo"); - $this->assertValid($v); - } + $this->assertValid($v); + } - public function testAddAddRuleWithCallbackFail() - { - $v = new Validator(array("foo" => "baz")); - $v->rule(function($field, $value) { - return $field === "foo" && $value === "bar"; - }, "foo"); + public function testAddAddRuleWithCallbackFail() + { + $v = new Validator(array("foo" => "baz")); + $v->rule(function($field, $value) { + return $field === "foo" && $value === "bar"; + }, "foo"); - $this->assertFalse($v->validate()); - } + $this->assertFalse($v->validate()); + } - public function testAddAddRuleWithCallbackFailMessage() - { - $v = new Validator(array("foo" => "baz")); - $v->rule(function($field, $value) { - return $field === "foo" && $value === "bar"; - }, "foo", "test error message"); + public function testAddAddRuleWithCallbackFailMessage() + { + $v = new Validator(array("foo" => "baz")); + $v->rule(function($field, $value) { + return $field === "foo" && $value === "bar"; + }, "foo", "test error message"); - $this->assertFalse($v->validate()); + $this->assertFalse($v->validate()); $errors = $v->errors(); - $this->assertArrayHasKey("foo", $errors); - $this->assertCount(1, $errors["foo"]); - $this->assertEquals("Foo test error message", $errors["foo"][0]); - } + $this->assertArrayHasKey("foo", $errors); + $this->assertCount(1, $errors["foo"]); + $this->assertEquals("Foo test error message", $errors["foo"][0]); + } - public function testUniqueRuleName() - { - $v = new Validator(array()); - $args = array("foo", "bar"); - $this->assertEquals("foo_bar_rule", $v->getUniqueRuleName($args)); - $this->assertEquals("foo_rule", $v->getUniqueRuleName("foo")); + public function testUniqueRuleName() + { + $v = new Validator(array()); + $args = array("foo", "bar"); + $this->assertEquals("foo_bar_rule", $v->getUniqueRuleName($args)); + $this->assertEquals("foo_rule", $v->getUniqueRuleName("foo")); - $v->addInstanceRule("foo_rule", function() {}); - $u = $v->getUniqueRuleName("foo"); - $this->assertRegExp("/^foo_rule_[0-9]{1,5}$/", $u); - } + $v->addInstanceRule("foo_rule", function() {}); + $u = $v->getUniqueRuleName("foo"); + $this->assertRegExp("/^foo_rule_[0-9]{1,5}$/", $u); + } } From ebbeca46af510934dcb9c68840865b1d558b8437 Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Sun, 6 Nov 2016 01:46:34 +0100 Subject: [PATCH 09/15] Updated README.md --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index c2f7195..18ee53c 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,29 @@ Valitron\Validator::addRule('alwaysFail', function($field, $value, array $params }, 'Everything you do is wrong. You fail.'); ``` +You can also use one-off rules that are only valid for the specified +fields. + +```php +$v = new Valitron\Validator(array("foo" => "bar")); +$v->rule(function($field, $value, $params, $fields) { + return true; +}, "foo")->message("{field} failed..."); +``` + +This is useful because such rules can have access to variables +defined in the scope where the `Validator` lives. The Closure's +signature is identical to `Validator::addRule` callback's +signature. + +If you wish to add your own rules that are not static (i.e., +your rule is not static and available to call `Validator` +instances), you need to use `Validator::addInstanceRule`. +This rule will take the same parameters as +`Validator::addRule` but it has to be called on a `Validator` +instance. + + ## Alternate syntax for adding rules As the number of rules grows, you may prefer the alternate syntax From a6be7e2dd00ac752611d99ab74b48d1c1cc24058 Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Sun, 6 Nov 2016 01:40:05 +0100 Subject: [PATCH 10/15] Validator::rule now accepts any kind of callback This has been achieved by making sure that the rule is neither a "native" Validator rule (e.g. there is a validate{$CamleCaseName} method) nor it has been registered. If both conditions are met, the rule (even if it is a string) will be accepted and used. Tests for this case have also been added. --- src/Valitron/Validator.php | 22 ++++++++++++++----- .../Valitron/ValidateAddInstanceRuleTest.php | 19 ++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 20dc88e..4a1a49a 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -1066,6 +1066,20 @@ class Validator return $name; } + /** + * Returns true if either a valdiator with the given name has been + * registered or there is a default validator by that name. + * + * @param string $name + * @return bool + */ + public function hasValidator($name) + { + $rules = $this->getRules(); + return method_exists($this, "validate" . ucfirst($name)) + || isset($rules[$name]); + } + /** * Convenience method to add a single validation rule * @@ -1079,12 +1093,8 @@ class Validator // Get any other arguments passed to function $params = array_slice(func_get_args(), 2); - // Note: we cannot use is_callable here since max, int, and many - // other string can also be callables but aren't really rule callbacks. - // - // If a closure is used, we can be sure that the user actually - // wants $rule to be a custom rule check. - if (is_object($rule) && ($rule instanceof \Closure)) + if (is_callable($rule) + && !(is_string($rule) && $this->hasValidator($rule))) { $name = $this->getUniqueRuleName($fields); $msg = isset($params[0]) ? $params[0] : null; diff --git a/tests/Valitron/ValidateAddInstanceRuleTest.php b/tests/Valitron/ValidateAddInstanceRuleTest.php index cc10d0b..ddefac3 100644 --- a/tests/Valitron/ValidateAddInstanceRuleTest.php +++ b/tests/Valitron/ValidateAddInstanceRuleTest.php @@ -1,6 +1,11 @@ assertEquals("Foo test error message", $errors["foo"][0]); } + public function testAddRuleWithNamedCallbackOk() + { + $v = new Validator(array("bar" => "foo")); + $v->rule("callbackTestFunction", "bar"); + $this->assertFalse($v->validate()); + } + + public function testAddRuleWithNamedCallbackErr() + { + $v = new Validator(array("foo" => "bar")); + $v->rule("callbackTestFunction", "foo"); + $this->assertTrue($v->validate()); + } + public function testUniqueRuleName() { $v = new Validator(array()); From 4f0f78ecfb216caa278d452492fa05380516e952 Mon Sep 17 00:00:00 2001 From: shin1x1 Date: Tue, 15 Nov 2016 18:06:16 +0900 Subject: [PATCH 11/15] Fix wrong @return --- src/Valitron/Validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 4a1a49a..b456b52 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -1139,7 +1139,7 @@ class Validator /** * @param array $labels - * @return string + * @return $this */ public function labels($labels = array()) { From f9dd486b5d3f4d5e0a1784cd7edf85a7263eec36 Mon Sep 17 00:00:00 2001 From: Andy Snell Date: Mon, 5 Dec 2016 00:09:39 -0500 Subject: [PATCH 12/15] Update MasterCard BIN Range Updates the credit card validator regular expression for the MasterCard brand to also match 16-digit card numbers starting with 22 - 27. Additionally, updates the unit test for credit cards with two published test cards in the new BIN range. Additional information on the BIN range update can be found at: https://www.mastercard.us/en-us/issuers/get-support/2-series-bin-expansion.html --- src/Valitron/Validator.php | 2 +- tests/Valitron/ValidateTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index b456b52..c5947ae 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -725,7 +725,7 @@ class Validator } else { $cardRegex = array( 'visa' => '#^4[0-9]{12}(?:[0-9]{3})?$#', - 'mastercard' => '#^5[1-5][0-9]{14}$#', + 'mastercard' => '#^(5[1-5]|2[2-7])[0-9]{14}$#', 'amex' => '#^3[47][0-9]{13}$#', 'dinersclub' => '#^3(?:0[0-5]|[68][0-9])[0-9]{11}$#', 'discover' => '#^6(?:011|5[0-9]{2})[0-9]{12}$#', diff --git a/tests/Valitron/ValidateTest.php b/tests/Valitron/ValidateTest.php index 459e5c1..ceb10bb 100644 --- a/tests/Valitron/ValidateTest.php +++ b/tests/Valitron/ValidateTest.php @@ -994,7 +994,7 @@ class ValidateTest extends BaseTestCase public function testCreditCardValid() { $visa = array(4539511619543489, 4532949059629052, 4024007171194938, 4929646403373269, 4539135861690622); - $mastercard = array(5162057048081965, 5382687859049349, 5484388880142230, 5464941521226434, 5473481232685965); + $mastercard = array(5162057048081965, 5382687859049349, 5484388880142230, 5464941521226434, 5473481232685965, 2223000048400011, 2223520043560014); $amex = array(371442067262027, 340743030537918, 345509167493596, 343665795576848, 346087552944316); $dinersclub = array(30363194756249, 30160097740704, 38186521192206, 38977384214552, 38563220301454); $discover = array(6011712400392605, 6011536340491809, 6011785775263015, 6011984124619056, 6011320958064251); From f8290a8c59e1cf2bf7dfb656a8b722bc494286eb Mon Sep 17 00:00:00 2001 From: Willem Wollebrants Date: Fri, 9 Dec 2016 11:44:39 +0100 Subject: [PATCH 13/15] Fix bug where Validator::withData would also erase all rules --- src/Valitron/Validator.php | 2 +- tests/Valitron/ValidateTest.php | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index c5947ae..965c0f8 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -1204,8 +1204,8 @@ class Validator public function withData($data, $fields = array()) { $clone = clone $this; - $clone->reset(); $clone->_fields = !empty($fields) ? array_intersect_key($data, array_flip($fields)) : $data; + $clone->_errors = array(); return $clone; } } diff --git a/tests/Valitron/ValidateTest.php b/tests/Valitron/ValidateTest.php index ceb10bb..4a91b33 100644 --- a/tests/Valitron/ValidateTest.php +++ b/tests/Valitron/ValidateTest.php @@ -1164,10 +1164,16 @@ class ValidateTest extends BaseTestCase //validation failed, so must have errors $this->assertFalse($v->validate()); $this->assertNotEmpty($v->errors()); - //create copy with different data + + //create copy with valid data $v2 = $v->withData(array('name' => 'Chester Tester')); $this->assertTrue($v2->validate()); $this->assertEmpty($v2->errors()); + + //create copy with invalid data + $v3 = $v->withData(array('firstname' => 'Chester')); + $this->assertFalse($v3->validate()); + $this->assertNotEmpty($v3->errors()); } } From 768a26c7f4ad1bd1ae913020dd22c3684fc60f93 Mon Sep 17 00:00:00 2001 From: Willem Wollebrants Date: Fri, 9 Dec 2016 11:50:13 +0100 Subject: [PATCH 14/15] Add note about re-usability to readme --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 18ee53c..bf18d32 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,19 @@ $v->validate(); This introduces a new set of tags to your error language file which looks like `{field}`, if you are using a rule like `equals` you can access the second value in the language file by incrementing the field with a value like `{field1}`. +## Re-use of validation rules + +You can re-use your validation rules to quickly validate different data with the same rules by using the withData method: + +```php +$v = new Valitron\Validator(array()); +$v->rule('required', 'name')->message('{field} is required'); +$v->validate(); //false + +$v2 = $v->withData(array('name'=>'example')); +$v2->validate(); //true +``` + ## Running Tests The test suite depends on the Composer autoloader to load and run the From 5181664c792dd6b0d64c5ff31824500649fb8074 Mon Sep 17 00:00:00 2001 From: Egor Gruzdev Date: Wed, 14 Dec 2016 10:40:14 +0200 Subject: [PATCH 15/15] for PhpStorm validation --- src/Valitron/Validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 965c0f8..1ecf43d 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -1084,7 +1084,7 @@ class Validator * Convenience method to add a single validation rule * * @param string|callback $rule - * @param array $fields + * @param array|string $fields * @return $this * @throws \InvalidArgumentException */