4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\PropertyAccess
;
14 use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
;
15 use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException
;
16 use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
;
19 * Default implementation of {@link PropertyPathInterface}.
21 * @author Bernhard Schussek <bschussek@gmail.com>
23 class PropertyPath
implements \IteratorAggregate
, PropertyPathInterface
26 * Character used for separating between plural and singular of an element.
29 const SINGULAR_SEPARATOR
= '|';
32 * The elements of the property path
35 private $elements = array();
38 * The singular forms of the elements in the property path.
41 private $singulars = array();
44 * The number of elements in the property path
50 * Contains a Boolean for each property in $elements denoting whether this
51 * element is an index. It is a property otherwise.
54 private $isIndex = array();
57 * String representation of the path
60 private $pathAsString;
63 * Constructs a property path from a string.
65 * @param PropertyPath|string $propertyPath The property path as string or instance
67 * @throws UnexpectedTypeException If the given path is not a string
68 * @throws InvalidPropertyPathException If the syntax of the property path is not valid
70 public function __construct($propertyPath)
72 // Can be used as copy constructor
73 if ($propertyPath instanceof PropertyPath
) {
74 /* @var PropertyPath $propertyPath */
75 $this->elements
= $propertyPath->elements
;
76 $this->singulars
= $propertyPath->singulars
;
77 $this->length
= $propertyPath->length
;
78 $this->isIndex
= $propertyPath->isIndex
;
79 $this->pathAsString
= $propertyPath->pathAsString
;
83 if (!is_string($propertyPath)) {
84 throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPath');
87 if ('' === $propertyPath) {
88 throw new InvalidPropertyPathException('The property path should not be empty.');
91 $this->pathAsString
= $propertyPath;
93 $remaining = $propertyPath;
95 // first element is evaluated differently - no leading dot for properties
96 $pattern = '/^(([^\.\[]+)|\[([^\]]+)\])(.*)/';
98 while (preg_match($pattern, $remaining, $matches)) {
99 if ('' !== $matches[2]) {
100 $element = $matches[2];
101 $this->isIndex
[] = false;
103 $element = $matches[3];
104 $this->isIndex
[] = true;
106 // Disabled this behaviour as the syntax is not yet final
107 //$pos = strpos($element, self::SINGULAR_SEPARATOR);
111 if (false !== $pos) {
112 $singular = substr($element, $pos +
1);
113 $element = substr($element, 0, $pos);
116 $this->elements
[] = $element;
117 $this->singulars
[] = $singular;
119 $position +
= strlen($matches[1]);
120 $remaining = $matches[4];
121 $pattern = '/^(\.(\w+)|\[([^\]]+)\])(.*)/';
124 if ('' !== $remaining) {
125 throw new InvalidPropertyPathException(sprintf(
126 'Could not parse property path "%s". Unexpected token "%s" at position %d',
133 $this->length
= count($this->elements
);
139 public function __toString()
141 return $this->pathAsString
;
147 public function getLength()
149 return $this->length
;
155 public function getParent()
157 if ($this->length
<= 1) {
161 $parent = clone $this;
164 $parent->pathAsString
= substr($parent->pathAsString
, 0, max(strrpos($parent->pathAsString
, '.'), strrpos($parent->pathAsString
, '[')));
165 array_pop($parent->elements
);
166 array_pop($parent->singulars
);
167 array_pop($parent->isIndex
);
173 * Returns a new iterator for this path
175 * @return PropertyPathIteratorInterface
177 public function getIterator()
179 return new PropertyPathIterator($this);
185 public function getElements()
187 return $this->elements
;
193 public function getElement($index)
195 if (!isset($this->elements
[$index])) {
196 throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
199 return $this->elements
[$index];
205 public function isProperty($index)
207 if (!isset($this->isIndex
[$index])) {
208 throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
211 return !$this->isIndex
[$index];
217 public function isIndex($index)
219 if (!isset($this->isIndex
[$index])) {
220 throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
223 return $this->isIndex
[$index];