PluginProbe
Packeta / 2.1
Packeta v2.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / forms / src / Forms / Rules.php

Rules.php in Packeta 2.1, at deps/nette/forms/src/Forms/Rules.php

277 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\Forms;
9
10 use Packetery\Nette;
11 /**
12 * List of validation & condition rules.
13 */
14 class Rules implements \IteratorAggregate
15 {
16 use \Packetery\Nette\SmartObject;
17 private const NEG_RULES = [Form::FILLED => Form::BLANK, Form::BLANK => Form::FILLED];
18 /** @var Rule|null */
19 private $required;
20 /** @var Rule[] */
21 private $rules = [];
22 /** @var Rules */
23 private $parent;
24 /** @var array */
25 private $toggles = [];
26 /** @var Control */
27 private $control;
28 public function __construct(Control $control)
29 {
30 $this->control = $control;
31 }
32 /**
33 * Makes control mandatory.
34 * @param string|bool $value
35 * @return static
36 */
37 public function setRequired($value = \true)
38 {
39 if ($value) {
40 $this->addRule(Form::FILLED, $value === \true ? null : $value);
41 } else {
42 $this->required = null;
43 }
44 return $this;
45 }
46 /**
47 * Is control mandatory?
48 */
49 public function isRequired() : bool
50 {
51 return (bool) $this->required;
52 }
53 /**
54 * Adds a validation rule for the current control.
55 * @param callable|string $validator
56 * @param string|object $errorMessage
57 * @return static
58 */
59 public function addRule($validator, $errorMessage = null, $arg = null)
60 {
61 if ($validator === Form::VALID || $validator === ~Form::VALID) {
62 throw new \Packetery\Nette\InvalidArgumentException('You cannot use Form::VALID in the addRule method.');
63 }
64 $rule = new Rule();
65 $rule->control = $this->control;
66 $rule->validator = $validator;
67 $this->adjustOperation($rule);
68 $rule->arg = $arg;
69 $rule->message = $errorMessage;
70 if ($rule->validator === Form::FILLED) {
71 $this->required = $rule;
72 } else {
73 $this->rules[] = $rule;
74 }
75 return $this;
76 }
77 /**
78 * Removes a validation rule for the current control.
79 * @param callable|string $validator
80 * @return static
81 */
82 public function removeRule($validator)
83 {
84 if ($validator === Form::FILLED) {
85 $this->required = null;
86 } else {
87 foreach ($this->rules as $i => $rule) {
88 if (!$rule->branch && $rule->validator === $validator) {
89 unset($this->rules[$i]);
90 }
91 }
92 }
93 return $this;
94 }
95 /**
96 * Adds a validation condition and returns new branch.
97 * @return static new branch
98 */
99 public function addCondition($validator, $arg = null)
100 {
101 if ($validator === Form::VALID || $validator === ~Form::VALID) {
102 throw new \Packetery\Nette\InvalidArgumentException('You cannot use Form::VALID in the addCondition method.');
103 } elseif (\is_bool($validator)) {
104 $arg = $validator;
105 $validator = ':static';
106 }
107 return $this->addConditionOn($this->control, $validator, $arg);
108 }
109 /**
110 * Adds a validation condition on specified control a returns new branch.
111 * @return static new branch
112 */
113 public function addConditionOn(Control $control, $validator, $arg = null)
114 {
115 $rule = new Rule();
116 $rule->control = $control;
117 $rule->validator = $validator;
118 $rule->arg = $arg;
119 $rule->branch = new static($this->control);
120 $rule->branch->parent = $this;
121 $this->adjustOperation($rule);
122 $this->rules[] = $rule;
123 return $rule->branch;
124 }
125 /**
126 * Adds a else statement.
127 * @return static else branch
128 */
129 public function elseCondition()
130 {
131 $rule = clone \end($this->parent->rules);
132 if (isset(self::NEG_RULES[$rule->validator])) {
133 $rule->validator = self::NEG_RULES[$rule->validator];
134 } else {
135 $rule->isNegative = !$rule->isNegative;
136 }
137 $rule->branch = new static($this->parent->control);
138 $rule->branch->parent = $this->parent;
139 $this->parent->rules[] = $rule;
140 return $rule->branch;
141 }
142 /**
143 * Ends current validation condition.
144 * @return Rules parent branch
145 */
146 public function endCondition() : self
147 {
148 return $this->parent;
149 }
150 /**
151 * Adds a filter callback.
152 * @return static
153 */
154 public function addFilter(callable $filter)
155 {
156 $this->rules[] = $rule = new Rule();
157 $rule->control = $this->control;
158 $rule->validator = function (Control $control) use($filter) : bool {
159 $control->setValue($filter($control->getValue()));
160 return \true;
161 };
162 return $this;
163 }
164 /**
165 * Toggles HTML element visibility.
166 * @return static
167 */
168 public function toggle(string $id, bool $hide = \true)
169 {
170 $this->toggles[$id] = $hide;
171 return $this;
172 }
173 public function getToggles(bool $actual = \false) : array
174 {
175 return $actual ? $this->getToggleStates() : $this->toggles;
176 }
177 /** @internal */
178 public function getToggleStates(array $toggles = [], bool $success = \true, bool $emptyOptional = null) : array
179 {
180 foreach ($this->toggles as $id => $hide) {
181 $toggles[$id] = ($success xor !$hide) || !empty($toggles[$id]);
182 }
183 $emptyOptional = $emptyOptional ?? !$this->isRequired() && !$this->control->isFilled();
184 foreach ($this as $rule) {
185 if ($rule->branch) {
186 $toggles = $rule->branch->getToggleStates($toggles, $success && static::validateRule($rule), $rule->validator === Form::BLANK ? \false : $emptyOptional);
187 } elseif (!$emptyOptional || $rule->validator === Form::FILLED) {
188 $success = $success && static::validateRule($rule);
189 }
190 }
191 return $toggles;
192 }
193 /**
194 * Validates against ruleset.
195 */
196 public function validate(bool $emptyOptional = null) : bool
197 {
198 $emptyOptional = $emptyOptional ?? !$this->isRequired() && !$this->control->isFilled();
199 foreach ($this as $rule) {
200 if (!$rule->branch && $emptyOptional && $rule->validator !== Form::FILLED) {
201 continue;
202 }
203 $success = $this->validateRule($rule);
204 if ($success && $rule->branch && !$rule->branch->validate($rule->validator === Form::BLANK ? \false : $emptyOptional)) {
205 return \false;
206 } elseif (!$success && !$rule->branch) {
207 $rule->control->addError(Validator::formatMessage($rule, \true), \false);
208 return \false;
209 }
210 }
211 return \true;
212 }
213 /**
214 * Clear all validation rules.
215 */
216 public function reset() : void
217 {
218 $this->rules = [];
219 }
220 /**
221 * Validates single rule.
222 */
223 public static function validateRule(Rule $rule) : bool
224 {
225 $args = \is_array($rule->arg) ? $rule->arg : [$rule->arg];
226 foreach ($args as &$val) {
227 $val = $val instanceof Control ? $val->getValue() : $val;
228 }
229 return $rule->isNegative xor self::getCallback($rule)($rule->control, \is_array($rule->arg) ? $args : $args[0]);
230 }
231 /**
232 * Iterates over complete ruleset.
233 * @return \ArrayIterator<int, Rule>
234 */
235 public function getIterator() : \Iterator
236 {
237 $priorities = [
238 0 => [],
239 // BLANK
240 1 => $this->required ? [$this->required] : [],
241 2 => [],
242 ];
243 foreach ($this->rules as $rule) {
244 $priorities[$rule->validator === Form::BLANK && $rule->control === $this->control ? 0 : 2][] = $rule;
245 }
246 return new \ArrayIterator(\array_merge(...$priorities));
247 }
248 /**
249 * Process 'operation' string.
250 */
251 private function adjustOperation(Rule $rule) : void
252 {
253 if (\is_string($rule->validator) && \ord($rule->validator[0]) > 127) {
254 $rule->isNegative = \true;
255 $rule->validator = ~$rule->validator;
256 if (!$rule->branch) {
257 $name = \strncmp($rule->validator, ':', 1) ? $rule->validator : 'Form:' . \strtoupper($rule->validator);
258 \trigger_error("Negative validation rules such as ~{$name} are deprecated.", \E_USER_DEPRECATED);
259 }
260 if (isset(self::NEG_RULES[$rule->validator])) {
261 $rule->validator = self::NEG_RULES[$rule->validator];
262 $rule->isNegative = \false;
263 \trigger_error('Replace negative validation rule ~Form::FILLED with Form::BLANK and vice versa.', \E_USER_DEPRECATED);
264 }
265 }
266 if (!\is_callable($this->getCallback($rule))) {
267 $validator = \is_scalar($rule->validator) ? " '{$rule->validator}'" : '';
268 throw new \Packetery\Nette\InvalidArgumentException("Unknown validator{$validator} for control '{$rule->control->name}'.");
269 }
270 }
271 private static function getCallback(Rule $rule)
272 {
273 $op = $rule->validator;
274 return \is_string($op) && \strncmp($op, ':', 1) === 0 ? [Validator::class, 'validate' . \ltrim($op, ':')] : $op;
275 }
276 }
277