vendor/symfony/form/FormErrorIterator.php line 31

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