| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- <?php
- /*
- * Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
- * SPDX-License-Identifier: MIT
- */
- declare(strict_types=1);
- namespace Respect\Validation\Exceptions;
- use IteratorAggregate;
- use RecursiveIteratorIterator;
- use SplObjectStorage;
- use function array_shift;
- use function count;
- use function current;
- use function implode;
- use function is_array;
- use function spl_object_hash;
- use function sprintf;
- use function str_repeat;
- use const PHP_EOL;
- /**
- * Exception for nested validations.
- *
- * This exception allows to have exceptions inside itself and providers methods
- * to handle them and to retrieve nested messages based on itself and its
- * children.
- *
- * @author Alexandre Gomes Gaigalas <alganet@gmail.com>
- * @author Henrique Moody <henriquemoody@gmail.com>
- * @author Jonathan Stewmon <jstewmon@rmn.com>
- * @author Wojciech Frącz <fraczwojciech@gmail.com>
- */
- class NestedValidationException extends ValidationException implements IteratorAggregate
- {
- /**
- * @var ValidationException[]
- */
- private $exceptions = [];
- /**
- * Returns the exceptions that are children of the exception.
- *
- * @return ValidationException[]
- */
- public function getChildren(): array
- {
- return $this->exceptions;
- }
- /**
- * Adds a child to the exception.
- */
- public function addChild(ValidationException $exception): self
- {
- $this->exceptions[spl_object_hash($exception)] = $exception;
- return $this;
- }
- /**
- * Adds children to the exception.
- *
- * @param ValidationException[] $exceptions
- */
- public function addChildren(array $exceptions): self
- {
- foreach ($exceptions as $exception) {
- $this->addChild($exception);
- }
- return $this;
- }
- /**
- * @return SplObjectStorage<ValidationException, int>
- */
- public function getIterator(): SplObjectStorage
- {
- /** @var SplObjectStorage<ValidationException, int> */
- $childrenExceptions = new SplObjectStorage();
- $recursiveIteratorIterator = $this->getRecursiveIterator();
- $lastDepth = 0;
- $lastDepthOriginal = 0;
- $knownDepths = [];
- foreach ($recursiveIteratorIterator as $childException) {
- if ($this->isOmissible($childException)) {
- continue;
- }
- $currentDepth = $lastDepth;
- $currentDepthOriginal = $recursiveIteratorIterator->getDepth() + 1;
- if (isset($knownDepths[$currentDepthOriginal])) {
- $currentDepth = $knownDepths[$currentDepthOriginal];
- } elseif ($currentDepthOriginal > $lastDepthOriginal) {
- ++$currentDepth;
- }
- if (!isset($knownDepths[$currentDepthOriginal])) {
- $knownDepths[$currentDepthOriginal] = $currentDepth;
- }
- $lastDepth = $currentDepth;
- $lastDepthOriginal = $currentDepthOriginal;
- $childrenExceptions->attach($childException, $currentDepth);
- }
- return $childrenExceptions;
- }
- /**
- * Returns a key->value array with all the messages of the exception.
- *
- * In this array the "keys" are the ids of the exceptions (defined name or
- * name of the rule) and the values are the message.
- *
- * Once templates are passed it overwrites the templates of the given
- * messages.
- *
- * @param string[]|string[][] $templates
- *
- * @return string[]
- */
- public function getMessages(array $templates = []): array
- {
- $messages = [$this->getId() => $this->renderMessage($this, $templates)];
- foreach ($this->getChildren() as $exception) {
- $id = $exception->getId();
- if (!$exception instanceof self) {
- $messages[$id] = $this->renderMessage(
- $exception,
- $this->findTemplates($templates, $this->getId())
- );
- continue;
- }
- $messages[$id] = $exception->getMessages($this->findTemplates($templates, $id, $this->getId()));
- if (count($messages[$id]) > 1) {
- continue;
- }
- $messages[$id] = current($messages[$exception->getId()]);
- }
- if (count($messages) > 1) {
- unset($messages[$this->getId()]);
- }
- return $messages;
- }
- /**
- * Returns a string with all the messages of the exception.
- */
- public function getFullMessage(): string
- {
- $messages = [];
- $leveler = 1;
- if (!$this->isOmissible($this)) {
- $leveler = 0;
- $messages[] = sprintf('- %s', $this->getMessage());
- }
- $exceptions = $this->getIterator();
- /** @var ValidationException $exception */
- foreach ($exceptions as $exception) {
- $messages[] = sprintf(
- '%s- %s',
- str_repeat(' ', (int) ($exceptions[$exception] - $leveler) * 2),
- $exception->getMessage()
- );
- }
- return implode(PHP_EOL, $messages);
- }
- /**
- * @param string[] $templates
- */
- protected function renderMessage(ValidationException $exception, array $templates): string
- {
- if (isset($templates[$exception->getId()])) {
- $exception->updateTemplate($templates[$exception->getId()]);
- }
- return $exception->getMessage();
- }
- /**
- * @param string[] $templates
- * @param mixed ...$ids
- *
- * @return string[]
- */
- protected function findTemplates(array $templates, ...$ids): array
- {
- while (count($ids) > 0) {
- $id = array_shift($ids);
- if (!isset($templates[$id])) {
- continue;
- }
- if (!is_array($templates[$id])) {
- continue;
- }
- $templates = $templates[$id];
- }
- return $templates;
- }
- /**
- * @return RecursiveIteratorIterator<RecursiveExceptionIterator>
- */
- private function getRecursiveIterator(): RecursiveIteratorIterator
- {
- return new RecursiveIteratorIterator(
- new RecursiveExceptionIterator($this),
- RecursiveIteratorIterator::SELF_FIRST
- );
- }
- private function isOmissible(Exception $exception): bool
- {
- if (!$exception instanceof self) {
- return false;
- }
- if (count($exception->getChildren()) !== 1) {
- return false;
- }
- /** @var ValidationException $childException */
- $childException = current($exception->getChildren());
- if ($childException->getMessage() === $exception->getMessage()) {
- return true;
- }
- if ($exception->hasCustomTemplate()) {
- return $childException->hasCustomTemplate();
- }
- return !$childException instanceof NonOmissibleException;
- }
- }
|