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