1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20:
21: final class NRules extends NObject implements IteratorAggregate
22: {
23:
24: const VALIDATE_PREFIX = 'validate';
25:
26:
27: public static $defaultMessages = array(
28: NForm::PROTECTION => 'Please submit this form again (security token has expired).',
29: NForm::EQUAL => 'Please enter %s.',
30: NForm::FILLED => 'Please complete mandatory field.',
31: NForm::MIN_LENGTH => 'Please enter a value of at least %d characters.',
32: NForm::MAX_LENGTH => 'Please enter a value no longer than %d characters.',
33: NForm::LENGTH => 'Please enter a value between %d and %d characters long.',
34: NForm::EMAIL => 'Please enter a valid email address.',
35: NForm::URL => 'Please enter a valid URL.',
36: NForm::INTEGER => 'Please enter a numeric value.',
37: NForm::FLOAT => 'Please enter a numeric value.',
38: NForm::RANGE => 'Please enter a value between %d and %d.',
39: NForm::MAX_FILE_SIZE => 'The size of the uploaded file can be up to %d bytes.',
40: NForm::IMAGE => 'The uploaded file must be image in format JPEG, GIF or PNG.',
41: );
42:
43:
44: private $rules = array();
45:
46:
47: private $parent;
48:
49:
50: private $toggles = array();
51:
52:
53: private $control;
54:
55:
56: public function __construct(IFormControl $control)
57: {
58: $this->control = $control;
59: }
60:
61:
62: 63: 64: 65: 66: 67: 68:
69: public function addRule($operation, $message = NULL, $arg = NULL)
70: {
71: $rule = new NRule;
72: $rule->control = $this->control;
73: $rule->operation = $operation;
74: $this->adjustOperation($rule);
75: $rule->arg = $arg;
76: $rule->type = NRule::VALIDATOR;
77: if ($message === NULL && is_string($rule->operation) && isset(self::$defaultMessages[$rule->operation])) {
78: $rule->message = self::$defaultMessages[$rule->operation];
79: } else {
80: $rule->message = $message;
81: }
82: $this->rules[] = $rule;
83: return $this;
84: }
85:
86:
87: 88: 89: 90: 91: 92:
93: public function addCondition($operation, $arg = NULL)
94: {
95: return $this->addConditionOn($this->control, $operation, $arg);
96: }
97:
98:
99: 100: 101: 102: 103: 104: 105:
106: public function addConditionOn(IFormControl $control, $operation, $arg = NULL)
107: {
108: $rule = new NRule;
109: $rule->control = $control;
110: $rule->operation = $operation;
111: $this->adjustOperation($rule);
112: $rule->arg = $arg;
113: $rule->type = NRule::CONDITION;
114: $rule->subRules = new self($this->control);
115: $rule->subRules->parent = $this;
116:
117: $this->rules[] = $rule;
118: return $rule->subRules;
119: }
120:
121:
122: 123: 124: 125:
126: public function elseCondition()
127: {
128: $rule = clone end($this->parent->rules);
129: $rule->isNegative = !$rule->isNegative;
130: $rule->subRules = new self($this->parent->control);
131: $rule->subRules->parent = $this->parent;
132: $this->parent->rules[] = $rule;
133: return $rule->subRules;
134: }
135:
136:
137: 138: 139: 140:
141: public function endCondition()
142: {
143: return $this->parent;
144: }
145:
146:
147: 148: 149: 150: 151: 152:
153: public function toggle($id, $hide = TRUE)
154: {
155: $this->toggles[$id] = $hide;
156: return $this;
157: }
158:
159:
160: 161: 162: 163: 164:
165: public function validate($onlyCheck = FALSE)
166: {
167: foreach ($this->rules as $rule) {
168: if ($rule->control->isDisabled()) {
169: continue;
170: }
171:
172: $success = ($rule->isNegative xor $this->getCallback($rule)->invoke($rule->control, $rule->arg));
173:
174: if ($rule->type === NRule::CONDITION && $success) {
175: if (!$rule->subRules->validate($onlyCheck)) {
176: return FALSE;
177: }
178:
179: } elseif ($rule->type === NRule::VALIDATOR && !$success) {
180: if (!$onlyCheck) {
181: $rule->control->addError(self::formatMessage($rule, TRUE));
182: }
183: return FALSE;
184: }
185: }
186: return TRUE;
187: }
188:
189:
190: 191: 192: 193:
194: final public function getIterator()
195: {
196: return new ArrayIterator($this->rules);
197: }
198:
199:
200: 201: 202:
203: final public function getToggles()
204: {
205: return $this->toggles;
206: }
207:
208:
209: 210: 211: 212: 213:
214: private function adjustOperation($rule)
215: {
216: if (is_string($rule->operation) && ord($rule->operation[0]) > 127) {
217: $rule->isNegative = TRUE;
218: $rule->operation = ~$rule->operation;
219: }
220:
221: if (!$this->getCallback($rule)->isCallable()) {
222: $operation = is_scalar($rule->operation) ? " '$rule->operation'" : '';
223: throw new InvalidArgumentException("Unknown operation$operation for control '{$rule->control->name}'.");
224: }
225: }
226:
227:
228: private function getCallback($rule)
229: {
230: $op = $rule->operation;
231: if (is_string($op) && strncmp($op, ':', 1) === 0) {
232: return new NCallback(get_class($rule->control), self::VALIDATE_PREFIX . ltrim($op, ':'));
233: } else {
234: return new NCallback($op);
235: }
236: }
237:
238:
239: public static function formatMessage($rule, $withValue)
240: {
241: $message = $rule->message;
242: if ($message instanceof NHtml) {
243: return $message;
244: }
245: if ($message == NULL) {
246: trigger_error("Missing validation message for control '{$rule->control->name}'.", E_USER_WARNING);
247: }
248: if ($translator = $rule->control->getForm()->getTranslator()) {
249: $message = $translator->translate($message, is_int($rule->arg) ? $rule->arg : NULL);
250: }
251: $message = vsprintf(preg_replace('#%(name|label|value)#', '%$0', $message), (array) $rule->arg);
252: $message = str_replace('%name', $rule->control->getName(), $message);
253: $message = str_replace('%label', $rule->control->translate($rule->control->caption), $message);
254: if ($withValue && strpos($message, '%value') !== FALSE) {
255: $message = str_replace('%value', $rule->control->getValue(), $message);
256: }
257: return $message;
258: }
259:
260: }
261: