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