1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Forms;
9:
10: use Nette;
11:
12:
13: 14: 15: 16: 17:
18: class Rules extends Nette\Object implements \IteratorAggregate
19: {
20:
21: const VALIDATE_PREFIX = 'validate';
22:
23:
24: public static $defaultMessages = array(
25: Form::PROTECTION => 'Please submit this form again (security token has expired).',
26: Form::EQUAL => 'Please enter %s.',
27: Form::NOT_EQUAL => 'This value should not be %s.',
28: Form::FILLED => 'This field is required.',
29: Form::BLANK => 'This field should be blank.',
30: Form::MIN_LENGTH => 'Please enter at least %d characters.',
31: Form::MAX_LENGTH => 'Please enter no more than %d characters.',
32: Form::LENGTH => 'Please enter a value between %d and %d characters long.',
33: Form::EMAIL => 'Please enter a valid email address.',
34: Form::URL => 'Please enter a valid URL.',
35: Form::INTEGER => 'Please enter a valid integer.',
36: Form::FLOAT => 'Please enter a valid number.',
37: Form::RANGE => 'Please enter a value between %d and %d.',
38: Form::MAX_FILE_SIZE => 'The size of the uploaded file can be up to %d bytes.',
39: Form::MAX_POST_SIZE => 'The uploaded data exceeds the limit of %d bytes.',
40: Form::IMAGE => 'The uploaded file must be image in format JPEG, GIF or PNG.',
41: Nette\Forms\Controls\SelectBox::VALID => 'Please select a valid option.',
42: );
43:
44:
45: private $required;
46:
47:
48: private $rules = array();
49:
50:
51: private $parent;
52:
53:
54: private $toggles = array();
55:
56:
57: private $control;
58:
59:
60: public function __construct(IControl $control)
61: {
62: $this->control = $control;
63: }
64:
65:
66: 67: 68: 69: 70:
71: public function setRequired($value = TRUE)
72: {
73: if ($value) {
74: $this->addRule(Form::REQUIRED, is_string($value) ? $value : NULL);
75: } else {
76: $this->required = NULL;
77: }
78: return $this;
79: }
80:
81:
82: 83: 84: 85:
86: public function isRequired()
87: {
88: return $this->required instanceof Rule ? !$this->required->isNegative : FALSE;
89: }
90:
91:
92: 93: 94: 95: 96: 97: 98:
99: public function addRule($operation, $message = NULL, $arg = NULL)
100: {
101: $rule = new Rule;
102: $rule->control = $this->control;
103: $rule->operation = $operation;
104: $this->adjustOperation($rule);
105: $rule->arg = $arg;
106: $rule->type = Rule::VALIDATOR;
107: $rule->message = $message;
108: if ($rule->operation === Form::REQUIRED) {
109: $this->required = $rule;
110: } else {
111: $this->rules[] = $rule;
112: }
113: return $this;
114: }
115:
116:
117: 118: 119: 120: 121: 122:
123: public function addCondition($operation, $arg = NULL)
124: {
125: return $this->addConditionOn($this->control, $operation, $arg);
126: }
127:
128:
129: 130: 131: 132: 133: 134: 135:
136: public function addConditionOn(IControl $control, $operation, $arg = NULL)
137: {
138: $rule = new Rule;
139: $rule->control = $control;
140: $rule->operation = $operation;
141: $this->adjustOperation($rule);
142: $rule->arg = $arg;
143: $rule->type = Rule::CONDITION;
144: $rule->subRules = new static($this->control);
145: $rule->subRules->parent = $this;
146:
147: $this->rules[] = $rule;
148: return $rule->subRules;
149: }
150:
151:
152: 153: 154: 155:
156: public function elseCondition()
157: {
158: $rule = clone end($this->parent->rules);
159: $rule->isNegative = !$rule->isNegative;
160: $rule->subRules = new static($this->parent->control);
161: $rule->subRules->parent = $this->parent;
162: $this->parent->rules[] = $rule;
163: return $rule->subRules;
164: }
165:
166:
167: 168: 169: 170:
171: public function endCondition()
172: {
173: return $this->parent;
174: }
175:
176:
177: 178: 179: 180: 181: 182:
183: public function toggle($id, $hide = TRUE)
184: {
185: $this->toggles[$id] = $hide;
186: return $this;
187: }
188:
189:
190: 191: 192: 193:
194: public function validate()
195: {
196: foreach ($this as $rule) {
197: $success = $this->validateRule($rule);
198:
199: if ($rule->type === Rule::CONDITION && $success && !$rule->subRules->validate()) {
200: return FALSE;
201:
202: } elseif ($rule->type === Rule::VALIDATOR && !$success) {
203: $rule->control->addError(static::formatMessage($rule, TRUE));
204: return FALSE;
205: }
206: }
207: return TRUE;
208: }
209:
210:
211: 212: 213: 214:
215: public static function validateRule(Rule $rule)
216: {
217: $args = is_array($rule->arg) ? $rule->arg : array($rule->arg);
218: foreach ($args as & $val) {
219: $val = $val instanceof IControl ? $val->getValue() : $val;
220: }
221: return $rule->isNegative
222: xor call_user_func(self::getCallback($rule), $rule->control, is_array($rule->arg) ? $args : $args[0]);
223: }
224:
225:
226: 227: 228: 229:
230: public function getIterator()
231: {
232: $rules = $this->rules;
233: if ($this->required) {
234: array_unshift($rules, $this->required);
235: }
236: return new \ArrayIterator($rules);
237: }
238:
239:
240: 241: 242: 243:
244: public function getToggles($actual = FALSE)
245: {
246: $toggles = $this->toggles;
247: foreach ($actual ? $this : array() as $rule) {
248: if ($rule->type === Rule::CONDITION) {
249: $success = static::validateRule($rule);
250: foreach ($rule->subRules->getToggles(TRUE) as $id => $hide) {
251: $toggles[$id] = empty($toggles[$id]) ? ($success && $hide) : TRUE;
252: }
253: }
254: }
255: return $toggles;
256: }
257:
258:
259: 260: 261: 262: 263:
264: private function adjustOperation($rule)
265: {
266: if (is_string($rule->operation) && ord($rule->operation[0]) > 127) {
267: $rule->isNegative = TRUE;
268: $rule->operation = ~$rule->operation;
269: }
270:
271: if (!is_callable($this->getCallback($rule))) {
272: $operation = is_scalar($rule->operation) ? " '$rule->operation'" : '';
273: throw new Nette\InvalidArgumentException("Unknown operation$operation for control '{$rule->control->name}'.");
274: }
275: }
276:
277:
278: private static function getCallback($rule)
279: {
280: $op = $rule->operation;
281: if (is_string($op) && strncmp($op, ':', 1) === 0) {
282: return get_class($rule->control) . '::' . self::VALIDATE_PREFIX . ltrim($op, ':');
283: } else {
284: return $op;
285: }
286: }
287:
288:
289: public static function formatMessage($rule, $withValue)
290: {
291: $message = $rule->message;
292: if ($message instanceof Nette\Utils\Html) {
293: return $message;
294:
295: } elseif ($message === NULL && is_string($rule->operation) && isset(self::$defaultMessages[$rule->operation])) {
296: $message = self::$defaultMessages[$rule->operation];
297:
298: } elseif ($message == NULL) {
299: trigger_error("Missing validation message for control '{$rule->control->name}'.", E_USER_WARNING);
300: }
301:
302: if ($translator = $rule->control->getForm()->getTranslator()) {
303: $message = $translator->translate($message, is_int($rule->arg) ? $rule->arg : NULL);
304: }
305:
306: $message = preg_replace_callback('#%(name|label|value|\d+\$[ds]|[ds])#', function($m) use ($rule, $withValue) {
307: static $i = -1;
308: switch ($m[1]) {
309: case 'name': return $rule->control->getName();
310: case 'label': return $rule->control->translate($rule->control->caption);
311: case 'value': return $withValue ? $rule->control->getValue() : $m[0];
312: default:
313: $args = is_array($rule->arg) ? $rule->arg : array($rule->arg);
314: $i = (int) $m[1] ? $m[1] - 1 : $i + 1;
315: return isset($args[$i]) ? ($args[$i] instanceof IControl ? ($withValue ? $args[$i]->getValue() : "%$i") : $args[$i]) : '';
316: }
317: }, $message);
318: return $message;
319: }
320:
321:
322: }
323: