/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyPath.php

https://gitlab.com/matijabelec/bigpandadev · PHP · 235 lines · 119 code · 35 blank · 81 comment · 12 complexity · f891116857ccc7cd8a43b1554e5bfa10 MD5 · raw file

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