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

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