206 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			PHP
		
	
	
		
		
			
		
	
	
			206 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			PHP
		
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * This file is part of the Symfony package.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * (c) Fabien Potencier <fabien@symfony.com>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * For the full copyright and license information, please view the LICENSE
							 | 
						||
| 
								 | 
							
								 * file that was distributed with this source code.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Symfony\Component\PropertyAccess;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
							 | 
						||
| 
								 | 
							
								use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
							 | 
						||
| 
								 | 
							
								use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Default implementation of {@link PropertyPathInterface}.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @author Bernhard Schussek <bschussek@gmail.com>
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class PropertyPath implements \IteratorAggregate, PropertyPathInterface
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Character used for separating between plural and singular of an element.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public const SINGULAR_SEPARATOR = '|';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * The elements of the property path.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @var array
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private $elements = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * The number of elements in the property path.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @var int
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private $length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Contains a Boolean for each property in $elements denoting whether this
							 | 
						||
| 
								 | 
							
								     * element is an index. It is a property otherwise.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @var array
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private $isIndex = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * String representation of the path.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @var string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private $pathAsString;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Constructs a property path from a string.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param PropertyPath|string $propertyPath The property path as string or instance
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @throws InvalidArgumentException     If the given path is not a string
							 | 
						||
| 
								 | 
							
								     * @throws InvalidPropertyPathException If the syntax of the property path is not valid
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function __construct($propertyPath)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // Can be used as copy constructor
							 | 
						||
| 
								 | 
							
								        if ($propertyPath instanceof self) {
							 | 
						||
| 
								 | 
							
								            /* @var PropertyPath $propertyPath */
							 | 
						||
| 
								 | 
							
								            $this->elements = $propertyPath->elements;
							 | 
						||
| 
								 | 
							
								            $this->length = $propertyPath->length;
							 | 
						||
| 
								 | 
							
								            $this->isIndex = $propertyPath->isIndex;
							 | 
						||
| 
								 | 
							
								            $this->pathAsString = $propertyPath->pathAsString;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!\is_string($propertyPath)) {
							 | 
						||
| 
								 | 
							
								            throw new InvalidArgumentException(sprintf('The property path constructor needs a string or an instance of "Symfony\Component\PropertyAccess\PropertyPath". Got: "%s".', \is_object($propertyPath) ? \get_class($propertyPath) : \gettype($propertyPath)));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ('' === $propertyPath) {
							 | 
						||
| 
								 | 
							
								            throw new InvalidPropertyPathException('The property path should not be empty.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $this->pathAsString = $propertyPath;
							 | 
						||
| 
								 | 
							
								        $position = 0;
							 | 
						||
| 
								 | 
							
								        $remaining = $propertyPath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // first element is evaluated differently - no leading dot for properties
							 | 
						||
| 
								 | 
							
								        $pattern = '/^(([^\.\[]++)|\[([^\]]++)\])(.*)/';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        while (preg_match($pattern, $remaining, $matches)) {
							 | 
						||
| 
								 | 
							
								            if ('' !== $matches[2]) {
							 | 
						||
| 
								 | 
							
								                $element = $matches[2];
							 | 
						||
| 
								 | 
							
								                $this->isIndex[] = false;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                $element = $matches[3];
							 | 
						||
| 
								 | 
							
								                $this->isIndex[] = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            $this->elements[] = $element;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            $position += \strlen($matches[1]);
							 | 
						||
| 
								 | 
							
								            $remaining = $matches[4];
							 | 
						||
| 
								 | 
							
								            $pattern = '/^(\.([^\.|\[]++)|\[([^\]]++)\])(.*)/';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ('' !== $remaining) {
							 | 
						||
| 
								 | 
							
								            throw new InvalidPropertyPathException(sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d.', $propertyPath, $remaining[0], $position));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $this->length = \count($this->elements);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function __toString()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->pathAsString;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getLength()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->length;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getParent()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if ($this->length <= 1) {
							 | 
						||
| 
								 | 
							
								            return null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $parent = clone $this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        --$parent->length;
							 | 
						||
| 
								 | 
							
								        $parent->pathAsString = substr($parent->pathAsString, 0, max(strrpos($parent->pathAsString, '.'), strrpos($parent->pathAsString, '[')));
							 | 
						||
| 
								 | 
							
								        array_pop($parent->elements);
							 | 
						||
| 
								 | 
							
								        array_pop($parent->isIndex);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $parent;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns a new iterator for this path.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return PropertyPathIteratorInterface
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getIterator()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return new PropertyPathIterator($this);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getElements()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->elements;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getElement($index)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!isset($this->elements[$index])) {
							 | 
						||
| 
								 | 
							
								            throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $this->elements[$index];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function isProperty($index)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!isset($this->isIndex[$index])) {
							 | 
						||
| 
								 | 
							
								            throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return !$this->isIndex[$index];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function isIndex($index)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!isset($this->isIndex[$index])) {
							 | 
						||
| 
								 | 
							
								            throw new OutOfBoundsException(sprintf('The index "%s" is not within the property path.', $index));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $this->isIndex[$index];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |