vendor/symfony/form/FormErrorIterator.php line 36

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Form;
  11. use Symfony\Component\Form\Exception\BadMethodCallException;
  12. use Symfony\Component\Form\Exception\InvalidArgumentException;
  13. use Symfony\Component\Form\Exception\LogicException;
  14. use Symfony\Component\Form\Exception\OutOfBoundsException;
  15. use Symfony\Component\Validator\ConstraintViolation;
  16. /**
  17.  * Iterates over the errors of a form.
  18.  *
  19.  * This class supports recursive iteration. In order to iterate recursively,
  20.  * pass a structure of {@link FormError} and {@link FormErrorIterator} objects
  21.  * to the $errors constructor argument.
  22.  *
  23.  * You can also wrap the iterator into a {@link \RecursiveIteratorIterator} to
  24.  * flatten the recursive structure into a flat list of errors.
  25.  *
  26.  * @author Bernhard Schussek <bschussek@gmail.com>
  27.  *
  28.  * @implements \ArrayAccess<int, FormError|FormErrorIterator>
  29.  * @implements \RecursiveIterator<int, FormError|FormErrorIterator>
  30.  * @implements \SeekableIterator<int, FormError|FormErrorIterator>
  31.  */
  32. class FormErrorIterator implements \RecursiveIterator\SeekableIterator\ArrayAccess\Countable
  33. {
  34.     /**
  35.      * The prefix used for indenting nested error messages.
  36.      */
  37.     public const INDENTATION '    ';
  38.     private $form;
  39.     private array $errors;
  40.     /**
  41.      * @param list<FormError|self> $errors
  42.      *
  43.      * @throws InvalidArgumentException If the errors are invalid
  44.      */
  45.     public function __construct(FormInterface $form, array $errors)
  46.     {
  47.         foreach ($errors as $error) {
  48.             if (!($error instanceof FormError || $error instanceof self)) {
  49.                 throw new InvalidArgumentException(sprintf('The errors must be instances of "Symfony\Component\Form\FormError" or "%s". Got: "%s".'__CLASS__get_debug_type($error)));
  50.             }
  51.         }
  52.         $this->form $form;
  53.         $this->errors $errors;
  54.     }
  55.     /**
  56.      * Returns all iterated error messages as string.
  57.      */
  58.     public function __toString(): string
  59.     {
  60.         $string '';
  61.         foreach ($this->errors as $error) {
  62.             if ($error instanceof FormError) {
  63.                 $string .= 'ERROR: '.$error->getMessage()."\n";
  64.             } else {
  65.                 /* @var self $error */
  66.                 $string .= $error->form->getName().":\n";
  67.                 $string .= self::indent((string) $error);
  68.             }
  69.         }
  70.         return $string;
  71.     }
  72.     /**
  73.      * Returns the iterated form.
  74.      */
  75.     public function getForm(): FormInterface
  76.     {
  77.         return $this->form;
  78.     }
  79.     /**
  80.      * Returns the current element of the iterator.
  81.      */
  82.     public function current(): FormError|self
  83.     {
  84.         return current($this->errors);
  85.     }
  86.     /**
  87.      * Advances the iterator to the next position.
  88.      */
  89.     public function next(): void
  90.     {
  91.         next($this->errors);
  92.     }
  93.     /**
  94.      * Returns the current position of the iterator.
  95.      */
  96.     public function key(): int
  97.     {
  98.         return key($this->errors);
  99.     }
  100.     /**
  101.      * Returns whether the iterator's position is valid.
  102.      */
  103.     public function valid(): bool
  104.     {
  105.         return null !== key($this->errors);
  106.     }
  107.     /**
  108.      * Sets the iterator's position to the beginning.
  109.      *
  110.      * This method detects if errors have been added to the form since the
  111.      * construction of the iterator.
  112.      */
  113.     public function rewind(): void
  114.     {
  115.         reset($this->errors);
  116.     }
  117.     /**
  118.      * Returns whether a position exists in the iterator.
  119.      *
  120.      * @param int $position The position
  121.      */
  122.     public function offsetExists(mixed $position): bool
  123.     {
  124.         return isset($this->errors[$position]);
  125.     }
  126.     /**
  127.      * Returns the element at a position in the iterator.
  128.      *
  129.      * @param int $position The position
  130.      *
  131.      * @throws OutOfBoundsException If the given position does not exist
  132.      */
  133.     public function offsetGet(mixed $position): FormError|self
  134.     {
  135.         if (!isset($this->errors[$position])) {
  136.             throw new OutOfBoundsException('The offset '.$position.' does not exist.');
  137.         }
  138.         return $this->errors[$position];
  139.     }
  140.     /**
  141.      * Unsupported method.
  142.      *
  143.      * @throws BadMethodCallException
  144.      */
  145.     public function offsetSet(mixed $positionmixed $value): void
  146.     {
  147.         throw new BadMethodCallException('The iterator doesn\'t support modification of elements.');
  148.     }
  149.     /**
  150.      * Unsupported method.
  151.      *
  152.      * @throws BadMethodCallException
  153.      */
  154.     public function offsetUnset(mixed $position): void
  155.     {
  156.         throw new BadMethodCallException('The iterator doesn\'t support modification of elements.');
  157.     }
  158.     /**
  159.      * Returns whether the current element of the iterator can be recursed
  160.      * into.
  161.      */
  162.     public function hasChildren(): bool
  163.     {
  164.         return current($this->errors) instanceof self;
  165.     }
  166.     public function getChildren(): self
  167.     {
  168.         if (!$this->hasChildren()) {
  169.             throw new LogicException(sprintf('The current element is not iterable. Use "%s" to get the current element.'self::class.'::current()'));
  170.         }
  171.         return current($this->errors);
  172.     }
  173.     /**
  174.      * Returns the number of elements in the iterator.
  175.      *
  176.      * Note that this is not the total number of errors, if the constructor
  177.      * parameter $deep was set to true! In that case, you should wrap the
  178.      * iterator into a {@link \RecursiveIteratorIterator} with the standard mode
  179.      * {@link \RecursiveIteratorIterator::LEAVES_ONLY} and count the result.
  180.      *
  181.      *     $iterator = new \RecursiveIteratorIterator($form->getErrors(true));
  182.      *     $count = count(iterator_to_array($iterator));
  183.      *
  184.      * Alternatively, set the constructor argument $flatten to true as well.
  185.      *
  186.      *     $count = count($form->getErrors(true, true));
  187.      */
  188.     public function count(): int
  189.     {
  190.         return \count($this->errors);
  191.     }
  192.     /**
  193.      * Sets the position of the iterator.
  194.      *
  195.      * @throws OutOfBoundsException If the position is invalid
  196.      */
  197.     public function seek(int $position): void
  198.     {
  199.         if (!isset($this->errors[$position])) {
  200.             throw new OutOfBoundsException('The offset '.$position.' does not exist.');
  201.         }
  202.         reset($this->errors);
  203.         while ($position !== key($this->errors)) {
  204.             next($this->errors);
  205.         }
  206.     }
  207.     /**
  208.      * Creates iterator for errors with specific codes.
  209.      *
  210.      * @param string|string[] $codes The codes to find
  211.      */
  212.     public function findByCodes(string|array $codes): static
  213.     {
  214.         $codes = (array) $codes;
  215.         $errors = [];
  216.         foreach ($this as $error) {
  217.             $cause $error->getCause();
  218.             if ($cause instanceof ConstraintViolation && \in_array($cause->getCode(), $codestrue)) {
  219.                 $errors[] = $error;
  220.             }
  221.         }
  222.         return new static($this->form$errors);
  223.     }
  224.     /**
  225.      * Utility function for indenting multi-line strings.
  226.      */
  227.     private static function indent(string $string): string
  228.     {
  229.         return rtrim(self::INDENTATION.str_replace("\n""\n".self::INDENTATION$string), ' ');
  230.     }
  231. }