1: <?php
2: /**
3: * CValidator class file.
4: *
5: * @author Qiang Xue <qiang.xue@gmail.com>
6: * @link http://www.yiiframework.com/
7: * @copyright 2008-2013 Yii Software LLC
8: * @license http://www.yiiframework.com/license/
9: */
10:
11: /**
12: * CValidator is the base class for all validators.
13: *
14: * Child classes must implement the {@link validateAttribute} method.
15: *
16: * When using {@link createValidator} to create a validator, the following aliases
17: * are recognized as the corresponding built-in validator classes:
18: * <ul>
19: * <li>required: {@link CRequiredValidator}</li>
20: * <li>filter: {@link CFilterValidator}</li>
21: * <li>match: {@link CRegularExpressionValidator}</li>
22: * <li>email: {@link CEmailValidator}</li>
23: * <li>url: {@link CUrlValidator}</li>
24: * <li>unique: {@link CUniqueValidator}</li>
25: * <li>compare: {@link CCompareValidator}</li>
26: * <li>length: {@link CStringValidator}</li>
27: * <li>in: {@link CRangeValidator}</li>
28: * <li>numerical: {@link CNumberValidator}</li>
29: * <li>captcha: {@link CCaptchaValidator}</li>
30: * <li>type: {@link CTypeValidator}</li>
31: * <li>file: {@link CFileValidator}</li>
32: * <li>default: {@link CDefaultValueValidator}</li>
33: * <li>exist: {@link CExistValidator}</li>
34: * <li>boolean: {@link CBooleanValidator}</li>
35: * <li>date: {@link CDateValidator}</li>
36: * <li>safe: {@link CSafeValidator}</li>
37: * <li>unsafe: {@link CUnsafeValidator}</li>
38: * </ul>
39: *
40: * @author Qiang Xue <qiang.xue@gmail.com>
41: * @package system.validators
42: * @since 1.0
43: */
44: abstract class CValidator extends CComponent
45: {
46: /**
47: * @var array list of built-in validators (name=>class)
48: */
49: public static $builtInValidators=array(
50: 'required'=>'CRequiredValidator',
51: 'filter'=>'CFilterValidator',
52: 'match'=>'CRegularExpressionValidator',
53: 'email'=>'CEmailValidator',
54: 'url'=>'CUrlValidator',
55: 'unique'=>'CUniqueValidator',
56: 'compare'=>'CCompareValidator',
57: 'length'=>'CStringValidator',
58: 'in'=>'CRangeValidator',
59: 'numerical'=>'CNumberValidator',
60: 'captcha'=>'CCaptchaValidator',
61: 'type'=>'CTypeValidator',
62: 'file'=>'CFileValidator',
63: 'default'=>'CDefaultValueValidator',
64: 'exist'=>'CExistValidator',
65: 'boolean'=>'CBooleanValidator',
66: 'safe'=>'CSafeValidator',
67: 'unsafe'=>'CUnsafeValidator',
68: 'date'=>'CDateValidator',
69: );
70:
71: /**
72: * @var array list of attributes to be validated.
73: */
74: public $attributes;
75: /**
76: * @var string the user-defined error message. Different validators may define various
77: * placeholders in the message that are to be replaced with actual values. All validators
78: * recognize "{attribute}" placeholder, which will be replaced with the label of the attribute.
79: */
80: public $message;
81: /**
82: * @var boolean whether this validation rule should be skipped when there is already a validation
83: * error for the current attribute. Defaults to false.
84: * @since 1.1.1
85: */
86: public $skipOnError=false;
87: /**
88: * @var array list of scenarios that the validator should be applied.
89: * Each array value refers to a scenario name with the same name as its array key.
90: */
91: public $on;
92: /**
93: * @var array list of scenarios that the validator should not be applied to.
94: * Each array value refers to a scenario name with the same name as its array key.
95: * @since 1.1.11
96: */
97: public $except;
98: /**
99: * @var boolean whether attributes listed with this validator should be considered safe for massive assignment.
100: * Defaults to true.
101: * @since 1.1.4
102: */
103: public $safe=true;
104: /**
105: * @var boolean whether to perform client-side validation. Defaults to true.
106: * Please refer to {@link CActiveForm::enableClientValidation} for more details about client-side validation.
107: * @since 1.1.7
108: */
109: public $enableClientValidation=true;
110:
111: /**
112: * Validates a single attribute.
113: * This method should be overridden by child classes.
114: * @param CModel $object the data object being validated
115: * @param string $attribute the name of the attribute to be validated.
116: */
117: abstract protected function validateAttribute($object,$attribute);
118:
119:
120: /**
121: * Creates a validator object.
122: * @param string $name the name or class of the validator
123: * @param CModel $object the data object being validated that may contain the inline validation method
124: * @param mixed $attributes list of attributes to be validated. This can be either an array of
125: * the attribute names or a string of comma-separated attribute names.
126: * @param array $params initial values to be applied to the validator properties
127: * @return CValidator the validator
128: */
129: public static function createValidator($name,$object,$attributes,$params=array())
130: {
131: if(is_string($attributes))
132: $attributes=preg_split('/\s*,\s*/',$attributes,-1,PREG_SPLIT_NO_EMPTY);
133:
134: if(isset($params['on']))
135: {
136: if(is_array($params['on']))
137: $on=$params['on'];
138: else
139: $on=preg_split('/[\s,]+/',$params['on'],-1,PREG_SPLIT_NO_EMPTY);
140: }
141: else
142: $on=array();
143:
144: if(isset($params['except']))
145: {
146: if(is_array($params['except']))
147: $except=$params['except'];
148: else
149: $except=preg_split('/[\s,]+/',$params['except'],-1,PREG_SPLIT_NO_EMPTY);
150: }
151: else
152: $except=array();
153:
154: if(method_exists($object,$name))
155: {
156: $validator=new CInlineValidator;
157: $validator->attributes=$attributes;
158: $validator->method=$name;
159: if(isset($params['clientValidate']))
160: {
161: $validator->clientValidate=$params['clientValidate'];
162: unset($params['clientValidate']);
163: }
164: $validator->params=$params;
165: if(isset($params['skipOnError']))
166: $validator->skipOnError=$params['skipOnError'];
167: }
168: else
169: {
170: $params['attributes']=$attributes;
171: if(isset(self::$builtInValidators[$name]))
172: $className=Yii::import(self::$builtInValidators[$name],true);
173: else
174: $className=Yii::import($name,true);
175: $validator=new $className;
176: foreach($params as $name=>$value)
177: $validator->$name=$value;
178: }
179:
180: $validator->on=empty($on) ? array() : array_combine($on,$on);
181: $validator->except=empty($except) ? array() : array_combine($except,$except);
182:
183: return $validator;
184: }
185:
186: /**
187: * Validates the specified object.
188: * @param CModel $object the data object being validated
189: * @param array $attributes the list of attributes to be validated. Defaults to null,
190: * meaning every attribute listed in {@link attributes} will be validated.
191: */
192: public function validate($object,$attributes=null)
193: {
194: if(is_array($attributes))
195: $attributes=array_intersect($this->attributes,$attributes);
196: else
197: $attributes=$this->attributes;
198: foreach($attributes as $attribute)
199: {
200: if(!$this->skipOnError || !$object->hasErrors($attribute))
201: $this->validateAttribute($object,$attribute);
202: }
203: }
204:
205: /**
206: * Returns the JavaScript needed for performing client-side validation.
207: * Do not override this method if the validator does not support client-side validation.
208: * Two predefined JavaScript variables can be used:
209: * <ul>
210: * <li>value: the value to be validated</li>
211: * <li>messages: an array used to hold the validation error messages for the value</li>
212: * </ul>
213: * @param CModel $object the data object being validated
214: * @param string $attribute the name of the attribute to be validated.
215: * @return string the client-side validation script. Null if the validator does not support client-side validation.
216: * @see CActiveForm::enableClientValidation
217: * @since 1.1.7
218: */
219: public function clientValidateAttribute($object,$attribute)
220: {
221: }
222:
223: /**
224: * Returns a value indicating whether the validator applies to the specified scenario.
225: * A validator applies to a scenario as long as any of the following conditions is met:
226: * <ul>
227: * <li>the validator's "on" property is empty</li>
228: * <li>the validator's "on" property contains the specified scenario</li>
229: * </ul>
230: * @param string $scenario scenario name
231: * @return boolean whether the validator applies to the specified scenario.
232: */
233: public function applyTo($scenario)
234: {
235: if(isset($this->except[$scenario]))
236: return false;
237: return empty($this->on) || isset($this->on[$scenario]);
238: }
239:
240: /**
241: * Adds an error about the specified attribute to the active record.
242: * This is a helper method that performs message selection and internationalization.
243: * @param CModel $object the data object being validated
244: * @param string $attribute the attribute being validated
245: * @param string $message the error message
246: * @param array $params values for the placeholders in the error message
247: */
248: protected function addError($object,$attribute,$message,$params=array())
249: {
250: $params['{attribute}']=$object->getAttributeLabel($attribute);
251: $object->addError($attribute,strtr($message,$params));
252: }
253:
254: /**
255: * Checks if the given value is empty.
256: * A value is considered empty if it is null, an empty array, or the trimmed result is an empty string.
257: * Note that this method is different from PHP empty(). It will return false when the value is 0.
258: * @param mixed $value the value to be checked
259: * @param boolean $trim whether to perform trimming before checking if the string is empty. Defaults to false.
260: * @return boolean whether the value is empty
261: */
262: protected function isEmpty($value,$trim=false)
263: {
264: return $value===null || $value===array() || $value==='' || $trim && is_scalar($value) && trim($value)==='';
265: }
266: }
267:
268: