From 3bd186deebfed0a0cb3f286114daab98bc1a7340 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 3 Apr 2013 23:23:32 -0400 Subject: [PATCH] refactored validators. --- framework/base/Module.php | 5 +- framework/validators/BooleanValidator.php | 29 ++++----- framework/validators/CaptchaValidator.php | 56 +++++++++--------- framework/validators/CompareValidator.php | 69 ++++++++++++++-------- framework/validators/DefaultValueValidator.php | 15 ++--- framework/validators/EmailValidator.php | 16 +---- framework/validators/ExistValidator.php | 45 ++++++++------ framework/validators/FilterValidator.php | 20 ++++++- framework/validators/InlineValidator.php | 21 +++++-- framework/validators/NumberValidator.php | 28 +++++---- framework/validators/RangeValidator.php | 45 +++++++------- .../validators/RegularExpressionValidator.php | 44 ++++++++------ framework/validators/RequiredValidator.php | 21 +++++++ framework/validators/StringValidator.php | 64 ++++++++++++-------- framework/validators/UniqueValidator.php | 9 --- framework/validators/UrlValidator.php | 24 +++----- framework/validators/Validator.php | 19 +++++- 17 files changed, 309 insertions(+), 221 deletions(-) diff --git a/framework/base/Module.php b/framework/base/Module.php index 3e7eb04..296494d 100644 --- a/framework/base/Module.php +++ b/framework/base/Module.php @@ -580,8 +580,9 @@ abstract class Module extends Component * instance of it. * * @param string $route the route consisting of module, controller and action IDs. - * @return array|boolean if the controller is created successfully, it will be returned together - * with the remainder of the route which represents the action ID. Otherwise false will be returned. + * @return array|boolean If the controller is created successfully, it will be returned together + * with the requested action ID. Otherwise false will be returned. + * @throws InvalidConfigException if the controller class and its file do not match. */ public function createController($route) { diff --git a/framework/validators/BooleanValidator.php b/framework/validators/BooleanValidator.php index 7a7b68f..b441108 100644 --- a/framework/validators/BooleanValidator.php +++ b/framework/validators/BooleanValidator.php @@ -7,6 +7,8 @@ namespace yii\validators; +use Yii; + /** * BooleanValidator checks if the attribute value is a boolean value. * @@ -32,11 +34,6 @@ class BooleanValidator extends Validator * Defaults to false, meaning only the value needs to be matched. */ public $strict = false; - /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; /** * Validates the attribute of the object. @@ -47,12 +44,8 @@ class BooleanValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } - if (!$this->strict && $value != $this->trueValue && $value != $this->falseValue - || $this->strict && $value !== $this->trueValue && $value !== $this->falseValue) { - $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} must be either {true} or {false}.'); + if (!$this->validateValue($value)) { + $message = $this->message !== null ? $this->message : Yii::t('yii|{attribute} must be either "{true}" or "{false}".'); $this->addError($object, $attribute, $message, array( '{true}' => $this->trueValue, '{false}' => $this->falseValue, @@ -60,13 +53,15 @@ class BooleanValidator extends Validator } } + /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ public function validateValue($value) { - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } - return ($this->strict || $value == $this->trueValue || $value == $this->falseValue) - && (!$this->strict || $value === $this->trueValue || $value === $this->falseValue); + return $this->strict && ($value == $this->trueValue || $value == $this->falseValue) + || !$this->strict && ($value === $this->trueValue || $value === $this->falseValue); } /** @@ -77,7 +72,7 @@ class BooleanValidator extends Validator */ public function clientValidateAttribute($object, $attribute) { - $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} must be either {true} or {false}.'); + $message = ($this->message !== null) ? $this->message : Yii::t('yii|{attribute} must be either "{true}" or "{false}".'); $message = strtr($message, array( '{attribute}' => $object->getAttributeLabel($attribute), '{value}' => $object->$attribute, diff --git a/framework/validators/CaptchaValidator.php b/framework/validators/CaptchaValidator.php index 3f31f77..f35b332 100644 --- a/framework/validators/CaptchaValidator.php +++ b/framework/validators/CaptchaValidator.php @@ -7,6 +7,9 @@ namespace yii\validators; +use Yii; +use yii\base\InvalidConfigException; + /** * CaptchaValidator validates that the attribute value is the same as the verification code displayed in the CAPTCHA. * @@ -22,16 +25,10 @@ class CaptchaValidator extends Validator */ public $caseSensitive = false; /** - * @var string the ID of the action that renders the CAPTCHA image. Defaults to 'captcha', - * meaning the `captcha` action declared in the current controller. - * This can also be a route consisting of controller ID and action ID (e.g. 'site/captcha'). - */ - public $captchaAction = 'captcha'; - /** - * @var boolean whether the attribute value can be null or empty. - * Defaults to false, meaning the attribute is invalid if it is empty. + * @var string the route of the controller action that renders the CAPTCHA image. */ - public $allowEmpty = false; + public $captchaAction = 'site/captcha'; + /** * Validates the attribute of the object. @@ -42,36 +39,39 @@ class CaptchaValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } - $captcha = $this->getCaptchaAction(); - if (!$captcha->validate($value, $this->caseSensitive)) { - $message = $this->message !== null ? $this->message : \Yii::t('yii|The verification code is incorrect.'); + if (!$this->validateValue($value)) { + $message = $this->message !== null ? $this->message : Yii::t('yii|The verification code is incorrect.'); $this->addError($object, $attribute, $message); } } /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ + public function validateValue($value) + { + $captcha = $this->getCaptchaAction(); + return $captcha->validate($value, $this->caseSensitive); + } + + /** * Returns the CAPTCHA action object. - * @return CCaptchaAction the action object + * @return CaptchaAction the action object */ public function getCaptchaAction() { - if (strpos($this->captchaAction, '/') !== false) { // contains controller or module - $ca = \Yii::$app->createController($this->captchaAction); - if ($ca !== null) { - list($controller, $actionID) = $ca; - $action = $controller->createAction($actionID); + $ca = Yii::$app->createController($this->captchaAction); + if ($ca !== false) { + /** @var \yii\base\Controller $controller */ + list($controller, $actionID) = $ca; + $action = $controller->createAction($actionID); + if ($action !== null) { + return $action; } - } else { - $action = \Yii::$app->getController()->createAction($this->captchaAction); - } - - if ($action === null) { - throw new \yii\base\Exception('Invalid captcha action ID: ' . $this->captchaAction); } - return $action; + throw new InvalidConfigException('Invalid CAPTCHA action ID: ' . $this->captchaAction); } /** diff --git a/framework/validators/CompareValidator.php b/framework/validators/CompareValidator.php index 43f2edf..3c85367 100644 --- a/framework/validators/CompareValidator.php +++ b/framework/validators/CompareValidator.php @@ -6,6 +6,7 @@ */ namespace yii\validators; + use Yii; use yii\base\InvalidConfigException; @@ -45,23 +46,12 @@ class CompareValidator extends Validator */ public $compareValue; /** - * @var boolean whether the comparison is strict (both value and type must be the same.) - * Defaults to false. - */ - public $strict = false; - /** - * @var boolean whether the attribute value can be null or empty. Defaults to false. - * If this is true, it means the attribute is considered valid when it is empty. - */ - public $allowEmpty = false; - /** - * @var string the operator for comparison. Defaults to '='. - * The followings are valid operators: - * - * - `=` or `==`: validates to see if the two values are equal. If [[strict]] is true, the comparison - * will be done in strict mode (i.e. checking value type as well). - * - `!=`: validates to see if the two values are NOT equal. If [[strict]] is true, the comparison - * will be done in strict mode (i.e. checking value type as well). + * @var string the operator for comparison. The following operators are supported: + * + * - '==': validates to see if the two values are equal. The comparison is done is non-strict mode. + * - '===': validates to see if the two values are equal. The comparison is done is strict mode. + * - '!=': validates to see if the two values are NOT equal. The comparison is done is non-strict mode. + * - '!==': validates to see if the two values are NOT equal. The comparison is done is strict mode. * - `>`: validates to see if the value being validated is greater than the value being compared with. * - `>=`: validates to see if the value being validated is greater than or equal to the value being compared with. * - `<`: validates to see if the value being validated is less than the value being compared with. @@ -79,9 +69,6 @@ class CompareValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } if ($this->compareValue !== null) { $compareLabel = $compareValue = $this->compareValue; } else { @@ -91,15 +78,26 @@ class CompareValidator extends Validator } switch ($this->operator) { - case '=': case '==': - if (($this->strict && $value !== $compareValue) || (!$this->strict && $value != $compareValue)) { + if ($value != $compareValue) { + $message = ($this->message !== null) ? $this->message : Yii::t('yii|{attribute} must be repeated exactly.'); + $this->addError($object, $attribute, $message, array('{compareAttribute}' => $compareLabel)); + } + break; + case '===': + if ($value !== $compareValue) { $message = ($this->message !== null) ? $this->message : Yii::t('yii|{attribute} must be repeated exactly.'); $this->addError($object, $attribute, $message, array('{compareAttribute}' => $compareLabel)); } break; case '!=': - if (($this->strict && $value === $compareValue) || (!$this->strict && $value == $compareValue)) { + if ($value == $compareValue) { + $message = ($this->message !== null) ? $this->message : Yii::t('yii|{attribute} must not be equal to "{compareValue}".'); + $this->addError($object, $attribute, $message, array('{compareAttribute}' => $compareLabel, '{compareValue}' => $compareValue)); + } + break; + case '!==': + if ($value === $compareValue) { $message = ($this->message !== null) ? $this->message : Yii::t('yii|{attribute} must not be equal to "{compareValue}".'); $this->addError($object, $attribute, $message, array('{compareAttribute}' => $compareLabel, '{compareValue}' => $compareValue)); } @@ -134,6 +132,31 @@ class CompareValidator extends Validator } /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ + public function validateValue($value) + { + if ($this->compareValue === null) { + throw new InvalidConfigException('CompareValidator::compareValue must be set.'); + } + + switch ($this->operator) { + case '==': return $value == $this->compareValue; + case '===': return $value === $this->compareValue; + case '!=': return $value != $this->compareValue; + case '!==': return $value !== $this->compareValue; + case '>': return $value > $this->compareValue; + case '>=': return $value >= $this->compareValue; + case '<': return $value < $this->compareValue; + case '<=': return $value <= $this->compareValue; + default: + throw new InvalidConfigException("Unknown operator \"{$this->operator}\""); + } + } + + /** * Returns the JavaScript needed for performing client-side validation. * @param \yii\base\Model $object the data object being validated * @param string $attribute the name of the attribute to be validated diff --git a/framework/validators/DefaultValueValidator.php b/framework/validators/DefaultValueValidator.php index be06768..185dbd4 100644 --- a/framework/validators/DefaultValueValidator.php +++ b/framework/validators/DefaultValueValidator.php @@ -10,12 +10,8 @@ namespace yii\validators; /** * DefaultValueValidator sets the attribute to be the specified default value. * - * By default, when the attribute being validated is [[isEmpty|empty]], the validator - * will assign a default [[value]] to it. However, if [[setOnEmpty]] is false, the validator - * will always assign the default [[value]] to the attribute, no matter it is empty or not. - * * DefaultValueValidator is not really a validator. It is provided mainly to allow - * specifying attribute default values in a dynamic way. + * specifying attribute default values when they are empty. * * @author Qiang Xue * @since 2.0 @@ -27,11 +23,10 @@ class DefaultValueValidator extends Validator */ public $value; /** - * @var boolean whether to set the default [[value]] only when the attribute is [[isEmpty|empty]]. - * Defaults to true. If false, the attribute will always be assigned with the default [[value]], - * no matter it is empty or not. + * @var boolean this property is overwritten to be false so that this validator will + * be applied when the value being validated is empty. */ - public $setOnEmpty = true; + public $skipOnEmpty = false; /** * Validates the attribute of the object. @@ -40,7 +35,7 @@ class DefaultValueValidator extends Validator */ public function validateAttribute($object, $attribute) { - if (!$this->setOnEmpty || $this->isEmpty($object->$attribute)) { + if ($this->isEmpty($object->$attribute)) { $object->$attribute = $this->value; } } diff --git a/framework/validators/EmailValidator.php b/framework/validators/EmailValidator.php index d1d2257..396c25f 100644 --- a/framework/validators/EmailValidator.php +++ b/framework/validators/EmailValidator.php @@ -42,11 +42,6 @@ class EmailValidator extends Validator * Defaults to false. */ public $checkPort = false; - /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; /** * Validates the attribute of the object. @@ -57,9 +52,6 @@ class EmailValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } if (!$this->validateValue($value)) { $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} is not a valid email address.'); $this->addError($object, $attribute, $message); @@ -67,11 +59,9 @@ class EmailValidator extends Validator } /** - * Validates a static value to see if it is a valid email. - * Note that this method does not respect [[allowEmpty]] property. - * This method is provided so that you can call it directly without going through the model validation rule mechanism. - * @param mixed $value the value to be validated - * @return boolean whether the value is a valid email + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. */ public function validateValue($value) { diff --git a/framework/validators/ExistValidator.php b/framework/validators/ExistValidator.php index 8df3e19..b39be56 100644 --- a/framework/validators/ExistValidator.php +++ b/framework/validators/ExistValidator.php @@ -6,6 +6,8 @@ */ namespace yii\validators; + +use Yii; use yii\base\InvalidConfigException; /** @@ -34,11 +36,6 @@ class ExistValidator extends Validator * @see className */ public $attributeName; - /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; /** * Validates the attribute of the object. @@ -46,29 +43,41 @@ class ExistValidator extends Validator * * @param \yii\db\ActiveRecord $object the object being validated * @param string $attribute the attribute being validated - * @throws InvalidConfigException if table doesn't have column specified */ public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } /** @var $className \yii\db\ActiveRecord */ - $className = ($this->className === null) ? get_class($object) : \Yii::import($this->className); - $attributeName = ($this->attributeName === null) ? $attribute : $this->attributeName; - $table = $className::getTableSchema(); - if (($column = $table->getColumn($attributeName)) === null) { - throw new InvalidConfigException('Table "' . $table->name . '" does not have a column named "' . $attributeName . '"'); - } - + $className = $this->className === null ? get_class($object) : Yii::import($this->className); + $attributeName = $this->attributeName === null ? $attribute : $this->attributeName; $query = $className::find(); - $query->where(array($column->name => $value)); + $query->where(array($attributeName => $value)); if (!$query->exists()) { - $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} "{value}" is invalid.'); + $message = $this->message !== null ? $this->message : Yii::t('yii|{attribute} "{value}" is invalid.'); $this->addError($object, $attribute, $message); } } + + /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + * @throws InvalidConfigException if either [[className]] or [[attributeName]] is not set. + */ + public function validateValue($value) + { + if ($this->className === null) { + throw new InvalidConfigException('The "className" property must be set.'); + } + if ($this->attributeName === null) { + throw new InvalidConfigException('The "attributeName" property must be set.'); + } + /** @var $className \yii\db\ActiveRecord */ + $className = $this->className; + $query = $className::find(); + $query->where(array($this->attributeName => $value)); + return $query->exists(); + } } diff --git a/framework/validators/FilterValidator.php b/framework/validators/FilterValidator.php index c891979..72a9a9d 100644 --- a/framework/validators/FilterValidator.php +++ b/framework/validators/FilterValidator.php @@ -38,6 +38,23 @@ class FilterValidator extends Validator * ~~~ */ public $filter; + /** + * @var boolean this property is overwritten to be false so that this validator will + * be applied when the value being validated is empty. + */ + public $skipOnEmpty = false; + + /** + * Initializes the validator. + * @throws InvalidConfigException if [[filter]] is not set. + */ + public function init() + { + parent::init(); + if ($this->filter === null) { + throw new InvalidConfigException('The "filter" property must be set.'); + } + } /** * Validates the attribute of the object. @@ -48,9 +65,6 @@ class FilterValidator extends Validator */ public function validateAttribute($object, $attribute) { - if ($this->filter === null) { - throw new InvalidConfigException('The "filter" property must be specified with a valid callback.'); - } $object->$attribute = call_user_func($this->filter, $object->$attribute); } } diff --git a/framework/validators/InlineValidator.php b/framework/validators/InlineValidator.php index 5c12d52..3689a2f 100644 --- a/framework/validators/InlineValidator.php +++ b/framework/validators/InlineValidator.php @@ -25,8 +25,9 @@ namespace yii\validators; class InlineValidator extends Validator { /** - * @var string the name of the validation method defined in the - * \yii\base\Model class + * @var string|\Closure an anonymous function or the name of a model class method that will be + * called to perform the actual validation. Note that if you use anonymous function, you cannot + * use `$this` in it unless you are using PHP 5.4 or above. */ public $method; /** @@ -34,8 +35,8 @@ class InlineValidator extends Validator */ public $params; /** - * @var string the name of the method that returns the client validation code (see [[clientValidateAttribute()]] - * for details on how to return client validation code). The signature of the method should be like the following: + * @var string|\Closure an anonymous function or the name of a model class method that returns the client validation code. + * The signature of the method should be like the following: * * ~~~ * function foo($attribute) @@ -45,6 +46,8 @@ class InlineValidator extends Validator * ~~~ * * where `$attribute` refers to the attribute name to be validated. + * + * Please refer to [[clientValidateAttribute()]] for details on how to return client validation code. */ public $clientValidate; @@ -56,7 +59,10 @@ class InlineValidator extends Validator public function validateAttribute($object, $attribute) { $method = $this->method; - $object->$method($attribute, $this->params); + if (is_string($method)) { + $method = array($object, $method); + } + call_user_func($method, $attribute, $this->params); } /** @@ -82,7 +88,10 @@ class InlineValidator extends Validator { if ($this->clientValidate !== null) { $method = $this->clientValidate; - return $object->$method($attribute); + if (is_string($method)) { + $method = array($object, $method); + } + return call_user_func($method, $attribute); } else { return null; } diff --git a/framework/validators/NumberValidator.php b/framework/validators/NumberValidator.php index 89363fb..6219bdb 100644 --- a/framework/validators/NumberValidator.php +++ b/framework/validators/NumberValidator.php @@ -26,11 +26,6 @@ class NumberValidator extends Validator */ public $integerOnly = false; /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; - /** * @var integer|float upper limit of the number. Defaults to null, meaning no upper limit. */ public $max; @@ -66,9 +61,6 @@ class NumberValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } if ($this->integerOnly) { if (!preg_match($this->integerPattern, "$value")) { $message = $this->message !== null ? $this->message : Yii::t('yii|{attribute} must be an integer.'); @@ -81,16 +73,28 @@ class NumberValidator extends Validator } } if ($this->min !== null && $value < $this->min) { - $message = $this->tooSmall !== null ? $this->tooSmall : Yii::t('yii|{attribute} is too small (minimum is {min}).'); + $message = $this->tooSmall !== null ? $this->tooSmall : Yii::t('yii|{attribute} must be no less than {min}.'); $this->addError($object, $attribute, $message, array('{min}' => $this->min)); } if ($this->max !== null && $value > $this->max) { - $message = $this->tooBig !== null ? $this->tooBig : Yii::t('yii|{attribute} is too big (maximum is {max}).'); + $message = $this->tooBig !== null ? $this->tooBig : Yii::t('yii|{attribute} must be no greater than {max}.'); $this->addError($object, $attribute, $message, array('{max}' => $this->max)); } } /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ + public function validateValue($value) + { + return preg_match($this->integerOnly ? $this->integerPattern : $this->numberPattern, "$value") + && ($this->min === null || $value >= $this->min) + && ($this->max === null || $value <= $this->max); + } + + /** * Returns the JavaScript needed for performing client-side validation. * @param \yii\base\Model $object the data object being validated * @param string $attribute the name of the attribute to be validated. @@ -116,7 +120,7 @@ if(!value.match($pattern)) { "; if ($this->min !== null) { if (($tooSmall = $this->tooSmall) === null) { - $tooSmall = Yii::t('yii|{attribute} is too small (minimum is {min}).'); + $tooSmall = Yii::t('yii|{attribute} must be no less than {min}.'); } $tooSmall = strtr($tooSmall, array( '{attribute}' => $label, @@ -131,7 +135,7 @@ if(value<{$this->min}) { } if ($this->max !== null) { if (($tooBig = $this->tooBig) === null) { - $tooBig = Yii::t('yii|{attribute} is too big (maximum is {max}).'); + $tooBig = Yii::t('yii|{attribute} must be no greater than {max}.'); } $tooBig = strtr($tooBig, array( '{attribute}' => $label, diff --git a/framework/validators/RangeValidator.php b/framework/validators/RangeValidator.php index e23567c..0498a55 100644 --- a/framework/validators/RangeValidator.php +++ b/framework/validators/RangeValidator.php @@ -29,56 +29,61 @@ class RangeValidator extends Validator */ public $strict = false; /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; - /** * @var boolean whether to invert the validation logic. Defaults to false. If set to true, * the attribute value should NOT be among the list of values defined via [[range]]. **/ public $not = false; /** + * Initializes the validator. + * @throws InvalidConfigException if [[range]] is not set. + */ + public function init() + { + parent::init(); + if (!is_array($this->range)) { + throw new InvalidConfigException('The "range" property must be set.'); + } + } + + /** * Validates the attribute of the object. * If there is any error, the error message is added to the object. * @param \yii\base\Model $object the object being validated * @param string $attribute the attribute being validated - * @throws InvalidConfigException if the "range" property is not an array */ public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } - if (!is_array($this->range)) { - throw new InvalidConfigException('The "range" property must be specified as an array.'); - } + $message = $this->message !== null ? $this->message : \Yii::t('yii|{attribute} is invalid.'); if (!$this->not && !in_array($value, $this->range, $this->strict)) { - $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} should be in the list.'); $this->addError($object, $attribute, $message); } elseif ($this->not && in_array($value, $this->range, $this->strict)) { - $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} should NOT be in the list.'); $this->addError($object, $attribute, $message); } } /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ + public function validateValue($value) + { + return !$this->not && in_array($value, $this->range, $this->strict) + || $this->not && !in_array($value, $this->range, $this->strict); + } + + /** * Returns the JavaScript needed for performing client-side validation. * @param \yii\base\Model $object the data object being validated * @param string $attribute the name of the attribute to be validated. * @return string the client-side validation script. - * @throws InvalidConfigException if the "range" property is not an array */ public function clientValidateAttribute($object, $attribute) { - if (!is_array($this->range)) { - throw new InvalidConfigException('The "range" property must be specified as an array.'); - } - if (($message = $this->message) === null) { - $message = $this->not ? \Yii::t('yii|{attribute} should NOT be in the list.') : \Yii::t('yii|{attribute} should be in the list.'); + $message = \Yii::t('yii|{attribute} is invalid.'); } $message = strtr($message, array( '{attribute}' => $object->getAttributeLabel($attribute), diff --git a/framework/validators/RegularExpressionValidator.php b/framework/validators/RegularExpressionValidator.php index df2b657..b0811e9 100644 --- a/framework/validators/RegularExpressionValidator.php +++ b/framework/validators/RegularExpressionValidator.php @@ -7,6 +7,8 @@ namespace yii\validators; +use yii\base\InvalidConfigException; + /** * RegularExpressionValidator validates that the attribute value matches the specified [[pattern]]. * @@ -22,32 +24,33 @@ class RegularExpressionValidator extends Validator */ public $pattern; /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; - /** * @var boolean whether to invert the validation logic. Defaults to false. If set to true, * the regular expression defined via [[pattern]] should NOT match the attribute value. + * @throws InvalidConfigException if the "pattern" is not a valid regular expression **/ public $not = false; /** + * Initializes the validator. + * @throws InvalidConfigException if [[pattern]] is not set. + */ + public function init() + { + parent::init(); + if ($this->pattern === null) { + throw new InvalidConfigException('The "pattern" property must be set.'); + } + } + + /** * Validates the attribute of the object. * If there is any error, the error message is added to the object. * @param \yii\base\Model $object the object being validated * @param string $attribute the attribute being validated - * @throws \yii\base\Exception if the "pattern" is not a valid regular expression */ public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } - if ($this->pattern === null) { - throw new \yii\base\Exception('The "pattern" property must be specified with a valid regular expression.'); - } if ((!$this->not && !preg_match($this->pattern, $value)) || ($this->not && preg_match($this->pattern, $value))) { $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} is invalid.'); $this->addError($object, $attribute, $message); @@ -55,18 +58,25 @@ class RegularExpressionValidator extends Validator } /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ + public function validateValue($value) + { + return !$this->not && preg_match($this->pattern, $value) + || $this->not && !preg_match($this->pattern, $value); + } + + /** * Returns the JavaScript needed for performing client-side validation. * @param \yii\base\Model $object the data object being validated * @param string $attribute the name of the attribute to be validated. * @return string the client-side validation script. - * @throws \yii\base\Exception if the "pattern" is not a valid regular expression + * @throws InvalidConfigException if the "pattern" is not a valid regular expression */ public function clientValidateAttribute($object, $attribute) { - if ($this->pattern === null) { - throw new \yii\base\Exception('The "pattern" property must be specified with a valid regular expression.'); - } - $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} is invalid.'); $message = strtr($message, array( '{attribute}' => $object->getAttributeLabel($attribute), diff --git a/framework/validators/RequiredValidator.php b/framework/validators/RequiredValidator.php index 66b9c3c..944c695 100644 --- a/framework/validators/RequiredValidator.php +++ b/framework/validators/RequiredValidator.php @@ -16,6 +16,10 @@ namespace yii\validators; class RequiredValidator extends Validator { /** + * @var boolean whether to skip this validator if the value being validated is empty. + */ + public $skipOnEmpty = false; + /** * @var mixed the desired value that the attribute must have. * If this is null, the validator will validate that the specified attribute is not empty. * If this is set as a value that is not null, the validator will validate that @@ -59,6 +63,23 @@ class RequiredValidator extends Validator } /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ + public function validateValue($value) + { + if ($this->requiredValue === null) { + if ($this->strict && $value !== null || !$this->strict && !$this->isEmpty($value, true)) { + return true; + } + } elseif (!$this->strict && $value == $this->requiredValue || $this->strict && $value === $this->requiredValue) { + return true; + } + return false; + } + + /** * Returns the JavaScript needed for performing client-side validation. * @param \yii\base\Model $object the data object being validated * @param string $attribute the name of the attribute to be validated. diff --git a/framework/validators/StringValidator.php b/framework/validators/StringValidator.php index 9135b9e..83ff35b 100644 --- a/framework/validators/StringValidator.php +++ b/framework/validators/StringValidator.php @@ -7,6 +7,8 @@ namespace yii\validators; +use Yii; + /** * StringValidator validates that the attribute value is of certain length. * @@ -46,19 +48,22 @@ class StringValidator extends Validator */ public $notEqual; /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. + * @var string the encoding of the string value to be validated (e.g. 'UTF-8'). + * If this property is not set, [[\yii\base\Application::charset]] will be used. */ - public $allowEmpty = true; + public $encoding; + + /** - * @var mixed the encoding of the string value to be validated (e.g. 'UTF-8'). - * This property is used only when mbstring PHP extension is enabled. - * The value of this property will be used as the 2nd parameter of the - * mb_strlen() function. If this property is not set, the application charset - * will be used. If this property is set false, then strlen() will be used even - * if mbstring is enabled. + * Initializes the validator. */ - public $encoding; + public function init() + { + parent::init(); + if ($this->encoding === null) { + $this->encoding = Yii::$app->charset; + } + } /** * Validates the attribute of the object. @@ -69,37 +74,46 @@ class StringValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } if (!is_string($value)) { - $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} must be a string.'); + $message = ($this->message !== null) ? $this->message : Yii::t('yii|{attribute} must be a string.'); $this->addError($object, $attribute, $message); return; } - if (function_exists('mb_strlen') && $this->encoding !== false) { - $length = mb_strlen($value, $this->encoding ? $this->encoding : \Yii::$app->charset); - } else { - $length = strlen($value); - } + $length = mb_strlen($value, $this->encoding); if ($this->min !== null && $length < $this->min) { - $message = ($this->tooShort !== null) ? $this->tooShort : \Yii::t('yii|{attribute} is too short (minimum is {min} characters).'); + $message = ($this->tooShort !== null) ? $this->tooShort : Yii::t('yii|{attribute} should contain at least {min} characters.'); $this->addError($object, $attribute, $message, array('{min}' => $this->min)); } if ($this->max !== null && $length > $this->max) { - $message = ($this->tooLong !== null) ? $this->tooLong : \Yii::t('yii|{attribute} is too long (maximum is {max} characters).'); + $message = ($this->tooLong !== null) ? $this->tooLong : Yii::t('yii|{attribute} should contain at most {max} characters.'); $this->addError($object, $attribute, $message, array('{max}' => $this->max)); } if ($this->is !== null && $length !== $this->is) { - $message = ($this->notEqual !== null) ? $this->notEqual : \Yii::t('yii|{attribute} is of the wrong length (should be {length} characters).'); + $message = ($this->notEqual !== null) ? $this->notEqual : Yii::t('yii|{attribute} should contain {length} characters.'); $this->addError($object, $attribute, $message, array('{length}' => $this->is)); } } /** + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. + */ + public function validateValue($value) + { + if (!is_string($value)) { + return false; + } + $length = mb_strlen($value, $this->encoding); + return ($this->min === null || $length >= $this->min) + && ($this->max === null || $length <= $this->max) + && ($this->is === null || $length === $this->is); + } + + /** * Returns the JavaScript needed for performing client-side validation. * @param \yii\base\Model $object the data object being validated * @param string $attribute the name of the attribute to be validated. @@ -111,7 +125,7 @@ class StringValidator extends Validator $value = $object->$attribute; if (($notEqual = $this->notEqual) === null) { - $notEqual = \Yii::t('yii|{attribute} is of the wrong length (should be {length} characters).'); + $notEqual = Yii::t('yii|{attribute} should contain {length} characters.'); } $notEqual = strtr($notEqual, array( '{attribute}' => $label, @@ -120,7 +134,7 @@ class StringValidator extends Validator )); if (($tooShort = $this->tooShort) === null) { - $tooShort = \Yii::t('yii|{attribute} is too short (minimum is {min} characters).'); + $tooShort = Yii::t('yii|{attribute} should contain at least {min} characters.'); } $tooShort = strtr($tooShort, array( '{attribute}' => $label, @@ -129,7 +143,7 @@ class StringValidator extends Validator )); if (($tooLong = $this->tooLong) === null) { - $tooLong = \Yii::t('yii|{attribute} is too long (maximum is {max} characters).'); + $tooLong = Yii::t('yii|{attribute} should contain at most {max} characters.'); } $tooLong = strtr($tooLong, array( '{attribute}' => $label, diff --git a/framework/validators/UniqueValidator.php b/framework/validators/UniqueValidator.php index bc12f5a..8d5c8b7 100644 --- a/framework/validators/UniqueValidator.php +++ b/framework/validators/UniqueValidator.php @@ -17,11 +17,6 @@ use yii\base\InvalidConfigException; class UniqueValidator extends Validator { /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; - /** * @var string the ActiveRecord class name or alias of the class * that should be used to look for the attribute value being validated. * Defaults to null, meaning using the ActiveRecord class of the attribute being validated. @@ -45,10 +40,6 @@ class UniqueValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } - /** @var $className \yii\db\ActiveRecord */ $className = $this->className === null ? get_class($object) : \Yii::import($this->className); $attributeName = $this->attributeName === null ? $attribute : $this->attributeName; diff --git a/framework/validators/UrlValidator.php b/framework/validators/UrlValidator.php index 0ba039b..fb743e0 100644 --- a/framework/validators/UrlValidator.php +++ b/framework/validators/UrlValidator.php @@ -32,11 +32,6 @@ class UrlValidator extends Validator * contain the scheme part. **/ public $defaultScheme; - /** - * @var boolean whether the attribute value can be null or empty. Defaults to true, - * meaning that if the attribute is empty, it is considered valid. - */ - public $allowEmpty = true; /** * Validates the attribute of the object. @@ -47,11 +42,10 @@ class UrlValidator extends Validator public function validateAttribute($object, $attribute) { $value = $object->$attribute; - if ($this->allowEmpty && $this->isEmpty($value)) { - return; - } - if (($value = $this->validateValue($value)) !== false) { - $object->$attribute = $value; + if ($this->validateValue($value)) { + if ($this->defaultScheme !== null && strpos($value, '://') === false) { + $object->$attribute = $this->defaultScheme . '://' . $value; + } } else { $message = ($this->message !== null) ? $this->message : \Yii::t('yii|{attribute} is not a valid URL.'); $this->addError($object, $attribute, $message); @@ -59,11 +53,9 @@ class UrlValidator extends Validator } /** - * Validates a static value to see if it is a valid URL. - * Note that this method does not respect [[allowEmpty]] property. - * This method is provided so that you can call it directly without going through the model validation rule mechanism. - * @param mixed $value the value to be validated - * @return mixed false if the the value is not a valid URL, otherwise the possibly modified value ({@see defaultScheme}) + * Validates the given value. + * @param mixed $value the value to be validated. + * @return boolean whether the value is valid. */ public function validateValue($value) { @@ -80,7 +72,7 @@ class UrlValidator extends Validator } if (preg_match($pattern, $value)) { - return $value; + return true; } } return false; diff --git a/framework/validators/Validator.php b/framework/validators/Validator.php index 00a88ba..4dc58ae 100644 --- a/framework/validators/Validator.php +++ b/framework/validators/Validator.php @@ -7,6 +7,7 @@ namespace yii\validators; +use Yii; use yii\base\Component; use yii\base\NotSupportedException; @@ -95,6 +96,12 @@ abstract class Validator extends Component */ public $skipOnError = true; /** + * @var boolean whether this validation rule should be skipped if the attribute value + * is null or an empty string. + */ + public $skipOnEmpty = true; + + /** * @var boolean whether to enable client-side validation. Defaults to null, meaning * its actual value inherits from that of [[\yii\web\ActiveForm::enableClientValidation]]. */ @@ -150,7 +157,7 @@ abstract class Validator extends Component } } - return \Yii::createObject($params); + return Yii::createObject($params); } /** @@ -169,12 +176,20 @@ abstract class Validator extends Component $attributes = $this->attributes; } foreach ($attributes as $attribute) { - if (!($this->skipOnError && $object->hasErrors($attribute))) { + $skip = $this->skipOnError && $object->hasErrors($attribute) + || $this->skipOnEmpty && $this->isEmpty($object->$attribute); + if (!$skip) { $this->validateAttribute($object, $attribute); } } } + /** + * Validates a value. + * A validator class can implement this method to support data validation out of the context of a data model. + * @param mixed $value the data value to be validated. + * @throws NotSupportedException if data validation without a model is not supported + */ public function validateValue($value) { throw new NotSupportedException(__CLASS__ . ' does not support validateValue().');