/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionParser.php

https://bitbucket.org/hill2steve/mobileroom · PHP · 297 lines · 213 code · 69 blank · 15 comment · 30 complexity · 7b30138f19e5711113af6c7e6e8a6314 MD5 · raw file

  1. <?php
  2. /*
  3. * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace JMS\SecurityExtraBundle\Security\Authorization\Expression;
  18. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\NotExpression;
  19. use JMS\SecurityExtraBundle\Exception\RuntimeException;
  20. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\IsEqualExpression;
  21. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ParameterExpression;
  22. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression;
  23. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ConstantExpression;
  24. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\OrExpression;
  25. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\AndExpression;
  26. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ArrayExpression;
  27. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetItemExpression;
  28. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetPropertyExpression;
  29. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\MethodCallExpression;
  30. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface;
  31. use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression;
  32. final class ExpressionParser
  33. {
  34. const PRECEDENCE_OR = 10;
  35. const PRECEDENCE_AND = 15;
  36. const PRECEDENCE_IS_EQUAL = 20;
  37. const PRECEDENCE_NOT = 30;
  38. private $lexer;
  39. public function __construct()
  40. {
  41. $this->lexer = new ExpressionLexer();
  42. }
  43. public function parse($str)
  44. {
  45. $this->lexer->initialize($str);
  46. $expr = $this->Expression();
  47. if (null !== $this->lexer->lookahead) {
  48. throw new \RuntimeException(sprintf('Malformed expression. Expected end of expression, but got "%s" (%s).',
  49. $this->lexer->lookahead['value'], $this->lexer->getLiteral($this->lexer->lookahead['type'])));
  50. }
  51. return $expr;
  52. }
  53. private function Expression($precedence = 0)
  54. {
  55. $expr = $this->Primary();
  56. while (true) {
  57. if (ExpressionLexer::T_AND === $this->lexer->lookahead['type']
  58. && $precedence <= self::PRECEDENCE_AND) {
  59. $this->lexer->next();
  60. $expr = new AndExpression($expr, $this->Expression(
  61. self::PRECEDENCE_AND + 1));
  62. continue;
  63. }
  64. if (ExpressionLexer::T_OR === $this->lexer->lookahead['type']
  65. && $precedence <= self::PRECEDENCE_OR) {
  66. $this->lexer->next();
  67. $expr = new OrExpression($expr, $this->Expression(
  68. self::PRECEDENCE_OR + 1));
  69. continue;
  70. }
  71. if (ExpressionLexer::T_IS_EQUAL === $this->lexer->lookahead['type']
  72. && $precedence <= self::PRECEDENCE_IS_EQUAL) {
  73. $this->lexer->next();
  74. $expr = new IsEqualExpression($expr, $this->Expression(
  75. self::PRECEDENCE_IS_EQUAL + 1));
  76. continue;
  77. }
  78. break;
  79. }
  80. return $expr;
  81. }
  82. private function Primary()
  83. {
  84. if (ExpressionLexer::T_NOT === $this->lexer->lookahead['type']) {
  85. $this->lexer->next();
  86. $expr = new NotExpression($this->Expression(self::PRECEDENCE_NOT));
  87. return $this->Suffix($expr);
  88. }
  89. if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) {
  90. $this->lexer->next();
  91. $expr = $this->Expression();
  92. $this->match(ExpressionLexer::T_CLOSE_PARENTHESIS);
  93. return $this->Suffix($expr);
  94. }
  95. if (ExpressionLexer::T_STRING === $this->lexer->lookahead['type']) {
  96. return new ConstantExpression($this->match(ExpressionLexer::T_STRING));
  97. }
  98. if (ExpressionLexer::T_OPEN_BRACE === $this->lexer->lookahead['type']) {
  99. return $this->Suffix($this->MapExpr());
  100. }
  101. if (ExpressionLexer::T_OPEN_BRACKET === $this->lexer->lookahead['type']) {
  102. return $this->Suffix($this->ListExpr());
  103. }
  104. if (ExpressionLexer::T_IDENTIFIER === $this->lexer->lookahead['type']) {
  105. $name = $this->match(ExpressionLexer::T_IDENTIFIER);
  106. if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) {
  107. $args = $this->Arguments();
  108. return $this->Suffix(new FunctionExpression($name, $args));
  109. }
  110. return $this->Suffix(new VariableExpression($name));
  111. }
  112. if (ExpressionLexer::T_PARAMETER === $this->lexer->lookahead['type']) {
  113. return $this->Suffix(new ParameterExpression($this->match(ExpressionLexer::T_PARAMETER)));
  114. }
  115. $this->error('primary expression');
  116. }
  117. private function ListExpr()
  118. {
  119. $this->match(ExpressionLexer::T_OPEN_BRACKET);
  120. $elements = array();
  121. while (ExpressionLexer::T_CLOSE_BRACKET !== $this->lexer->lookahead['type']) {
  122. $elements[] = $this->Expression();
  123. if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) {
  124. break;
  125. }
  126. $this->lexer->next();
  127. }
  128. $this->match(ExpressionLexer::T_CLOSE_BRACKET);
  129. return new ArrayExpression($elements);
  130. }
  131. private function MapExpr()
  132. {
  133. $this->match(ExpressionLexer::T_OPEN_BRACE);
  134. $entries = array();
  135. while (ExpressionLexer::T_CLOSE_BRACE !== $this->lexer->lookahead['type']) {
  136. $key = $this->match(ExpressionLexer::T_STRING);
  137. $this->match(ExpressionLexer::T_COLON);
  138. $entries[$key] = $this->Expression();
  139. if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) {
  140. break;
  141. }
  142. $this->lexer->next();
  143. }
  144. $this->match(ExpressionLexer::T_CLOSE_BRACE);
  145. return new ArrayExpression($entries);
  146. }
  147. private function Suffix(ExpressionInterface $expr)
  148. {
  149. while (true) {
  150. if (ExpressionLexer::T_OBJECT_OPERATOR === $this->lexer->lookahead['type']) {
  151. $this->lexer->next();
  152. $name = $this->match(ExpressionLexer::T_IDENTIFIER);
  153. if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) {
  154. $args = $this->Arguments();
  155. $expr = new MethodCallExpression($expr, $name, $args);
  156. continue;
  157. }
  158. $expr = new GetPropertyExpression($expr, $name);
  159. continue;
  160. }
  161. if (ExpressionLexer::T_OPEN_BRACKET === $this->lexer->lookahead['type']) {
  162. $this->lexer->next();
  163. $key = $this->Expression();
  164. $this->match(ExpressionLexer::T_CLOSE_BRACKET);
  165. $expr = new GetItemExpression($expr, $key);
  166. continue;
  167. }
  168. break;
  169. }
  170. return $expr;
  171. }
  172. private function FunctionCall()
  173. {
  174. $name = $this->match(ExpressionLexer::T_IDENTIFIER);
  175. $args = $this->Arguments();
  176. return new FunctionExpression($name, $args);
  177. }
  178. private function Arguments()
  179. {
  180. $this->match(ExpressionLexer::T_OPEN_PARENTHESIS);
  181. $args = array();
  182. while (ExpressionLexer::T_CLOSE_PARENTHESIS !== $this->lexer->lookahead['type']) {
  183. $args[] = $this->Expression();
  184. if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) {
  185. break;
  186. }
  187. $this->match(ExpressionLexer::T_COMMA);
  188. }
  189. $this->match(ExpressionLexer::T_CLOSE_PARENTHESIS);
  190. return $args;
  191. }
  192. private function Value()
  193. {
  194. return $this->matchAny(array(ExpressionLexer::T_STRING));
  195. }
  196. private function matchAny(array $types)
  197. {
  198. if (null !== $this->lexer->lookahead) {
  199. foreach ($types as $type) {
  200. if ($type === $this->lexer->lookahead['type']) {
  201. $this->lexer->next();
  202. return $this->lexer->token['value'];
  203. }
  204. }
  205. }
  206. $this->error(sprintf('one of these tokens "%s"',
  207. implode('", "', array_map(array('JMS\SecurityExtraBundle\Security\Authorization\Expression\Lexer', 'getLiteral'), $types))
  208. ));
  209. }
  210. private function match($type)
  211. {
  212. if (null === $this->lexer->lookahead
  213. || $type !== $this->lexer->lookahead['type']) {
  214. $this->error(sprintf('token "%s"', ExpressionLexer::getLiteral($type)));
  215. }
  216. $this->lexer->next();
  217. return $this->lexer->token['value'];
  218. }
  219. private function error($expected)
  220. {
  221. $actual = null === $this->lexer->lookahead ? 'end of file'
  222. : sprintf('token "%s" with value "%s" at position %d',
  223. ExpressionLexer::getLiteral($this->lexer->lookahead['type']),
  224. $this->lexer->lookahead['value'],
  225. $this->lexer->lookahead['position']);
  226. throw new RuntimeException(sprintf('Expected %s, but got %s.', $expected, $actual));
  227. }
  228. }