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