PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.0
6.2.1 6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / libraries / framework / Validation / Validator.php
kirki / libraries / framework / Validation Last commit date
Rules 6 days ago Rule.php 6 days ago RuleFactory.php 6 days ago RuleParser.php 6 days ago ValidationRule.php 6 days ago Validator.php 6 days ago
Validator.php
383 lines
1 <?php
2
3 /**
4 * Validator class.
5 *
6 * @package Framework
7 * @subpackage Validation
8 * @since 1.0.0
9 */
10 namespace Kirki\Framework\Validation;
11
12 use Closure;
13 use Kirki\Framework\Exceptions\ValidationException;
14 use Kirki\Framework\Supports\Arr;
15 use Kirki\Framework\Validation\Rules\ArrayRule;
16 use Kirki\Framework\Validation\Rules\ConditionalRule;
17 use Kirki\Framework\Validation\Rules\NullableRule;
18 use Kirki\Framework\Validation\Rules\SometimesRule;
19 use stdClass;
20 use function Kirki\Framework\deep_get;
21 use function Kirki\Framework\message;
22 \defined('ABSPATH') || exit;
23 class Validator
24 {
25 /**
26 * The data to validate.
27 *
28 * @var array
29 *
30 * @since 1.0.0
31 */
32 protected array $data = [];
33 /**
34 * The rules to validate.
35 *
36 * @var array
37 *
38 * @since 1.0.0
39 */
40 protected array $rules = [];
41 /**
42 * The raw rules to validate.
43 *
44 * @var array
45 *
46 * @since 1.0.0
47 */
48 protected array $raw_rules = [];
49 /**
50 * The custom validation messages.
51 *
52 * @var array
53 *
54 * @since 1.0.0
55 */
56 protected array $custom_messages = [];
57 /**
58 * The errors encountered during validation.
59 *
60 * @var array
61 *
62 * @since 1.0.0
63 */
64 protected array $errors = [];
65 /**
66 * The validation should stop on first failure.
67 *
68 * @var bool
69 *
70 * @since 1.0.0
71 */
72 protected bool $stop_on_first_failure = \false;
73 /**
74 * Create a new validator instance.
75 *
76 * @param array $data The data to validate.
77 * @param array $rules The rules to validate.
78 * @param array $messages The messages to validate.
79 *
80 * @return void
81 *
82 * @since 1.0.0
83 */
84 public function __construct(array $data, array $rules, array $messages = [])
85 {
86 $this->data = $data;
87 $this->raw_rules = $rules;
88 $this->rules = $this->parse_rules($rules);
89 $this->custom_messages = $messages;
90 }
91 /**
92 * Create a new validator instance.
93 *
94 * @param array $data The data to validate.
95 * @param array $rules The rules to validate.
96 * @param array $messages The messages to validate. *
97 *
98 * @return static
99 *
100 * @since 1.0.0
101 */
102 public static function make(array $data, array $rules, array $messages = [])
103 {
104 return new static($data, $rules, $messages);
105 }
106 /**
107 * Parse the raw rules into rule instances.
108 *
109 * @param array $rules The rules to parse.
110 *
111 * @return array
112 *
113 * @since 1.0.0
114 */
115 protected function parse_rules(array $rules)
116 {
117 return (new RuleParser($this->data, $rules))->parse()->all();
118 }
119 /**
120 * Get the custom messages.
121 *
122 * @return array
123 *
124 * @since 1.0.0
125 */
126 public function get_custom_messages()
127 {
128 return $this->custom_messages;
129 }
130 /**
131 * Get the value of an attribute.
132 *
133 * @param string $attribute The attribute to get the value of.
134 *
135 * @return mixed
136 *
137 * @since 1.0.0
138 */
139 public function get_value($attribute)
140 {
141 return Arr::get($this->data, $attribute);
142 }
143 /**
144 * Set the value of an attribute.
145 *
146 * @param string $attribute The attribute to set the value of.
147 * @param mixed $value The value to set.
148 *
149 * @return void
150 *
151 * @since 1.0.0
152 */
153 public function set_value($attribute, $value)
154 {
155 Arr::set($this->data, $attribute, $value);
156 }
157 /**
158 * Get the rules.
159 *
160 * @return array
161 *
162 * @since 1.0.0
163 */
164 public function get_rules()
165 {
166 return $this->rules;
167 }
168 /**
169 * Get the data.
170 *
171 * @return array
172 *
173 * @since 1.0.0
174 */
175 public function get_data()
176 {
177 return $this->data;
178 }
179 /**
180 * Validate the data.
181 *
182 * @return bool
183 *
184 * @since 1.0.0
185 */
186 public function passes()
187 {
188 foreach ($this->rules as $key => $rules) {
189 if ($this->stop_on_first_failure && !empty($this->errors())) {
190 break;
191 }
192 $this->validate_rules($key, $rules);
193 }
194 return empty($this->errors());
195 }
196 /**
197 * Apply the rules to the key.
198 *
199 * @param string $key The key to apply the rules to.
200 * @param array<ValidationRule> $rules The rules to apply.
201 *
202 * @return void
203 *
204 * @since 1.0.0
205 */
206 protected function validate_rules(string $key, array $rules)
207 {
208 $value = deep_get($this->data, $key);
209 foreach ($rules as $rule) {
210 // For the nullable rule, the the value is nullable
211 // then we don't need to validate other rules.
212 if ($rule instanceof NullableRule && ValidationRule::is_nullish($value)) {
213 break;
214 }
215 // For the sometimes rule,
216 // if the key does not exist in the validating data then skip validation.
217 if ($rule instanceof SometimesRule && !\array_key_exists($key, $this->data)) {
218 break;
219 }
220 if ($rule instanceof Closure) {
221 $this->apply_closure_rule($rule, $value, $key);
222 continue;
223 }
224 $this->apply($rule, $value, $key);
225 }
226 }
227 /**
228 * Apply a closure to a value.
229 *
230 * @param Closure $closure The closure to apply.
231 * @param mixed $value The value to apply the closure to.
232 * @param string $key The key to apply the closure to.
233 *
234 * @return string|bool
235 *
236 * @since 1.0.0
237 */
238 protected function apply_closure_rule(Closure $closure, $value, string $key)
239 {
240 $result = $closure($value, $key, $this->data);
241 if (\is_string($result)) {
242 return $this->add_error($key, $result);
243 }
244 if ($result === \false) {
245 return $this->add_error($key, message('validator.invalid', [$key]));
246 }
247 return \true;
248 }
249 /**
250 * Apply a rule to a value.
251 *
252 * @param ValidationRule $rule The rule to apply.
253 * @param mixed $value The value to apply the rule to.
254 * @param string $key The key to apply the rule to.
255 *
256 * @return void
257 *
258 * @since 1.0.0
259 */
260 protected function apply(ValidationRule $rule, $value, string $key)
261 {
262 $rule->with_data($this->data)->with_name($key)->with_value($value)->clear_error_messages()->sync_custom_messages($this->custom_messages)->should_stop_on_first_failure($this->stop_on_first_failure);
263 if (!$rule->passed()) {
264 return $this->add_error($key, $rule->messages());
265 }
266 return \true;
267 }
268 /**
269 * Add an error to the errors array.
270 *
271 * @param string $key The key to add the error to.
272 * @param array $messages The messages to add.
273 *
274 * @return bool
275 *
276 * @since 1.0.0
277 */
278 protected function add_error(string $key, $messages)
279 {
280 $this->errors[$key] ??= [];
281 $this->errors[$key] = \array_merge($this->errors[$key], Arr::wrap($messages));
282 return \false;
283 }
284 /**
285 * Check if the validation failed.
286 *
287 * @return bool
288 *
289 * @since 1.0.0
290 */
291 public function fails()
292 {
293 return !$this->passes();
294 }
295 /**
296 * Get the errors.
297 *
298 * @return array
299 *
300 * @since 1.0.0
301 */
302 public function errors()
303 {
304 return $this->errors;
305 }
306 /**
307 * Validate the data.
308 *
309 * @return array
310 *
311 * @since 1.0.0
312 */
313 public function validate()
314 {
315 if ($this->fails()) {
316 throw ValidationException::with_errors($this->errors());
317 }
318 return $this->validated();
319 }
320 /**
321 * Get the validated data.
322 *
323 * @return array
324 *
325 * @since 1.0.0
326 */
327 public function validated()
328 {
329 if (empty($this->errors())) {
330 $this->passes();
331 }
332 if (!empty($this->errors())) {
333 throw ValidationException::with_errors($this->errors());
334 }
335 $results = [];
336 $missing_value = new stdClass();
337 foreach ($this->get_rules() as $attribute => $rules) {
338 $value = deep_get($this->get_data(), $attribute, $missing_value);
339 if ($this->has_array_rule($rules) && $value !== null && !empty(\preg_grep('/^' . \preg_quote($attribute, '/') . '\\.+/', \array_keys($this->get_rules())))) {
340 continue;
341 }
342 if ($value !== $missing_value) {
343 Arr::set($results, $attribute, $value);
344 }
345 }
346 return $results;
347 }
348 /**
349 * Check if the rules have an array rule.
350 *
351 * @param array $rules The rules to check.
352 *
353 * @return bool
354 *
355 * @since 1.0.0
356 */
357 protected function has_array_rule(array $rules)
358 {
359 return !empty(Arr::where($rules, function ($rule) {
360 return $rule instanceof ArrayRule;
361 }));
362 }
363 /**
364 * Sometimes the rules should be applied.
365 *
366 * @param string $key The key to apply the rules to.
367 * @param array|string $rules The rules to apply.
368 * @param callable $callback The callback to check if the rules should be applied.
369 *
370 * @return void
371 *
372 * @since 1.0.0
373 */
374 public function sometimes(string $key, $rules, $callback)
375 {
376 if ($callback($this->data)) {
377 $rules = [$key => $rules];
378 $rules = $this->parse_rules($rules);
379 $this->rules = \array_merge($this->rules, $rules);
380 }
381 }
382 }
383