Revision 1852
Added by darkviper almost 13 years ago
| Parser.php | ||
|---|---|---|
| 29 | 29 |
protected $macros; |
| 30 | 30 |
protected $env; |
| 31 | 31 |
protected $reservedMacroNames; |
| 32 |
protected $importedFunctions; |
|
| 33 |
protected $tmpVarCount; |
|
| 32 |
protected $importedSymbols; |
|
| 34 | 33 |
protected $traits; |
| 34 |
protected $embeddedTemplates = array(); |
|
| 35 | 35 |
|
| 36 | 36 |
/** |
| 37 | 37 |
* Constructor. |
| ... | ... | |
| 50 | 50 |
|
| 51 | 51 |
public function getVarName() |
| 52 | 52 |
{
|
| 53 |
return sprintf('__internal_%s_%d', substr($this->env->getTemplateClass($this->stream->getFilename()), strlen($this->env->getTemplateClassPrefix())), ++$this->tmpVarCount);
|
|
| 53 |
return sprintf('__internal_%s', hash('sha1', uniqid(mt_rand(), true), false));
|
|
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 |
public function getFilename() |
|
| 57 |
{
|
|
| 58 |
return $this->stream->getFilename(); |
|
| 59 |
} |
|
| 60 |
|
|
| 56 | 61 |
/** |
| 57 | 62 |
* Converts a token stream to a node tree. |
| 58 | 63 |
* |
| 59 |
* @param Twig_TokenStream $stream A token stream instance
|
|
| 64 |
* @param Twig_TokenStream $stream A token stream instance |
|
| 60 | 65 |
* |
| 61 | 66 |
* @return Twig_Node_Module A node tree |
| 62 | 67 |
*/ |
| 63 |
public function parse(Twig_TokenStream $stream) |
|
| 68 |
public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
|
|
| 64 | 69 |
{
|
| 65 | 70 |
// push all variables into the stack to keep the current state of the parser |
| 66 | 71 |
$vars = get_object_vars($this); |
| 67 | 72 |
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser']); |
| 68 | 73 |
$this->stack[] = $vars; |
| 69 | 74 |
|
| 70 |
$this->tmpVarCount = 0; |
|
| 71 |
|
|
| 72 | 75 |
// tag handlers |
| 73 | 76 |
if (null === $this->handlers) {
|
| 74 | 77 |
$this->handlers = $this->env->getTokenParsers(); |
| ... | ... | |
| 90 | 93 |
$this->macros = array(); |
| 91 | 94 |
$this->traits = array(); |
| 92 | 95 |
$this->blockStack = array(); |
| 93 |
$this->importedFunctions = array(array()); |
|
| 96 |
$this->importedSymbols = array(array()); |
|
| 97 |
$this->embeddedTemplates = array(); |
|
| 94 | 98 |
|
| 95 | 99 |
try {
|
| 96 |
$body = $this->subparse(null);
|
|
| 100 |
$body = $this->subparse($test, $dropNeedle);
|
|
| 97 | 101 |
|
| 98 | 102 |
if (null !== $this->parent) {
|
| 99 | 103 |
if (null === $body = $this->filterBodyNodes($body)) {
|
| ... | ... | |
| 101 | 105 |
} |
| 102 | 106 |
} |
| 103 | 107 |
} catch (Twig_Error_Syntax $e) {
|
| 104 |
if (null === $e->getTemplateFile()) {
|
|
| 105 |
$e->setTemplateFile($this->stream->getFilename());
|
|
| 108 |
if (!$e->getTemplateFile()) {
|
|
| 109 |
$e->setTemplateFile($this->getFilename()); |
|
| 106 | 110 |
} |
| 107 | 111 |
|
| 112 |
if (!$e->getTemplateLine()) {
|
|
| 113 |
$e->setTemplateLine($this->stream->getCurrent()->getLine()); |
|
| 114 |
} |
|
| 115 |
|
|
| 108 | 116 |
throw $e; |
| 109 | 117 |
} |
| 110 | 118 |
|
| 111 |
$node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->stream->getFilename());
|
|
| 119 |
$node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->getFilename());
|
|
| 112 | 120 |
|
| 113 | 121 |
$traverser = new Twig_NodeTraverser($this->env, $this->visitors); |
| 114 | 122 |
|
| ... | ... | |
| 145 | 153 |
$token = $this->getCurrentToken(); |
| 146 | 154 |
|
| 147 | 155 |
if ($token->getType() !== Twig_Token::NAME_TYPE) {
|
| 148 |
throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->stream->getFilename());
|
|
| 156 |
throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->getFilename());
|
|
| 149 | 157 |
} |
| 150 | 158 |
|
| 151 | 159 |
if (null !== $test && call_user_func($test, $token)) {
|
| ... | ... | |
| 163 | 171 |
$subparser = $this->handlers->getTokenParser($token->getValue()); |
| 164 | 172 |
if (null === $subparser) {
|
| 165 | 173 |
if (null !== $test) {
|
| 166 |
throw new Twig_Error_Syntax(sprintf('Unexpected tag name "%s" (expecting closing tag for the "%s" tag defined near line %s)', $token->getValue(), $test[0]->getTag(), $lineno), $token->getLine(), $this->stream->getFilename());
|
|
| 174 |
$error = sprintf('Unexpected tag name "%s"', $token->getValue());
|
|
| 175 |
if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) {
|
|
| 176 |
$error .= sprintf(' (expecting closing tag for the "%s" tag defined near line %s)', $test[0]->getTag(), $lineno);
|
|
| 177 |
} |
|
| 178 |
|
|
| 179 |
throw new Twig_Error_Syntax($error, $token->getLine(), $this->getFilename()); |
|
| 167 | 180 |
} |
| 168 | 181 |
|
| 169 | 182 |
$message = sprintf('Unknown tag name "%s"', $token->getValue());
|
| ... | ... | |
| 171 | 184 |
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
|
| 172 | 185 |
} |
| 173 | 186 |
|
| 174 |
throw new Twig_Error_Syntax($message, $token->getLine(), $this->stream->getFilename());
|
|
| 187 |
throw new Twig_Error_Syntax($message, $token->getLine(), $this->getFilename()); |
|
| 175 | 188 |
} |
| 176 | 189 |
|
| 177 | 190 |
$this->stream->next(); |
| ... | ... | |
| 183 | 196 |
break; |
| 184 | 197 |
|
| 185 | 198 |
default: |
| 186 |
throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', -1, $this->stream->getFilename());
|
|
| 199 |
throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->getFilename());
|
|
| 187 | 200 |
} |
| 188 | 201 |
} |
| 189 | 202 |
|
| ... | ... | |
| 255 | 268 |
} |
| 256 | 269 |
|
| 257 | 270 |
if (in_array($name, $this->reservedMacroNames)) {
|
| 258 |
throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword', $name), $node->getLine());
|
|
| 271 |
throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword', $name), $node->getLine(), $this->getFilename());
|
|
| 259 | 272 |
} |
| 260 | 273 |
|
| 261 | 274 |
$this->macros[$name] = $node; |
| ... | ... | |
| 271 | 284 |
return count($this->traits) > 0; |
| 272 | 285 |
} |
| 273 | 286 |
|
| 274 |
public function addImportedFunction($alias, $name, Twig_Node_Expression $node)
|
|
| 287 |
public function embedTemplate(Twig_Node_Module $template)
|
|
| 275 | 288 |
{
|
| 276 |
$this->importedFunctions[0][$alias] = array('name' => $name, 'node' => $node);
|
|
| 289 |
$template->setIndex(mt_rand()); |
|
| 290 |
|
|
| 291 |
$this->embeddedTemplates[] = $template; |
|
| 277 | 292 |
} |
| 278 | 293 |
|
| 279 |
public function getImportedFunction($alias)
|
|
| 294 |
public function addImportedSymbol($type, $alias, $name = null, Twig_Node_Expression $node = null)
|
|
| 280 | 295 |
{
|
| 281 |
foreach ($this->importedFunctions as $functions) {
|
|
| 282 |
if (isset($functions[$alias])) {
|
|
| 283 |
return $functions[$alias]; |
|
| 296 |
$this->importedSymbols[0][$type][$alias] = array('name' => $name, 'node' => $node);
|
|
| 297 |
} |
|
| 298 |
|
|
| 299 |
public function getImportedSymbol($type, $alias) |
|
| 300 |
{
|
|
| 301 |
foreach ($this->importedSymbols as $functions) {
|
|
| 302 |
if (isset($functions[$type][$alias])) {
|
|
| 303 |
return $functions[$type][$alias]; |
|
| 284 | 304 |
} |
| 285 | 305 |
} |
| 286 | 306 |
} |
| 287 | 307 |
|
| 288 | 308 |
public function isMainScope() |
| 289 | 309 |
{
|
| 290 |
return 1 === count($this->importedFunctions);
|
|
| 310 |
return 1 === count($this->importedSymbols);
|
|
| 291 | 311 |
} |
| 292 | 312 |
|
| 293 | 313 |
public function pushLocalScope() |
| 294 | 314 |
{
|
| 295 |
array_unshift($this->importedFunctions, array());
|
|
| 315 |
array_unshift($this->importedSymbols, array());
|
|
| 296 | 316 |
} |
| 297 | 317 |
|
| 298 | 318 |
public function popLocalScope() |
| 299 | 319 |
{
|
| 300 |
array_shift($this->importedFunctions);
|
|
| 320 |
array_shift($this->importedSymbols);
|
|
| 301 | 321 |
} |
| 302 | 322 |
|
| 303 | 323 |
/** |
| ... | ... | |
| 349 | 369 |
(!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface) |
| 350 | 370 |
) {
|
| 351 | 371 |
if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) {
|
| 352 |
throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->stream->getFilename());
|
|
| 372 |
throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->getFilename());
|
|
| 353 | 373 |
} |
| 354 | 374 |
|
| 355 |
throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->stream->getFilename());
|
|
| 375 |
throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->getFilename());
|
|
| 356 | 376 |
} |
| 357 | 377 |
|
| 358 | 378 |
// bypass "set" nodes as they "capture" the output |
Also available in: Unified diff
updated Twig template engine to stable version 1.11.1 step2