]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php
twig implementation
[github/wallabag/wallabag.git] / vendor / symfony / property-access / Symfony / Component / PropertyAccess / PropertyPath.php
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\PropertyAccess;
13
14 use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
15 use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
16 use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
17
18 /**
19 * Default implementation of {@link PropertyPathInterface}.
20 *
21 * @author Bernhard Schussek <bschussek@gmail.com>
22 */
23 class PropertyPath implements \IteratorAggregate, PropertyPathInterface
24 {
25 /**
26 * Character used for separating between plural and singular of an element.
27 * @var string
28 */
29 const SINGULAR_SEPARATOR = '|';
30
31 /**
32 * The elements of the property path
33 * @var array
34 */
35 private $elements = array();
36
37 /**
38 * The singular forms of the elements in the property path.
39 * @var array
40 */
41 private $singulars = array();
42
43 /**
44 * The number of elements in the property path
45 * @var integer
46 */
47 private $length;
48
49 /**
50 * Contains a Boolean for each property in $elements denoting whether this
51 * element is an index. It is a property otherwise.
52 * @var array
53 */
54 private $isIndex = array();
55
56 /**
57 * String representation of the path
58 * @var string
59 */
60 private $pathAsString;
61
62 /**
63 * Constructs a property path from a string.
64 *
65 * @param PropertyPath|string $propertyPath The property path as string or instance
66 *
67 * @throws UnexpectedTypeException If the given path is not a string
68 * @throws InvalidPropertyPathException If the syntax of the property path is not valid
69 */
70 public function __construct($propertyPath)
71 {
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;
80
81 return;
82 }
83 if (!is_string($propertyPath)) {
84 throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPath');
85 }
86
87 if ('' === $propertyPath) {
88 throw new InvalidPropertyPathException('The property path should not be empty.');
89 }
90
91 $this->pathAsString = $propertyPath;
92 $position = 0;
93 $remaining = $propertyPath;
94
95 // first element is evaluated differently - no leading dot for properties
96 $pattern = '/^(([^\.\[]+)|\[([^\]]+)\])(.*)/';
97
98 while (preg_match($pattern, $remaining, $matches)) {
99 if ('' !== $matches[2]) {
100 $element = $matches[2];
101 $this->isIndex[] = false;
102 } else {
103 $element = $matches[3];
104 $this->isIndex[] = true;
105 }
106 // Disabled this behaviour as the syntax is not yet final
107 //$pos = strpos($element, self::SINGULAR_SEPARATOR);
108 $pos = false;
109 $singular = null;
110
111 if (false !== $pos) {
112 $singular = substr($element, $pos + 1);
113 $element = substr($element, 0, $pos);
114 }
115
116 $this->elements[] = $element;
117 $this->singulars[] = $singular;
118
119 $position += strlen($matches[1]);
120 $remaining = $matches[4];
121 $pattern = '/^(\.(\w+)|\[([^\]]+)\])(.*)/';
122 }
123
124 if ('' !== $remaining) {
125 throw new InvalidPropertyPathException(sprintf(
126 'Could not parse property path "%s". Unexpected token "%s" at position %d',
127 $propertyPath,
128 $remaining{0},
129 $position
130 ));
131 }
132
133 $this->length = count($this->elements);
134 }
135
136 /**
137 * {@inheritdoc}
138 */
139 public function __toString()
140 {
141 return $this->pathAsString;
142 }
143
144 /**
145 * {@inheritdoc}
146 */
147 public function getLength()
148 {
149 return $this->length;
150 }
151
152 /**
153 * {@inheritdoc}
154 */
155 public function getParent()
156 {
157 if ($this->length <= 1) {
158 return null;
159 }
160
161 $parent = clone $this;
162
163 --$parent->length;
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);
168
169 return $parent;
170 }
171
172 /**
173 * Returns a new iterator for this path
174 *
175 * @return PropertyPathIteratorInterface
176 */
177 public function getIterator()
178 {
179 return new PropertyPathIterator($this);
180 }
181
182 /**
183 * {@inheritdoc}
184 */
185 public function getElements()
186 {
187 return $this->elements;
188 }
189
190 /**
191 * {@inheritdoc}
192 */
193 public function getElement($index)
194 {
195 if (!isset($this->elements[$index])) {
196 throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
197 }
198
199 return $this->elements[$index];
200 }
201
202 /**
203 * {@inheritdoc}
204 */
205 public function isProperty($index)
206 {
207 if (!isset($this->isIndex[$index])) {
208 throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
209 }
210
211 return !$this->isIndex[$index];
212 }
213
214 /**
215 * {@inheritdoc}
216 */
217 public function isIndex($index)
218 {
219 if (!isset($this->isIndex[$index])) {
220 throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
221 }
222
223 return $this->isIndex[$index];
224 }
225 }