/src/PHPagstract/Page/Resolver/PropertyResolver.php

https://gitlab.com/php.bjoernbartels.earth/phpagstract · PHP · 231 lines · 117 code · 30 blank · 84 comment · 16 complexity · f500e09c78caec32f6136902eda1e344 MD5 · raw file

  1. <?php
  2. namespace PHPagstract\Page\Resolver;
  3. use PHPagstract\Traits\ScopesTrait;
  4. use PHPagstract\Traits\StreamTrait;
  5. use PHPagstract\Symbol\Symbols\AbstractPropertySymbol;
  6. /**
  7. * PHPagstract generic property resolver class
  8. *
  9. * generic methods to resolve property references
  10. *
  11. * @package PHPagstract
  12. * @author Björn Bartels <coding@bjoernbartels.earth>
  13. * @link https://gitlab.bjoernbartels.earth/groups/zf2
  14. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  15. * @copyright copyright (c) 2016 Björn Bartels <coding@bjoernbartels.earth>
  16. */
  17. class PropertyResolver
  18. {
  19. use StreamTrait;
  20. use ScopesTrait;
  21. /**
  22. * regular expression to parse reference string representation
  23. * ex: ".artikel.verknuepfungen.kacheln[0].liste[1].label"
  24. *
  25. * @var string|RegEx
  26. */
  27. private $parseRegex = "/(\\.\\.\\/\\.|\\.)?([a-zA-Z0-9\\-\\_]*)\\[?([0-9]*)]?/i";
  28. /**
  29. * regular expression to match invalid characters in a reference string representation
  30. * aka, every character which is not allowed in a referencestring like:
  31. * ".artikel().verknuepfungen,kacheln[0]/liste[1].label"
  32. *
  33. * @var string|RegEx
  34. */
  35. private $errorRegex = "/([^a-zA-Z0-9\-\_\]\[\.\/])/i";
  36. /**
  37. * throw exception on error?
  38. *
  39. * @var boolean
  40. */
  41. protected $throwOnError;
  42. /**
  43. * @param boolean $throwOnError throw exception on error?
  44. */
  45. public function __construct(AbstractPropertySymbol $dataStream = null, $throwOnError = false)
  46. {
  47. if ($dataStream !== null) {
  48. $this->setStream($dataStream);
  49. $stream = $this->getStream();
  50. $this->addScope($stream);
  51. $scope = $this->getRootScope();
  52. $this->setContext($scope);
  53. }
  54. $this->throwOnError = $throwOnError;
  55. }
  56. /**
  57. * parse a reference string into parts
  58. *
  59. * @param string $reference
  60. * @return array
  61. */
  62. public function parsePropertyReferenceString($reference)
  63. {
  64. $tokens = array();
  65. preg_match_all($this->parseRegex, $reference, $tokens);
  66. $errors = preg_match_all($this->errorRegex, $reference);
  67. if (($errors !== false) && ($errors > 0)) {
  68. return array();
  69. }
  70. $parts = array();
  71. foreach ($tokens[2] as $idx => $property) {
  72. if (!empty($property)) {
  73. $parts[] = array(
  74. $tokens[1][$idx], // dot/parent
  75. $tokens[2][$idx], // property-name
  76. $tokens[3][$idx], // list index
  77. );
  78. }
  79. }
  80. return $parts;
  81. }
  82. /**
  83. * resolve a reference string to data object/array
  84. *
  85. * @param string $reference
  86. * @return mixed
  87. */
  88. public function resolvePropertyByReference($reference)
  89. {
  90. if (strpos($reference, "../") === 0) {
  91. // we have a parental reference here, so get parent and recurse
  92. $data = $this->getContext()->getParent();
  93. $reference = mb_substr($reference, 3);
  94. $this->setContext($data);
  95. }
  96. $data = $this->getContext();
  97. $tokens = $this->parsePropertyReferenceString($reference);
  98. foreach ($tokens as $value) {
  99. $propertyName = $value[1];
  100. $index = $value[2];
  101. $type = $data->getType();
  102. if (in_array($type, array('root', 'object'))) {
  103. $properties = $data->get('properties');
  104. if (isset($properties->$propertyName)) {
  105. $data = $properties->$propertyName;
  106. }
  107. $items = $data->get('items');
  108. if (($index != '') && isset($items[$index])) {
  109. $data = $items[$index];
  110. }
  111. }
  112. }
  113. $rootContext = $this->getContext();
  114. if (($rootContext->getName() == $data->getName()) && ($rootContext->getParent()->getName() == $data->getParent()->getName())) {
  115. return null;
  116. }
  117. return $data;
  118. }
  119. /**
  120. * resolve a reference string to data object/array
  121. *
  122. * @param string $reference
  123. * @return mixed
  124. */
  125. public function findPropertyInScopes($reference)
  126. {
  127. // process scopes in reverse order
  128. $scopes = $this->getScopes();
  129. $scopes = array_reverse($scopes);
  130. // remember current content
  131. $currentContext = $this->getContext();
  132. $propertyInScope = $this->getRootScope();
  133. foreach ($scopes as $scope) {
  134. // set current scope to resolver context
  135. $this->setContext($scope);
  136. $propertyInScope = $this->resolvePropertyByReference($reference);
  137. if ($propertyInScope !== null) {
  138. // could resolve property in current scope,
  139. // return it and reset context
  140. $this->setContext($currentContext);
  141. return $propertyInScope;
  142. }
  143. }
  144. // no property found, reset context
  145. $this->setContext($currentContext);
  146. return $propertyInScope;
  147. }
  148. /**
  149. * resolve a reference string to data object/array
  150. *
  151. * @param string $reference
  152. * @return mixed
  153. */
  154. public function getPropertyByReference($reference)
  155. {
  156. return $this->findPropertyInScopes($reference);
  157. }
  158. /**
  159. * resolve a reference string to data object/array
  160. *
  161. * @param string $reference
  162. * @return mixed
  163. */
  164. public function getValueByReference($reference)
  165. {
  166. $property = $this->getPropertyByReference($reference);
  167. switch ($property->getType())
  168. {
  169. case 'object': $properties = $property->get('properties');
  170. return $properties;
  171. case 'list': $list = $property->get('items');
  172. return $list;
  173. default: $value = $property->getProperty();
  174. return $value;
  175. }
  176. }
  177. /**
  178. * context, a.k.a. data container to map the reference to
  179. *
  180. * @var array|object
  181. */
  182. private $context = null;
  183. /**
  184. * get the $context data container
  185. *
  186. * @return array|object
  187. */
  188. public function getContext()
  189. {
  190. return $this->context;
  191. }
  192. /**
  193. * set the $context data container
  194. *
  195. * @param array|object $context
  196. */
  197. public function setContext(&$context)
  198. {
  199. $this->context = $context;
  200. }
  201. }