ExpressionLanguage.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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\ExpressionLanguage;
  11. use Symfony\Component\ExpressionLanguage\ParserCache\ArrayParserCache;
  12. use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
  13. /**
  14. * Allows to compile and evaluate expressions written in your own DSL.
  15. *
  16. * @author Fabien Potencier <fabien@symfony.com>
  17. */
  18. class ExpressionLanguage
  19. {
  20. private $cache;
  21. private $lexer;
  22. private $parser;
  23. private $compiler;
  24. protected $functions = array();
  25. /**
  26. * @param ParserCacheInterface $cache
  27. * @param ExpressionFunctionProviderInterface[] $providers
  28. */
  29. public function __construct(ParserCacheInterface $cache = null, array $providers = array())
  30. {
  31. $this->cache = $cache ?: new ArrayParserCache();
  32. $this->registerFunctions();
  33. foreach ($providers as $provider) {
  34. $this->registerProvider($provider);
  35. }
  36. }
  37. /**
  38. * Compiles an expression source code.
  39. *
  40. * @param Expression|string $expression The expression to compile
  41. * @param array $names An array of valid names
  42. *
  43. * @return string The compiled PHP source code
  44. */
  45. public function compile($expression, $names = array())
  46. {
  47. return $this->getCompiler()->compile($this->parse($expression, $names)->getNodes())->getSource();
  48. }
  49. /**
  50. * Evaluate an expression.
  51. *
  52. * @param Expression|string $expression The expression to compile
  53. * @param array $values An array of values
  54. *
  55. * @return mixed The result of the evaluation of the expression
  56. */
  57. public function evaluate($expression, $values = array())
  58. {
  59. return $this->parse($expression, array_keys($values))->getNodes()->evaluate($this->functions, $values);
  60. }
  61. /**
  62. * Parses an expression.
  63. *
  64. * @param Expression|string $expression The expression to parse
  65. * @param array $names An array of valid names
  66. *
  67. * @return ParsedExpression A ParsedExpression instance
  68. */
  69. public function parse($expression, $names)
  70. {
  71. if ($expression instanceof ParsedExpression) {
  72. return $expression;
  73. }
  74. asort($names);
  75. $cacheKeyItems = array();
  76. foreach ($names as $nameKey => $name) {
  77. $cacheKeyItems[] = \is_int($nameKey) ? $name : $nameKey.':'.$name;
  78. }
  79. $key = $expression.'//'.implode('|', $cacheKeyItems);
  80. if (null === $parsedExpression = $this->cache->fetch($key)) {
  81. $nodes = $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names);
  82. $parsedExpression = new ParsedExpression((string) $expression, $nodes);
  83. $this->cache->save($key, $parsedExpression);
  84. }
  85. return $parsedExpression;
  86. }
  87. /**
  88. * Registers a function.
  89. *
  90. * @param string $name The function name
  91. * @param callable $compiler A callable able to compile the function
  92. * @param callable $evaluator A callable able to evaluate the function
  93. *
  94. * @throws \LogicException when registering a function after calling evaluate(), compile() or parse()
  95. *
  96. * @see ExpressionFunction
  97. */
  98. public function register($name, $compiler, $evaluator)
  99. {
  100. if (null !== $this->parser) {
  101. throw new \LogicException('Registering functions after calling evaluate(), compile() or parse() is not supported.');
  102. }
  103. $this->functions[$name] = array('compiler' => $compiler, 'evaluator' => $evaluator);
  104. }
  105. public function addFunction(ExpressionFunction $function)
  106. {
  107. $this->register($function->getName(), $function->getCompiler(), $function->getEvaluator());
  108. }
  109. public function registerProvider(ExpressionFunctionProviderInterface $provider)
  110. {
  111. foreach ($provider->getFunctions() as $function) {
  112. $this->addFunction($function);
  113. }
  114. }
  115. protected function registerFunctions()
  116. {
  117. $this->register('constant', function ($constant) {
  118. return sprintf('constant(%s)', $constant);
  119. }, function (array $values, $constant) {
  120. return \constant($constant);
  121. });
  122. }
  123. private function getLexer()
  124. {
  125. if (null === $this->lexer) {
  126. $this->lexer = new Lexer();
  127. }
  128. return $this->lexer;
  129. }
  130. private function getParser()
  131. {
  132. if (null === $this->parser) {
  133. $this->parser = new Parser($this->functions);
  134. }
  135. return $this->parser;
  136. }
  137. private function getCompiler()
  138. {
  139. if (null === $this->compiler) {
  140. $this->compiler = new Compiler($this->functions);
  141. }
  142. return $this->compiler->reset();
  143. }
  144. }