This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Component/PropertyAccess/PropertyPath.php

236 lines
6.1 KiB
PHP
Raw Normal View History

<?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.
2014-12-21 17:00:50 +00:00
*
* @var string
*/
const SINGULAR_SEPARATOR = '|';
/**
2014-12-21 17:00:50 +00:00
* The elements of the property path.
*
* @var array
*/
private $elements = array();
/**
* The singular forms of the elements in the property path.
2014-12-21 17:00:50 +00:00
*
* @var array
*/
private $singulars = array();
/**
2014-12-21 17:00:50 +00:00
* The number of elements in the property path.
*
2014-04-16 11:30:19 +01:00
* @var int
*/
private $length;
/**
* Contains a Boolean for each property in $elements denoting whether this
* element is an index. It is a property otherwise.
2014-12-21 17:00:50 +00:00
*
* @var array
*/
private $isIndex = array();
/**
2014-12-21 17:00:50 +00:00
* 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 PropertyPath) {
/* @var PropertyPath $propertyPath */
$this->elements = $propertyPath->elements;
$this->singulars = $propertyPath->singulars;
$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;
}
2015-01-21 19:48:04 +00:00
$pos = false;
$singular = null;
if (false !== $pos) {
$singular = substr($element, $pos + 1);
$element = substr($element, 0, $pos);
}
$this->elements[] = $element;
$this->singulars[] = $singular;
$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,
2014-02-11 15:38:11 +00:00
$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) {
2014-04-16 08:15:58 +01:00
return;
}
$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->singulars);
array_pop($parent->isIndex);
return $parent;
}
/**
2014-12-21 17:00:50 +00:00
* 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])) {
2013-04-10 11:24:37 +01:00
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])) {
2013-04-10 11:24:37 +01:00
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])) {
2013-04-10 11:24:37 +01:00
throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
}
return $this->isIndex[$index];
}
}