vendor/symfony/property-access/PropertyPath.php line 25

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\PropertyAccess;
  11. use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
  12. use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
  13. use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
  14. /**
  15.  * Default implementation of {@link PropertyPathInterface}.
  16.  *
  17.  * @author Bernhard Schussek <bschussek@gmail.com>
  18.  *
  19.  * @implements \IteratorAggregate<int, string>
  20.  */
  21. class PropertyPath implements \IteratorAggregatePropertyPathInterface
  22. {
  23.     /**
  24.      * Character used for separating between plural and singular of an element.
  25.      */
  26.     public const SINGULAR_SEPARATOR '|';
  27.     /**
  28.      * The elements of the property path.
  29.      *
  30.      * @var list<string>
  31.      */
  32.     private $elements = [];
  33.     /**
  34.      * The number of elements in the property path.
  35.      *
  36.      * @var int
  37.      */
  38.     private $length;
  39.     /**
  40.      * Contains a Boolean for each property in $elements denoting whether this
  41.      * element is an index. It is a property otherwise.
  42.      *
  43.      * @var array
  44.      */
  45.     private $isIndex = [];
  46.     /**
  47.      * String representation of the path.
  48.      *
  49.      * @var string
  50.      */
  51.     private $pathAsString;
  52.     /**
  53.      * Constructs a property path from a string.
  54.      *
  55.      * @param PropertyPath|string $propertyPath The property path as string or instance
  56.      *
  57.      * @throws InvalidArgumentException     If the given path is not a string
  58.      * @throws InvalidPropertyPathException If the syntax of the property path is not valid
  59.      */
  60.     public function __construct($propertyPath)
  61.     {
  62.         // Can be used as copy constructor
  63.         if ($propertyPath instanceof self) {
  64.             /* @var PropertyPath $propertyPath */
  65.             $this->elements $propertyPath->elements;
  66.             $this->length $propertyPath->length;
  67.             $this->isIndex $propertyPath->isIndex;
  68.             $this->pathAsString $propertyPath->pathAsString;
  69.             return;
  70.         }
  71.         if (!\is_string($propertyPath)) {
  72.             throw new InvalidArgumentException(sprintf('The property path constructor needs a string or an instance of "Symfony\Component\PropertyAccess\PropertyPath". Got: "%s".'get_debug_type($propertyPath)));
  73.         }
  74.         if ('' === $propertyPath) {
  75.             throw new InvalidPropertyPathException('The property path should not be empty.');
  76.         }
  77.         $this->pathAsString $propertyPath;
  78.         $position 0;
  79.         $remaining $propertyPath;
  80.         // first element is evaluated differently - no leading dot for properties
  81.         $pattern '/^(([^\.\[]++)|\[([^\]]++)\])(.*)/';
  82.         while (preg_match($pattern$remaining$matches)) {
  83.             if ('' !== $matches[2]) {
  84.                 $element $matches[2];
  85.                 $this->isIndex[] = false;
  86.             } else {
  87.                 $element $matches[3];
  88.                 $this->isIndex[] = true;
  89.             }
  90.             $this->elements[] = $element;
  91.             $position += \strlen($matches[1]);
  92.             $remaining $matches[4];
  93.             $pattern '/^(\.([^\.|\[]++)|\[([^\]]++)\])(.*)/';
  94.         }
  95.         if ('' !== $remaining) {
  96.             throw new InvalidPropertyPathException(sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d.'$propertyPath$remaining[0], $position));
  97.         }
  98.         $this->length \count($this->elements);
  99.     }
  100.     /**
  101.      * {@inheritdoc}
  102.      */
  103.     public function __toString()
  104.     {
  105.         return $this->pathAsString;
  106.     }
  107.     /**
  108.      * {@inheritdoc}
  109.      */
  110.     public function getLength()
  111.     {
  112.         return $this->length;
  113.     }
  114.     /**
  115.      * {@inheritdoc}
  116.      */
  117.     public function getParent()
  118.     {
  119.         if ($this->length <= 1) {
  120.             return null;
  121.         }
  122.         $parent = clone $this;
  123.         --$parent->length;
  124.         $parent->pathAsString substr($parent->pathAsString0max(strrpos($parent->pathAsString'.'), strrpos($parent->pathAsString'[')));
  125.         array_pop($parent->elements);
  126.         array_pop($parent->isIndex);
  127.         return $parent;
  128.     }
  129.     /**
  130.      * Returns a new iterator for this path.
  131.      *
  132.      * @return PropertyPathIteratorInterface
  133.      */
  134.     #[\ReturnTypeWillChange]
  135.     public function getIterator()
  136.     {
  137.         return new PropertyPathIterator($this);
  138.     }
  139.     /**
  140.      * {@inheritdoc}
  141.      */
  142.     public function getElements()
  143.     {
  144.         return $this->elements;
  145.     }
  146.     /**
  147.      * {@inheritdoc}
  148.      */
  149.     public function getElement(int $index)
  150.     {
  151.         if (!isset($this->elements[$index])) {
  152.             throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.'$index));
  153.         }
  154.         return $this->elements[$index];
  155.     }
  156.     /**
  157.      * {@inheritdoc}
  158.      */
  159.     public function isProperty(int $index)
  160.     {
  161.         if (!isset($this->isIndex[$index])) {
  162.             throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.'$index));
  163.         }
  164.         return !$this->isIndex[$index];
  165.     }
  166.     /**
  167.      * {@inheritdoc}
  168.      */
  169.     public function isIndex(int $index)
  170.     {
  171.         if (!isset($this->isIndex[$index])) {
  172.             throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.'$index));
  173.         }
  174.         return $this->isIndex[$index];
  175.     }
  176. }