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