Validator.php 11.3 KB
Newer Older
w  
Qiang Xue committed
1 2 3
<?php
/**
 * @link http://www.yiiframework.com/
Qiang Xue committed
4
 * @copyright Copyright (c) 2008 Yii Software LLC
w  
Qiang Xue committed
5 6 7
 * @license http://www.yiiframework.com/license/
 */

w  
Qiang Xue committed
8 9
namespace yii\validators;

Qiang Xue committed
10
use Yii;
Qiang Xue committed
11
use yii\base\Component;
Qiang Xue committed
12
use yii\base\NotSupportedException;
Qiang Xue committed
13

w  
Qiang Xue committed
14
/**
w  
Qiang Xue committed
15
 * Validator is the base class for all validators.
w  
Qiang Xue committed
16
 *
Qiang Xue committed
17
 * Child classes should override the [[validateValue()]] and/or [[validateAttribute()]] methods to provide the actual
Qiang Xue committed
18
 * logic of performing data validation. Child classes may also override [[clientValidateAttribute()]]
w  
Qiang Xue committed
19
 * to provide client-side validation support.
w  
Qiang Xue committed
20
 *
Qiang Xue committed
21
 * Validator declares a set of [[builtInValidators|built-in validators] which can
w  
Qiang Xue committed
22 23
 * be referenced using short names. They are listed as follows:
 *
w  
Qiang Xue committed
24
 * - `boolean`: [[BooleanValidator]]
25
 * - `captcha`: [[\yii\captcha\CaptchaValidator]]
Qiang Xue committed
26 27
 * - `compare`: [[CompareValidator]]
 * - `date`: [[DateValidator]]
w  
Qiang Xue committed
28
 * - `default`: [[DefaultValueValidator]]
Qiang Xue committed
29 30
 * - `double`: [[NumberValidator]]
 * - `email`: [[EmailValidator]]
w  
Qiang Xue committed
31
 * - `exist`: [[ExistValidator]]
Qiang Xue committed
32 33
 * - `file`: [[FileValidator]]
 * - `filter`: [[FilterValidator]]
Gudz Taras committed
34
 * - `image`: [[ImageValidator]]
Qiang Xue committed
35 36 37 38
 * - `in`: [[RangeValidator]]
 * - `integer`: [[NumberValidator]]
 * - `match`: [[RegularExpressionValidator]]
 * - `required`: [[RequiredValidator]]
Qiang Xue committed
39
 * - `safe`: [[SafeValidator]]
Qiang Xue committed
40
 * - `string`: [[StringValidator]]
Qiang Xue committed
41
 * - `trim`: [[FilterValidator]]
Qiang Xue committed
42 43
 * - `unique`: [[UniqueValidator]]
 * - `url`: [[UrlValidator]]
w  
Qiang Xue committed
44 45
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
46
 * @since 2.0
w  
Qiang Xue committed
47
 */
Qiang Xue committed
48
class Validator extends Component
w  
Qiang Xue committed
49 50
{
	/**
w  
Qiang Xue committed
51
	 * @var array list of built-in validators (name => class or configuration)
w  
Qiang Xue committed
52
	 */
Alexander Makarov committed
53
	public static $builtInValidators = [
Qiang Xue committed
54
		'boolean' => 'yii\validators\BooleanValidator',
Qiang Xue committed
55
		'captcha' => 'yii\captcha\CaptchaValidator',
Qiang Xue committed
56 57 58 59 60 61 62 63
		'compare' => 'yii\validators\CompareValidator',
		'date' => 'yii\validators\DateValidator',
		'default' => 'yii\validators\DefaultValueValidator',
		'double' => 'yii\validators\NumberValidator',
		'email' => 'yii\validators\EmailValidator',
		'exist' => 'yii\validators\ExistValidator',
		'file' => 'yii\validators\FileValidator',
		'filter' => 'yii\validators\FilterValidator',
Gudz Taras committed
64
		'image' => 'yii\validators\ImageValidator',
Qiang Xue committed
65
		'in' => 'yii\validators\RangeValidator',
Alexander Makarov committed
66
		'integer' => [
Qiang Xue committed
67 68
			'class' => 'yii\validators\NumberValidator',
			'integerOnly' => true,
Alexander Makarov committed
69
		],
Qiang Xue committed
70
		'match' => 'yii\validators\RegularExpressionValidator',
Qiang Xue committed
71
		'number' => 'yii\validators\NumberValidator',
Qiang Xue committed
72
		'required' => 'yii\validators\RequiredValidator',
Qiang Xue committed
73
		'safe' => 'yii\validators\SafeValidator',
Qiang Xue committed
74
		'string' => 'yii\validators\StringValidator',
Qiang Xue committed
75 76 77 78
		'trim' => [
			'class' => 'yii\validators\FilterValidator',
			'filter' => 'trim',
		],
Qiang Xue committed
79 80
		'unique' => 'yii\validators\UniqueValidator',
		'url' => 'yii\validators\UrlValidator',
Alexander Makarov committed
81
	];
w  
Qiang Xue committed
82 83

	/**
84 85
	 * @var array|string attributes to be validated by this validator. For multiple attributes,
	 * please specify them as an array; for single attribute, you may use either a string or an array.
w  
Qiang Xue committed
86
	 */
Alexander Makarov committed
87
	public $attributes = [];
w  
Qiang Xue committed
88
	/**
89 90 91 92 93
	 * @var string the user-defined error message. It may contain the following placeholders which
	 * will be replaced accordingly by the validator:
	 *
	 * - `{attribute}`: the label of the attribute being validated
	 * - `{value}`: the value of the attribute being validated
w  
Qiang Xue committed
94 95 96
	 */
	public $message;
	/**
97 98
	 * @var array|string scenarios that the validator can be applied to. For multiple scenarios,
	 * please specify them as an array; for single scenario, you may use either a string or an array.
w  
Qiang Xue committed
99
	 */
Alexander Makarov committed
100
	public $on = [];
Qiang Xue committed
101
	/**
102 103
	 * @var array|string scenarios that the validator should not be applied to. For multiple scenarios,
	 * please specify them as an array; for single scenario, you may use either a string or an array.
Qiang Xue committed
104
	 */
Alexander Makarov committed
105
	public $except = [];
w  
Qiang Xue committed
106
	/**
w  
Qiang Xue committed
107
	 * @var boolean whether this validation rule should be skipped if the attribute being validated
Qiang Xue committed
108
	 * already has some validation error according to some previous rules. Defaults to true.
w  
Qiang Xue committed
109
	 */
w  
Qiang Xue committed
110
	public $skipOnError = true;
Qiang Xue committed
111 112 113 114 115
	/**
	 * @var boolean whether this validation rule should be skipped if the attribute value
	 * is null or an empty string.
	 */
	public $skipOnEmpty = true;
Qiang Xue committed
116 117 118 119 120 121 122
	/**
	 * @var boolean whether to enable client-side validation for this validator.
	 * The actual client-side validation is done via the JavaScript code returned
	 * by [[clientValidateAttribute()]]. If that method returns null, even if this property
	 * is true, no client-side validation will be done by this validator.
	 */
	public $enableClientValidation = true;
Qiang Xue committed
123

w  
Qiang Xue committed
124 125 126

	/**
	 * Creates a validator object.
127 128
	 * @param mixed $type the validator type. This can be a built-in validator name,
	 * a method name of the model class, an anonymous function, or a validator class name.
Qiang Xue committed
129 130
	 * @param \yii\base\Model $object the data object to be validated.
	 * @param array|string $attributes list of attributes to be validated. This can be either an array of
w  
Qiang Xue committed
131 132
	 * the attribute names or a string of comma-separated attribute names.
	 * @param array $params initial values to be applied to the validator properties
w  
Qiang Xue committed
133
	 * @return Validator the validator
w  
Qiang Xue committed
134
	 */
135
	public static function createValidator($type, $object, $attributes, $params = [])
w  
Qiang Xue committed
136
	{
slavcodev committed
137
		$params['attributes'] = $attributes;
Qiang Xue committed
138

139
		if ($type instanceof \Closure || method_exists($object, $type)) {
Alexander Makarov committed
140
			// method-based validator
Qiang Xue committed
141 142
			$params['class'] = __NAMESPACE__ . '\InlineValidator';
			$params['method'] = $type;
Qiang Xue committed
143
		} else {
144 145
			if (isset(static::$builtInValidators[$type])) {
				$type = static::$builtInValidators[$type];
w  
Qiang Xue committed
146
			}
Qiang Xue committed
147 148 149 150 151 152 153
			if (is_array($type)) {
				foreach ($type as $name => $value) {
					$params[$name] = $value;
				}
			} else {
				$params['class'] = $type;
			}
w  
Qiang Xue committed
154 155
		}

Qiang Xue committed
156
		return Yii::createObject($params);
w  
Qiang Xue committed
157 158
	}

159
	/**
Qiang Xue committed
160
	 * @inheritdoc
161 162 163 164
	 */
	public function init()
	{
		parent::init();
165 166 167
		$this->attributes = (array)$this->attributes;
		$this->on = (array)$this->on;
		$this->except = (array)$this->except;
168 169
	}

w  
Qiang Xue committed
170 171
	/**
	 * Validates the specified object.
w  
Qiang Xue committed
172
	 * @param \yii\base\Model $object the data object being validated
173 174 175 176
	 * @param array|null $attributes the list of attributes to be validated.
	 * Note that if an attribute is not associated with the validator,
	 * it will be ignored.
	 * If this parameter is null, every attribute listed in [[attributes]] will be validated.
w  
Qiang Xue committed
177
	 */
Qiang Xue committed
178
	public function validateAttributes($object, $attributes = null)
w  
Qiang Xue committed
179
	{
w  
Qiang Xue committed
180
		if (is_array($attributes)) {
w  
Qiang Xue committed
181
			$attributes = array_intersect($this->attributes, $attributes);
Qiang Xue committed
182
		} else {
w  
Qiang Xue committed
183
			$attributes = $this->attributes;
w  
Qiang Xue committed
184 185
		}
		foreach ($attributes as $attribute) {
Qiang Xue committed
186
			$skip = $this->skipOnError && $object->hasErrors($attribute)
resurtm committed
187
				|| $this->skipOnEmpty && $this->isEmpty($object->$attribute);
Qiang Xue committed
188
			if (!$skip) {
w  
Qiang Xue committed
189
				$this->validateAttribute($object, $attribute);
w  
Qiang Xue committed
190
			}
w  
Qiang Xue committed
191 192 193
		}
	}

Qiang Xue committed
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
	/**
	 * Validates a single attribute.
	 * Child classes must implement this method to provide the actual validation logic.
	 * @param \yii\base\Model $object the data object to be validated
	 * @param string $attribute the name of the attribute to be validated.
	 */
	public function validateAttribute($object, $attribute)
	{
		$result = $this->validateValue($object->$attribute);
		if (!empty($result)) {
			$this->addError($object, $attribute, $result[0], $result[1]);
		}
	}

	/**
	 * Validates a given value.
	 * You may use this method to validate a value out of the context of a data model.
	 * @param mixed $value the data value to be validated.
	 * @param string $error the error message to be returned, if the validation fails.
	 * @return boolean whether the data is valid.
	 */
	public function validate($value, &$error = null)
	{
		$result = $this->validateValue($value);
		if (empty($result)) {
			return true;
		} else {
			list($message, $params) = $result;
			$params['attribute'] = Yii::t('yii', 'the input value');
			$params['value'] = is_array($value) ? 'array()' : $value;
			$error = Yii::$app->getI18n()->format($message, $params, Yii::$app->language);
			return false;
		}
	}

Qiang Xue committed
229 230 231 232
	/**
	 * 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.
Qiang Xue committed
233 234 235
	 * @return array|null the error message and the parameters to be inserted into the error message.
	 * Null should be returned if the data is valid.
	 * @throws NotSupportedException if the validator does not supporting data validation without a model
Qiang Xue committed
236
	 */
Qiang Xue committed
237
	protected function validateValue($value)
Qiang Xue committed
238
	{
239
		throw new NotSupportedException(get_class($this) . ' does not support validateValue().');
Qiang Xue committed
240 241
	}

w  
Qiang Xue committed
242 243
	/**
	 * Returns the JavaScript needed for performing client-side validation.
w  
Qiang Xue committed
244 245 246 247 248 249 250 251 252 253 254
	 *
	 * You may override this method to return the JavaScript validation code if
	 * the validator can support client-side validation.
	 *
	 * The following JavaScript variables are predefined and can be used in the validation code:
	 *
	 * - `attribute`: the name of the attribute being validated.
	 * - `value`: the value being validated.
	 * - `messages`: an array used to hold the validation error messages for the attribute.
	 *
	 * @param \yii\base\Model $object the data object being validated
w  
Qiang Xue committed
255
	 * @param string $attribute the name of the attribute to be validated.
Alexander Makarov committed
256
	 * @param \yii\web\View $view the view object that is going to be used to render views or view files
257
	 * containing a model form with this validator applied.
w  
Qiang Xue committed
258 259
	 * @return string the client-side validation script. Null if the validator does not support
	 * client-side validation.
260
	 * @see \yii\widgets\ActiveForm::enableClientValidation
w  
Qiang Xue committed
261
	 */
262
	public function clientValidateAttribute($object, $attribute, $view)
w  
Qiang Xue committed
263
	{
Qiang Xue committed
264
		return null;
w  
Qiang Xue committed
265 266 267
	}

	/**
268 269 270
	 * Returns a value indicating whether the validator is active for the given scenario and attribute.
	 *
	 * A validator is active if
w  
Qiang Xue committed
271
	 *
272
	 * - the validator's `on` property is empty, or
w  
Qiang Xue committed
273 274
	 * - the validator's `on` property contains the specified scenario
	 *
w  
Qiang Xue committed
275 276 277
	 * @param string $scenario scenario name
	 * @return boolean whether the validator applies to the specified scenario.
	 */
Qiang Xue committed
278
	public function isActive($scenario)
w  
Qiang Xue committed
279
	{
Qiang Xue committed
280
		return !in_array($scenario, $this->except, true) && (empty($this->on) || in_array($scenario, $this->on, true));
w  
Qiang Xue committed
281 282 283
	}

	/**
w  
Qiang Xue committed
284
	 * Adds an error about the specified attribute to the model object.
w  
Qiang Xue committed
285
	 * This is a helper method that performs message selection and internationalization.
w  
Qiang Xue committed
286
	 * @param \yii\base\Model $object the data object being validated
w  
Qiang Xue committed
287 288 289 290
	 * @param string $attribute the attribute being validated
	 * @param string $message the error message
	 * @param array $params values for the placeholders in the error message
	 */
Alexander Makarov committed
291
	public function addError($object, $attribute, $message, $params = [])
w  
Qiang Xue committed
292
	{
Qiang Xue committed
293
		$value = $object->$attribute;
294 295 296
		$params['attribute'] = $object->getAttributeLabel($attribute);
		$params['value'] = is_array($value) ? 'array()' : $value;
		$object->addError($attribute, Yii::$app->getI18n()->format($message, $params, Yii::$app->language));
w  
Qiang Xue committed
297 298 299 300 301 302 303 304 305 306
	}

	/**
	 * Checks if the given value is empty.
	 * A value is considered empty if it is null, an empty array, or the trimmed result is an empty string.
	 * Note that this method is different from PHP empty(). It will return false when the value is 0.
	 * @param mixed $value the value to be checked
	 * @param boolean $trim whether to perform trimming before checking if the string is empty. Defaults to false.
	 * @return boolean whether the value is empty
	 */
w  
Qiang Xue committed
307
	public function isEmpty($value, $trim = false)
w  
Qiang Xue committed
308
	{
Alexander Makarov committed
309
		return $value === null || $value === [] || $value === ''
Qiang Xue committed
310
			|| $trim && is_scalar($value) && trim($value) === '';
w  
Qiang Xue committed
311 312
	}
}