PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/inc/lib/Twig/Node/Expression/Call.php

https://gitlab.com/potion/librechan
PHP | 210 lines | 173 code | 28 blank | 9 comment | 41 complexity | 2ccdcfaa737c027a72f3250b2848709a MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of Twig.
  4. *
  5. * (c) 2012 Fabien Potencier
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
  11. {
  12. protected function compileCallable(Twig_Compiler $compiler)
  13. {
  14. $closingParenthesis = false;
  15. if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
  16. if (is_string($callable)) {
  17. $compiler->raw($callable);
  18. } elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) {
  19. $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1]));
  20. } else {
  21. $type = ucfirst($this->getAttribute('type'));
  22. $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
  23. $closingParenthesis = true;
  24. }
  25. } else {
  26. $compiler->raw($this->getAttribute('thing')->compile());
  27. }
  28. $this->compileArguments($compiler);
  29. if ($closingParenthesis) {
  30. $compiler->raw(')');
  31. }
  32. }
  33. protected function compileArguments(Twig_Compiler $compiler)
  34. {
  35. $compiler->raw('(');
  36. $first = true;
  37. if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
  38. $compiler->raw('$this->env');
  39. $first = false;
  40. }
  41. if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
  42. if (!$first) {
  43. $compiler->raw(', ');
  44. }
  45. $compiler->raw('$context');
  46. $first = false;
  47. }
  48. if ($this->hasAttribute('arguments')) {
  49. foreach ($this->getAttribute('arguments') as $argument) {
  50. if (!$first) {
  51. $compiler->raw(', ');
  52. }
  53. $compiler->string($argument);
  54. $first = false;
  55. }
  56. }
  57. if ($this->hasNode('node')) {
  58. if (!$first) {
  59. $compiler->raw(', ');
  60. }
  61. $compiler->subcompile($this->getNode('node'));
  62. $first = false;
  63. }
  64. if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) {
  65. $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
  66. $arguments = $this->getArguments($callable, $this->getNode('arguments'));
  67. foreach ($arguments as $node) {
  68. if (!$first) {
  69. $compiler->raw(', ');
  70. }
  71. $compiler->subcompile($node);
  72. $first = false;
  73. }
  74. }
  75. $compiler->raw(')');
  76. }
  77. protected function getArguments($callable, $arguments)
  78. {
  79. $callType = $this->getAttribute('type');
  80. $callName = $this->getAttribute('name');
  81. $parameters = array();
  82. $named = false;
  83. foreach ($arguments as $name => $node) {
  84. if (!is_int($name)) {
  85. $named = true;
  86. $name = $this->normalizeName($name);
  87. } elseif ($named) {
  88. throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName));
  89. }
  90. $parameters[$name] = $node;
  91. }
  92. if (!$named) {
  93. return $parameters;
  94. }
  95. if (!$callable) {
  96. throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $callType, $callName));
  97. }
  98. // manage named arguments
  99. if (is_array($callable)) {
  100. $r = new ReflectionMethod($callable[0], $callable[1]);
  101. } elseif (is_object($callable) && !$callable instanceof Closure) {
  102. $r = new ReflectionObject($callable);
  103. $r = $r->getMethod('__invoke');
  104. } elseif (is_string($callable) && false !== strpos($callable, '::')) {
  105. $r = new ReflectionMethod($callable);
  106. } else {
  107. $r = new ReflectionFunction($callable);
  108. }
  109. $definition = $r->getParameters();
  110. if ($this->hasNode('node')) {
  111. array_shift($definition);
  112. }
  113. if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
  114. array_shift($definition);
  115. }
  116. if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
  117. array_shift($definition);
  118. }
  119. if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
  120. foreach ($this->getAttribute('arguments') as $argument) {
  121. array_shift($definition);
  122. }
  123. }
  124. $arguments = array();
  125. $names = array();
  126. $missingArguments = array();
  127. $optionalArguments = array();
  128. $pos = 0;
  129. foreach ($definition as $param) {
  130. $names[] = $name = $this->normalizeName($param->name);
  131. if (array_key_exists($name, $parameters)) {
  132. if (array_key_exists($pos, $parameters)) {
  133. throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName));
  134. }
  135. if (!empty($missingArguments)) {
  136. throw new Twig_Error_Syntax(sprintf(
  137. 'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
  138. $name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments))
  139. );
  140. }
  141. $arguments = array_merge($arguments, $optionalArguments);
  142. $arguments[] = $parameters[$name];
  143. unset($parameters[$name]);
  144. $optionalArguments = array();
  145. } elseif (array_key_exists($pos, $parameters)) {
  146. $arguments = array_merge($arguments, $optionalArguments);
  147. $arguments[] = $parameters[$pos];
  148. unset($parameters[$pos]);
  149. $optionalArguments = array();
  150. ++$pos;
  151. } elseif ($param->isDefaultValueAvailable()) {
  152. $optionalArguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
  153. } elseif ($param->isOptional()) {
  154. if (empty($parameters)) {
  155. break;
  156. } else {
  157. $missingArguments[] = $name;
  158. }
  159. } else {
  160. throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName));
  161. }
  162. }
  163. if (!empty($parameters)) {
  164. $unknownParameter = null;
  165. foreach ($parameters as $parameter) {
  166. if ($parameter instanceof Twig_Node) {
  167. $unknownParameter = $parameter;
  168. break;
  169. }
  170. }
  171. throw new Twig_Error_Syntax(sprintf(
  172. 'Unknown argument%s "%s" for %s "%s(%s)".',
  173. count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
  174. ), $unknownParameter ? $unknownParameter->getLine() : -1);
  175. }
  176. return $arguments;
  177. }
  178. protected function normalizeName($name)
  179. {
  180. return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
  181. }
  182. }