[BUGFIX] Fix several typos in php comments
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Error / Result.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Extbase\Error;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 /**
20 * Result object for operations dealing with objects, such as the Property Mapper or the Validators.
21 */
22 class Result
23 {
24 /**
25 * @var Error[]
26 */
27 protected $errors = [];
28
29 /**
30 * Caches the existence of errors
31 * @var bool
32 */
33 protected $errorsExist = false;
34
35 /**
36 * @var Warning[]
37 */
38 protected $warnings = [];
39
40 /**
41 * Caches the existence of warning
42 * @var bool
43 */
44 protected $warningsExist = false;
45
46 /**
47 * @var Notice[]
48 */
49 protected $notices = [];
50
51 /**
52 * Caches the existence of notices
53 * @var bool
54 */
55 protected $noticesExist = false;
56
57 /**
58 * The result objects for the sub properties
59 *
60 * @var Result[]
61 */
62 protected $propertyResults = [];
63
64 /**
65 * @var Result
66 */
67 protected $parent;
68
69 /**
70 * Injects the parent result and propagates the
71 * cached error states upwards
72 *
73 * @param Result $parent
74 */
75 public function setParent(Result $parent): void
76 {
77 if ($this->parent !== $parent) {
78 $this->parent = $parent;
79 if ($this->hasErrors()) {
80 $parent->setErrorsExist();
81 }
82 if ($this->hasWarnings()) {
83 $parent->setWarningsExist();
84 }
85 if ($this->hasNotices()) {
86 $parent->setNoticesExist();
87 }
88 }
89 }
90
91 /**
92 * Add an error to the current Result object
93 *
94 * @param Error $error
95 */
96 public function addError(Error $error): void
97 {
98 $this->errors[] = $error;
99 $this->setErrorsExist();
100 }
101
102 /**
103 * Add a warning to the current Result object
104 *
105 * @param Warning $warning
106 */
107 public function addWarning(Warning $warning): void
108 {
109 $this->warnings[] = $warning;
110 $this->setWarningsExist();
111 }
112
113 /**
114 * Add a notice to the current Result object
115 *
116 * @param Notice $notice
117 */
118 public function addNotice(Notice $notice): void
119 {
120 $this->notices[] = $notice;
121 $this->setNoticesExist();
122 }
123
124 /**
125 * Get all errors in the current Result object (non-recursive)
126 *
127 * @return Error[]
128 */
129 public function getErrors(): array
130 {
131 return $this->errors;
132 }
133
134 /**
135 * Get all warnings in the current Result object (non-recursive)
136 *
137 * @return Warning[]
138 */
139 public function getWarnings(): array
140 {
141 return $this->warnings;
142 }
143
144 /**
145 * Get all notices in the current Result object (non-recursive)
146 *
147 * @return Notice[]
148 */
149 public function getNotices(): array
150 {
151 return $this->notices;
152 }
153
154 /**
155 * Get the first error object of the current Result object (non-recursive)
156 *
157 * @return Error
158 */
159 public function getFirstError(): Error
160 {
161 reset($this->errors);
162 return current($this->errors);
163 }
164
165 /**
166 * Get the first warning object of the current Result object (non-recursive)
167 *
168 * @return Warning
169 */
170 public function getFirstWarning(): Warning
171 {
172 reset($this->warnings);
173 return current($this->warnings);
174 }
175
176 /**
177 * Get the first notice object of the current Result object (non-recursive)
178 *
179 * @return Notice
180 */
181 public function getFirstNotice(): Notice
182 {
183 reset($this->notices);
184 return current($this->notices);
185 }
186
187 /**
188 * Return a Result object for the given property path. This is
189 * a fluent interface, so you will probably use it like:
190 * $result->forProperty('foo.bar')->getErrors() -- to get all errors
191 * for property "foo.bar"
192 *
193 * @param string|null $propertyPath
194 * @return Result
195 */
196 public function forProperty(?string $propertyPath): Result
197 {
198 if ($propertyPath === '' || $propertyPath === null) {
199 return $this;
200 }
201 if (strpos($propertyPath, '.') !== false) {
202 return $this->recurseThroughResult(explode('.', $propertyPath));
203 }
204 if (!isset($this->propertyResults[$propertyPath])) {
205 $this->propertyResults[$propertyPath] = new self();
206 $this->propertyResults[$propertyPath]->setParent($this);
207 }
208 return $this->propertyResults[$propertyPath];
209 }
210
211 /**
212 * @todo: consider making this method protected as it will and should not be called from an outside scope
213 *
214 * @param array $pathSegments
215 * @return Result
216 *
217 * @internal only to be used within Extbase, not part of TYPO3 Core API.
218 */
219 public function recurseThroughResult(array $pathSegments): Result
220 {
221 if (count($pathSegments) === 0) {
222 return $this;
223 }
224
225 $propertyName = array_shift($pathSegments);
226
227 if (!isset($this->propertyResults[$propertyName])) {
228 $this->propertyResults[$propertyName] = new self();
229 $this->propertyResults[$propertyName]->setParent($this);
230 }
231
232 return $this->propertyResults[$propertyName]->recurseThroughResult($pathSegments);
233 }
234
235 /**
236 * Sets the error cache to TRUE and propagates the information
237 * upwards the Result-Object Tree
238 */
239 protected function setErrorsExist(): void
240 {
241 $this->errorsExist = true;
242 if ($this->parent !== null) {
243 $this->parent->setErrorsExist();
244 }
245 }
246
247 /**
248 * Sets the warning cache to TRUE and propagates the information
249 * upwards the Result-Object Tree
250 */
251 protected function setWarningsExist(): void
252 {
253 $this->warningsExist = true;
254 if ($this->parent !== null) {
255 $this->parent->setWarningsExist();
256 }
257 }
258
259 /**
260 * Sets the notices cache to TRUE and propagates the information
261 * upwards the Result-Object Tree
262 */
263 protected function setNoticesExist(): void
264 {
265 $this->noticesExist = true;
266 if ($this->parent !== null) {
267 $this->parent->setNoticesExist();
268 }
269 }
270
271 /**
272 * Does the current Result object have Notices, Errors or Warnings? (Recursively)
273 *
274 * @return bool
275 */
276 public function hasMessages(): bool
277 {
278 return $this->errorsExist || $this->noticesExist || $this->warningsExist;
279 }
280
281 /**
282 * Clears the result
283 */
284 public function clear(): void
285 {
286 $this->errors = [];
287 $this->notices = [];
288 $this->warnings = [];
289
290 $this->warningsExist = false;
291 $this->noticesExist = false;
292 $this->errorsExist = false;
293
294 $this->propertyResults = [];
295 }
296
297 /**
298 * Does the current Result object have Errors? (Recursively)
299 *
300 * @return bool
301 */
302 public function hasErrors(): bool
303 {
304 if (count($this->errors) > 0) {
305 return true;
306 }
307
308 foreach ($this->propertyResults as $subResult) {
309 if ($subResult->hasErrors()) {
310 return true;
311 }
312 }
313
314 return false;
315 }
316
317 /**
318 * Does the current Result object have Warnings? (Recursively)
319 *
320 * @return bool
321 */
322 public function hasWarnings(): bool
323 {
324 if (count($this->warnings) > 0) {
325 return true;
326 }
327
328 foreach ($this->propertyResults as $subResult) {
329 if ($subResult->hasWarnings()) {
330 return true;
331 }
332 }
333
334 return false;
335 }
336
337 /**
338 * Does the current Result object have Notices? (Recursively)
339 *
340 * @return bool
341 */
342 public function hasNotices(): bool
343 {
344 if (count($this->notices) > 0) {
345 return true;
346 }
347
348 foreach ($this->propertyResults as $subResult) {
349 if ($subResult->hasNotices()) {
350 return true;
351 }
352 }
353
354 return false;
355 }
356
357 /**
358 * Get a list of all Error objects recursively. The result is an array,
359 * where the key is the property path where the error occurred, and the
360 * value is a list of all errors (stored as array)
361 *
362 * @return Error[]
363 */
364 public function getFlattenedErrors(): array
365 {
366 $result = [];
367 $this->flattenErrorTree($result, []);
368 return $result;
369 }
370
371 /**
372 * Get a list of all Warning objects recursively. The result is an array,
373 * where the key is the property path where the warning occurred, and the
374 * value is a list of all warnings (stored as array)
375 *
376 * @return Warning[]
377 */
378 public function getFlattenedWarnings(): array
379 {
380 $result = [];
381 $this->flattenWarningsTree($result, []);
382 return $result;
383 }
384
385 /**
386 * Get a list of all Notice objects recursively. The result is an array,
387 * where the key is the property path where the notice occurred, and the
388 * value is a list of all notices (stored as array)
389 *
390 * @return Notice[]
391 */
392 public function getFlattenedNotices(): array
393 {
394 $result = [];
395 $this->flattenNoticesTree($result, []);
396 return $result;
397 }
398
399 /**
400 * @param array $result
401 * @param array $level
402 */
403 protected function flattenErrorTree(array &$result, array $level): void
404 {
405 if (count($this->errors) > 0) {
406 $result[implode('.', $level)] = $this->errors;
407 }
408 foreach ($this->propertyResults as $subPropertyName => $subResult) {
409 $level[] = $subPropertyName;
410 $subResult->flattenErrorTree($result, $level);
411 array_pop($level);
412 }
413 }
414
415 /**
416 * @param array $result
417 * @param array $level
418 */
419 protected function flattenWarningsTree(array &$result, array $level): void
420 {
421 if (count($this->warnings) > 0) {
422 $result[implode('.', $level)] = $this->warnings;
423 }
424 foreach ($this->propertyResults as $subPropertyName => $subResult) {
425 $level[] = $subPropertyName;
426 $subResult->flattenWarningsTree($result, $level);
427 array_pop($level);
428 }
429 }
430
431 /**
432 * @param array $result
433 * @param array $level
434 */
435 protected function flattenNoticesTree(array &$result, array $level): void
436 {
437 if (count($this->notices) > 0) {
438 $result[implode('.', $level)] = $this->notices;
439 }
440 foreach ($this->propertyResults as $subPropertyName => $subResult) {
441 $level[] = $subPropertyName;
442 $subResult->flattenNoticesTree($result, $level);
443 array_pop($level);
444 }
445 }
446
447 /**
448 * Merge the given Result object into this one.
449 *
450 * @param Result $otherResult
451 */
452 public function merge(Result $otherResult): void
453 {
454 if ($otherResult->errorsExist) {
455 $this->mergeProperty($otherResult, 'getErrors', 'addError');
456 }
457 if ($otherResult->warningsExist) {
458 $this->mergeProperty($otherResult, 'getWarnings', 'addWarning');
459 }
460 if ($otherResult->noticesExist) {
461 $this->mergeProperty($otherResult, 'getNotices', 'addNotice');
462 }
463
464 foreach ($otherResult->getSubResults() as $subPropertyName => $subResult) {
465 /** @var Result $subResult */
466 if (array_key_exists($subPropertyName, $this->propertyResults) && $this->propertyResults[$subPropertyName]->hasMessages()) {
467 $this->forProperty($subPropertyName)->merge($subResult);
468 } else {
469 $this->propertyResults[$subPropertyName] = $subResult;
470 $subResult->setParent($this);
471 }
472 }
473 }
474
475 /**
476 * Merge a single property from the other result object.
477 *
478 * @param Result $otherResult
479 * @param string $getterName
480 * @param string $adderName
481 */
482 protected function mergeProperty(Result $otherResult, string $getterName, string $adderName): void
483 {
484 $getter = [$otherResult, $getterName];
485 $adder = [$this, $adderName];
486
487 if (!is_callable($getter) || !is_callable($adder)) {
488 return;
489 }
490
491 foreach ($getter() as $messageInOtherResult) {
492 $adder($messageInOtherResult);
493 }
494 }
495
496 /**
497 * Get a list of all sub Result objects available.
498 *
499 * @return Result[]
500 */
501 public function getSubResults(): array
502 {
503 return $this->propertyResults;
504 }
505 }