1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Forms;
13:
14: use Nette;
15:
16:
17:
18: 19: 20: 21: 22:
23: final class Rules extends Nette\Object implements \IteratorAggregate
24: {
25:
26: const VALIDATE_PREFIX = 'validate';
27:
28:
29: public static $defaultMessages = array(
30: ':protection' => 'Security token did not match. Possible CSRF attack.',
31: );
32:
33:
34: private $rules = array();
35:
36:
37: private $parent;
38:
39:
40: private $toggles = array();
41:
42:
43: private $control;
44:
45:
46:
47: public function __construct(IFormControl $control)
48: {
49: $this->control = $control;
50: }
51:
52:
53:
54: 55: 56: 57: 58: 59: 60:
61: public function addRule($operation, $message = NULL, $arg = NULL)
62: {
63: $rule = new Rule;
64: $rule->control = $this->control;
65: $rule->operation = ($operation === ':protection' ? ':equal' : $operation);
66: $this->adjustOperation($rule);
67: $rule->arg = $arg;
68: $rule->type = Rule::VALIDATOR;
69: if ($message === NULL && is_string($operation) && isset(self::$defaultMessages[$operation])) {
70: $rule->message = self::$defaultMessages[$operation];
71: } else {
72: $rule->message = $message;
73: }
74:
75: if ($this->parent === NULL) {
76: 77: $this->control->notifyRule($rule);
78: }
79: $this->rules[] = $rule;
80: return $this;
81: }
82:
83:
84:
85: 86: 87: 88: 89: 90:
91: public function addCondition($operation, $arg = NULL)
92: {
93: return $this->addConditionOn($this->control, $operation, $arg);
94: }
95:
96:
97:
98: 99: 100: 101: 102: 103: 104:
105: public function addConditionOn(IFormControl $control, $operation, $arg = NULL)
106: {
107: $rule = new Rule;
108: $rule->control = $control;
109: $rule->operation = $operation;
110: $this->adjustOperation($rule);
111: $rule->arg = $arg;
112: $rule->type = Rule::CONDITION;
113: $rule->subRules = new self($this->control);
114: $rule->subRules->parent = $this;
115:
116: $this->rules[] = $rule;
117: return $rule->subRules;
118: }
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:
142: public function endCondition()
143: {
144: return $this->parent;
145: }
146:
147:
148:
149: 150: 151: 152: 153: 154:
155: public function toggle($id, $hide = TRUE)
156: {
157: $this->toggles[$id] = $hide;
158: return $this;
159: }
160:
161:
162:
163: 164: 165: 166: 167:
168: public function validate($onlyCheck = FALSE)
169: {
170: foreach ($this->rules as $rule)
171: {
172: if ($rule->control->isDisabled()) continue;
173:
174: $success = ($rule->isNegative xor $this->getCallback($rule)->invoke($rule->control, $rule->arg));
175:
176: if ($rule->type === Rule::CONDITION && $success) {
177: if (!$rule->subRules->validate($onlyCheck)) {
178: return FALSE;
179: }
180:
181: } elseif ($rule->type === Rule::VALIDATOR && !$success) {
182: if (!$onlyCheck) {
183: $rule->control->addError(self::formatMessage($rule, TRUE));
184: }
185: return FALSE;
186: }
187: }
188: return TRUE;
189: }
190:
191:
192:
193: 194: 195: 196:
197: final public function getIterator()
198: {
199: return new \ArrayIterator($this->rules);
200: }
201:
202:
203:
204: 205: 206:
207: final public function getToggles()
208: {
209: return $this->toggles;
210: }
211:
212:
213:
214: 215: 216: 217: 218:
219: private function adjustOperation($rule)
220: {
221: if (is_string($rule->operation) && ord($rule->operation[0]) > 127) {
222: $rule->isNegative = TRUE;
223: $rule->operation = ~$rule->operation;
224: }
225:
226: if (!$this->getCallback($rule)->isCallable()) {
227: $operation = is_scalar($rule->operation) ? " '$rule->operation'" : '';
228: throw new \InvalidArgumentException("Unknown operation$operation for control '{$rule->control->name}'.");
229: }
230: }
231:
232:
233:
234: private function getCallback($rule)
235: {
236: $op = $rule->operation;
237: if (is_string($op) && strncmp($op, ':', 1) === 0) {
238: return callback(get_class($rule->control), self::VALIDATE_PREFIX . ltrim($op, ':'));
239: } else {
240: return callback($op);
241: }
242: }
243:
244:
245:
246: public static function formatMessage($rule, $withValue)
247: {
248: $message = $rule->message;
249: if (!isset($message)) { 250: $message = self::$defaultMessages[$rule->operation];
251: }
252: if ($translator = $rule->control->getForm()->getTranslator()) {
253: $message = $translator->translate($message, is_int($rule->arg) ? $rule->arg : NULL);
254: }
255: $message = vsprintf(preg_replace('#%(name|label|value)#', '%$0', $message), (array) $rule->arg);
256: $message = str_replace('%name', $rule->control->getName(), $message);
257: $message = str_replace('%label', $rule->control->translate($rule->control->caption), $message);
258: if ($withValue && strpos($message, '%value') !== FALSE) {
259: $message = str_replace('%value', $rule->control->getValue(), $message);
260: }
261: return $message;
262: }
263:
264: }
265: