PK JL [T\ % lib/Twig/NodeVisitor/SafeAnalysis.phpnu W+A safeVars = $safeVars; } public function getSafe(Twig_Node $node) { $hash = spl_object_hash($node); if (!isset($this->data[$hash])) { return; } foreach ($this->data[$hash] as $bucket) { if ($bucket['key'] !== $node) { continue; } if (in_array('html_attr', $bucket['value'])) { $bucket['value'][] = 'html'; } return $bucket['value']; } } private function setSafe(Twig_Node $node, array $safe) { $hash = spl_object_hash($node); if (isset($this->data[$hash])) { foreach ($this->data[$hash] as &$bucket) { if ($bucket['key'] === $node) { $bucket['value'] = $safe; return; } } } $this->data[$hash][] = array( 'key' => $node, 'value' => $safe, ); } protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { return $node; } protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Expression_Constant) { // constants are marked safe for all $this->setSafe($node, array('all')); } elseif ($node instanceof Twig_Node_Expression_BlockReference) { // blocks are safe by definition $this->setSafe($node, array('all')); } elseif ($node instanceof Twig_Node_Expression_Parent) { // parent block is safe by definition $this->setSafe($node, array('all')); } elseif ($node instanceof Twig_Node_Expression_Conditional) { // intersect safeness of both operands $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); $this->setSafe($node, $safe); } elseif ($node instanceof Twig_Node_Expression_Filter) { // filter expression is safe when the filter is safe $name = $node->getNode('filter')->getAttribute('value'); $args = $node->getNode('arguments'); if (false !== $filter = $env->getFilter($name)) { $safe = $filter->getSafe($args); if (null === $safe) { $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); } $this->setSafe($node, $safe); } else { $this->setSafe($node, array()); } } elseif ($node instanceof Twig_Node_Expression_Function) { // function expression is safe when the function is safe $name = $node->getAttribute('name'); $args = $node->getNode('arguments'); $function = $env->getFunction($name); if (false !== $function) { $this->setSafe($node, $function->getSafe($args)); } else { $this->setSafe($node, array()); } } elseif ($node instanceof Twig_Node_Expression_MethodCall) { if ($node->getAttribute('safe')) { $this->setSafe($node, array('all')); } else { $this->setSafe($node, array()); } } elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) { $name = $node->getNode('node')->getAttribute('name'); if (in_array($name, $this->safeVars)) { $this->setSafe($node, array('all')); } else { $this->setSafe($node, array()); } } else { $this->setSafe($node, array()); } return $node; } private function intersectSafe(array $a = null, array $b = null) { if (null === $a || null === $b) { return array(); } if (in_array('all', $a)) { return $b; } if (in_array('all', $b)) { return $a; } return array_intersect($a, $b); } public function getPriority() { return 0; } } class_alias('Twig_NodeVisitor_SafeAnalysis', 'Twig\NodeVisitor\SafeAnalysisNodeVisitor', false); PK JLM# lib/Twig/NodeVisitor/Sandbox.phpnu W+A */ final class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor { private $inAModule = false; private $tags; private $filters; private $functions; protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { $this->inAModule = true; $this->tags = array(); $this->filters = array(); $this->functions = array(); return $node; } elseif ($this->inAModule) { // look for tags if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) { $this->tags[$node->getNodeTag()] = $node; } // look for filters if ($node instanceof Twig_Node_Expression_Filter && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) { $this->filters[$node->getNode('filter')->getAttribute('value')] = $node; } // look for functions if ($node instanceof Twig_Node_Expression_Function && !isset($this->functions[$node->getAttribute('name')])) { $this->functions[$node->getAttribute('name')] = $node; } // the .. operator is equivalent to the range() function if ($node instanceof Twig_Node_Expression_Binary_Range && !isset($this->functions['range'])) { $this->functions['range'] = $node; } // wrap print to check __toString() calls if ($node instanceof Twig_Node_Print) { return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag()); } } return $node; } protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { $this->inAModule = false; $node->setNode('display_start', new Twig_Node(array(new Twig_Node_CheckSecurity($this->filters, $this->tags, $this->functions), $node->getNode('display_start')))); } return $node; } public function getPriority() { return 0; } } class_alias('Twig_NodeVisitor_Sandbox', 'Twig\NodeVisitor\SandboxNodeVisitor', false); PK JLbh h lib/Twig/NodeVisitor/Escaper.phpnu W+A */ final class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor { private $statusStack = array(); private $blocks = array(); private $safeAnalysis; private $traverser; private $defaultStrategy = false; private $safeVars = array(); public function __construct() { $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis(); } protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { if ($env->hasExtension('Twig_Extension_Escaper') && $defaultStrategy = $env->getExtension('Twig_Extension_Escaper')->getDefaultStrategy($node->getTemplateName())) { $this->defaultStrategy = $defaultStrategy; } $this->safeVars = array(); $this->blocks = array(); } elseif ($node instanceof Twig_Node_AutoEscape) { $this->statusStack[] = $node->getAttribute('value'); } elseif ($node instanceof Twig_Node_Block) { $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env); } elseif ($node instanceof Twig_Node_Import) { $this->safeVars[] = $node->getNode('var')->getAttribute('name'); } return $node; } protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { $this->defaultStrategy = false; $this->safeVars = array(); $this->blocks = array(); } elseif ($node instanceof Twig_Node_Expression_Filter) { return $this->preEscapeFilterNode($node, $env); } elseif ($node instanceof Twig_Node_Print) { return $this->escapePrintNode($node, $env, $this->needEscaping($env)); } if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) { array_pop($this->statusStack); } elseif ($node instanceof Twig_Node_BlockReference) { $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env); } return $node; } private function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type) { if (false === $type) { return $node; } $expression = $node->getNode('expr'); if ($this->isSafeFor($type, $expression, $env)) { return $node; } $class = get_class($node); return new $class( $this->getEscaperFilter($type, $expression), $node->getTemplateLine() ); } private function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env) { $name = $filter->getNode('filter')->getAttribute('value'); $type = $env->getFilter($name)->getPreEscape(); if (null === $type) { return $filter; } $node = $filter->getNode('node'); if ($this->isSafeFor($type, $node, $env)) { return $filter; } $filter->setNode('node', $this->getEscaperFilter($type, $node)); return $filter; } private function isSafeFor($type, Twig_Node $expression, $env) { $safe = $this->safeAnalysis->getSafe($expression); if (null === $safe) { if (null === $this->traverser) { $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis)); } $this->safeAnalysis->setSafeVars($this->safeVars); $this->traverser->traverse($expression); $safe = $this->safeAnalysis->getSafe($expression); } return in_array($type, $safe) || in_array('all', $safe); } private function needEscaping(Twig_Environment $env) { if (count($this->statusStack)) { return $this->statusStack[count($this->statusStack) - 1]; } return $this->defaultStrategy ? $this->defaultStrategy : false; } private function getEscaperFilter($type, Twig_Node $node) { $line = $node->getTemplateLine(); $name = new Twig_Node_Expression_Constant('escape', $line); $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line))); return new Twig_Node_Expression_Filter($node, $name, $args, $line); } public function getPriority() { return 0; } } class_alias('Twig_NodeVisitor_Escaper', 'Twig\NodeVisitor\EscaperNodeVisitor', false); PK JLkeEG " lib/Twig/NodeVisitor/Optimizer.phpnu W+A */ final class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor { const OPTIMIZE_ALL = -1; const OPTIMIZE_NONE = 0; const OPTIMIZE_FOR = 2; const OPTIMIZE_RAW_FILTER = 4; // obsolete, does not do anything const OPTIMIZE_VAR_ACCESS = 8; private $loops = array(); private $loopsTargets = array(); private $optimizers; /** * @param int $optimizers The optimizer mode */ public function __construct($optimizers = -1) { if (!is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) { throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers)); } $this->optimizers = $optimizers; } protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { $this->enterOptimizeFor($node, $env); } return $node; } protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { $this->leaveOptimizeFor($node, $env); } if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) { $node = $this->optimizeRawFilter($node, $env); } $node = $this->optimizePrintNode($node, $env); return $node; } /** * Optimizes print nodes. * * It replaces: * * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()" * * @return Twig_Node */ private function optimizePrintNode(Twig_Node $node, Twig_Environment $env) { if (!$node instanceof Twig_Node_Print) { return $node; } $exprNode = $node->getNode('expr'); if ( $exprNode instanceof Twig_Node_Expression_BlockReference || $exprNode instanceof Twig_Node_Expression_Parent ) { $exprNode->setAttribute('output', true); return $exprNode; } return $node; } /** * Removes "raw" filters. * * @return Twig_Node */ private function optimizeRawFilter(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) { return $node->getNode('node'); } return $node; } /** * Optimizes "for" tag by removing the "loop" variable creation whenever possible. */ private function enterOptimizeFor(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_For) { // disable the loop variable by default $node->setAttribute('with_loop', false); array_unshift($this->loops, $node); array_unshift($this->loopsTargets, $node->getNode('value_target')->getAttribute('name')); array_unshift($this->loopsTargets, $node->getNode('key_target')->getAttribute('name')); } elseif (!$this->loops) { // we are outside a loop return; } // when do we need to add the loop variable back? // the loop variable is referenced for the current loop elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) { $node->setAttribute('always_defined', true); $this->addLoopToCurrent(); } // optimize access to loop targets elseif ($node instanceof Twig_Node_Expression_Name && in_array($node->getAttribute('name'), $this->loopsTargets)) { $node->setAttribute('always_defined', true); } // block reference elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) { $this->addLoopToCurrent(); } // include without the only attribute elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) { $this->addLoopToAll(); } // include function without the with_context=false parameter elseif ($node instanceof Twig_Node_Expression_Function && 'include' === $node->getAttribute('name') && (!$node->getNode('arguments')->hasNode('with_context') || false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value') ) ) { $this->addLoopToAll(); } // the loop variable is referenced via an attribute elseif ($node instanceof Twig_Node_Expression_GetAttr && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant || 'parent' === $node->getNode('attribute')->getAttribute('value') ) && (true === $this->loops[0]->getAttribute('with_loop') || ($node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' === $node->getNode('node')->getAttribute('name') ) ) ) { $this->addLoopToAll(); } } /** * Optimizes "for" tag by removing the "loop" variable creation whenever possible. */ private function leaveOptimizeFor(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_For) { array_shift($this->loops); array_shift($this->loopsTargets); array_shift($this->loopsTargets); } } private function addLoopToCurrent() { $this->loops[0]->setAttribute('with_loop', true); } private function addLoopToAll() { foreach ($this->loops as $loop) { $loop->setAttribute('with_loop', true); } } public function getPriority() { return 255; } } class_alias('Twig_NodeVisitor_Optimizer', 'Twig\NodeVisitor\OptimizerNodeVisitor', false); PK JL8 c4 c4 lib/Twig/ExtensionSet.phpnu W+A * * @internal */ final class Twig_ExtensionSet { private $extensions; private $initialized = false; private $runtimeInitialized = false; private $staging; private $parsers; private $visitors; private $filters; private $tests; private $functions; private $unaryOperators; private $binaryOperators; private $globals; private $functionCallbacks = array(); private $filterCallbacks = array(); private $lastModified = 0; public function __construct() { $this->staging = new Twig_Extension_Staging(); } /** * Initializes the runtime environment. */ public function initRuntime(Twig_Environment $env) { if ($this->runtimeInitialized) { return; } $this->runtimeInitialized = true; foreach ($this->extensions as $extension) { if ($extension instanceof Twig_Extension_InitRuntimeInterface) { $extension->initRuntime($env); } } } /** * Returns true if the given extension is registered. * * @param string $class The extension class name * * @return bool Whether the extension is registered or not */ public function hasExtension($class) { $class = ltrim($class, '\\'); if (!isset($this->extensions[$class]) && class_exists($class, false)) { // For BC/FC with namespaced aliases $class = (new ReflectionClass($class))->name; } return isset($this->extensions[$class]); } /** * Gets an extension by class name. * * @param string $class The extension class name * * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance */ public function getExtension($class) { $class = ltrim($class, '\\'); if (!isset($this->extensions[$class]) && class_exists($class, false)) { // For BC/FC with namespaced aliases $class = (new ReflectionClass($class))->name; } if (!isset($this->extensions[$class])) { throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $class)); } return $this->extensions[$class]; } /** * Registers an array of extensions. * * @param array $extensions An array of extensions */ public function setExtensions(array $extensions) { foreach ($extensions as $extension) { $this->addExtension($extension); } } /** * Returns all registered extensions. * * @return array An array of extensions */ public function getExtensions() { return $this->extensions; } public function getSignature() { return json_encode(array_keys($this->extensions)); } public function isInitialized() { return $this->initialized || $this->runtimeInitialized; } public function getLastModified() { if (0 !== $this->lastModified) { return $this->lastModified; } foreach ($this->extensions as $extension) { $r = new ReflectionObject($extension); if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModified) { $this->lastModified = $extensionTime; } } return $this->lastModified; } /** * Registers an extension. * * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance */ public function addExtension(Twig_ExtensionInterface $extension) { $class = get_class($extension); if ($this->initialized) { throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class)); } if (isset($this->extensions[$class])) { throw new LogicException(sprintf('Unable to register extension "%s" as it is already registered.', $class)); } // For BC/FC with namespaced aliases $class = (new ReflectionClass($class))->name; $this->extensions[$class] = $extension; } public function addFunction(Twig_Function $function) { if ($this->initialized) { throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName())); } $this->staging->addFunction($function); } public function getFunctions() { if (!$this->initialized) { $this->initExtensions(); } return $this->functions; } /** * Get a function by name. * * @param string $name function name * * @return Twig_Function|false A Twig_Function instance or false if the function does not exist */ public function getFunction($name) { if (!$this->initialized) { $this->initExtensions(); } if (isset($this->functions[$name])) { return $this->functions[$name]; } foreach ($this->functions as $pattern => $function) { $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) { array_shift($matches); $function->setArguments($matches); return $function; } } foreach ($this->functionCallbacks as $callback) { if (false !== $function = $callback($name)) { return $function; } } return false; } public function registerUndefinedFunctionCallback(callable $callable) { $this->functionCallbacks[] = $callable; } public function addFilter(Twig_Filter $filter) { if ($this->initialized) { throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $filter->getName())); } $this->staging->addFilter($filter); } public function getFilters() { if (!$this->initialized) { $this->initExtensions(); } return $this->filters; } /** * Get a filter by name. * * Subclasses may override this method and load filters differently; * so no list of filters is available. * * @param string $name The filter name * * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist */ public function getFilter($name) { if (!$this->initialized) { $this->initExtensions(); } if (isset($this->filters[$name])) { return $this->filters[$name]; } foreach ($this->filters as $pattern => $filter) { $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) { array_shift($matches); $filter->setArguments($matches); return $filter; } } foreach ($this->filterCallbacks as $callback) { if (false !== $filter = $callback($name)) { return $filter; } } return false; } public function registerUndefinedFilterCallback(callable $callable) { $this->filterCallbacks[] = $callable; } public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) { if ($this->initialized) { throw new LogicException('Unable to add a node visitor as extensions have already been initialized.'); } $this->staging->addNodeVisitor($visitor); } public function getNodeVisitors() { if (!$this->initialized) { $this->initExtensions(); } return $this->visitors; } public function addTokenParser(Twig_TokenParserInterface $parser) { if ($this->initialized) { throw new LogicException('Unable to add a token parser as extensions have already been initialized.'); } $this->staging->addTokenParser($parser); } public function getTokenParsers() { if (!$this->initialized) { $this->initExtensions(); } return $this->parsers; } public function getGlobals() { if (null !== $this->globals) { return $this->globals; } $globals = array(); foreach ($this->extensions as $extension) { if (!$extension instanceof Twig_Extension_GlobalsInterface) { continue; } $extGlobals = $extension->getGlobals(); if (!is_array($extGlobals)) { throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension))); } $globals = array_merge($globals, $extGlobals); } if ($this->initialized) { $this->globals = $globals; } return $globals; } public function addTest(Twig_Test $test) { if ($this->initialized) { throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName())); } $this->staging->addTest($test); } public function getTests() { if (!$this->initialized) { $this->initExtensions(); } return $this->tests; } /** * Gets a test by name. * * @param string $name The test name * * @return Twig_Test|false A Twig_Test instance or false if the test does not exist */ public function getTest($name) { if (!$this->initialized) { $this->initExtensions(); } if (isset($this->tests[$name])) { return $this->tests[$name]; } return false; } /** * Gets the registered unary Operators. * * @return array An array of unary operators */ public function getUnaryOperators() { if (!$this->initialized) { $this->initExtensions(); } return $this->unaryOperators; } /** * Gets the registered binary Operators. * * @return array An array of binary operators */ public function getBinaryOperators() { if (!$this->initialized) { $this->initExtensions(); } return $this->binaryOperators; } private function initExtensions() { $this->parsers = array(); $this->filters = array(); $this->functions = array(); $this->tests = array(); $this->visitors = array(); $this->unaryOperators = array(); $this->binaryOperators = array(); foreach ($this->extensions as $extension) { $this->initExtension($extension); } $this->initExtension($this->staging); // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception $this->initialized = true; } private function initExtension(Twig_ExtensionInterface $extension) { // filters foreach ($extension->getFilters() as $filter) { $this->filters[$filter->getName()] = $filter; } // functions foreach ($extension->getFunctions() as $function) { $this->functions[$function->getName()] = $function; } // tests foreach ($extension->getTests() as $test) { $this->tests[$test->getName()] = $test; } // token parsers foreach ($extension->getTokenParsers() as $parser) { if (!$parser instanceof Twig_TokenParserInterface) { throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface.'); } $this->parsers[] = $parser; } // node visitors foreach ($extension->getNodeVisitors() as $visitor) { $this->visitors[] = $visitor; } // operators if ($operators = $extension->getOperators()) { if (!is_array($operators)) { throw new InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', get_class($extension), is_object($operators) ? get_class($operators) : gettype($operators).(is_resource($operators) ? '' : '#'.$operators))); } if (2 !== count($operators)) { throw new InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', get_class($extension), count($operators))); } $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); } } } class_alias('Twig_ExtensionSet', 'Twig\ExtensionSet', false); PK JLsJ1 1 lib/Twig/Parser.phpnu W+A */ class Twig_Parser { private $stack = array(); private $stream; private $parent; private $handlers; private $visitors; private $expressionParser; private $blocks; private $blockStack; private $macros; private $env; private $importedSymbols; private $traits; private $embeddedTemplates = array(); private $varNameSalt = 0; public function __construct(Twig_Environment $env) { $this->env = $env; } public function getVarName() { return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->stream->getSourceContext()->getCode().$this->varNameSalt++)); } public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false) { $vars = get_object_vars($this); unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']); $this->stack[] = $vars; // tag handlers if (null === $this->handlers) { $this->handlers = array(); foreach ($this->env->getTokenParsers() as $handler) { $handler->setParser($this); $this->handlers[$handler->getTag()] = $handler; } } // node visitors if (null === $this->visitors) { $this->visitors = $this->env->getNodeVisitors(); } if (null === $this->expressionParser) { $this->expressionParser = new Twig_ExpressionParser($this, $this->env); } $this->stream = $stream; $this->parent = null; $this->blocks = array(); $this->macros = array(); $this->traits = array(); $this->blockStack = array(); $this->importedSymbols = array(array()); $this->embeddedTemplates = array(); $this->varNameSalt = 0; try { $body = $this->subparse($test, $dropNeedle); if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) { $body = new Twig_Node(); } } catch (Twig_Error_Syntax $e) { if (!$e->getSourceContext()) { $e->setSourceContext($this->stream->getSourceContext()); } if (!$e->getTemplateLine()) { $e->setTemplateLine($this->stream->getCurrent()->getLine()); } throw $e; } $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, $stream->getSourceContext()); $traverser = new Twig_NodeTraverser($this->env, $this->visitors); $node = $traverser->traverse($node); // restore previous stack so previous parse() call can resume working foreach (array_pop($this->stack) as $key => $val) { $this->$key = $val; } return $node; } public function subparse($test, $dropNeedle = false) { $lineno = $this->getCurrentToken()->getLine(); $rv = array(); while (!$this->stream->isEOF()) { switch ($this->getCurrentToken()->getType()) { case /* Twig_Token::TEXT_TYPE */ 0: $token = $this->stream->next(); $rv[] = new Twig_Node_Text($token->getValue(), $token->getLine()); break; case /* Twig_Token::VAR_START_TYPE */ 2: $token = $this->stream->next(); $expr = $this->expressionParser->parseExpression(); $this->stream->expect(/* Twig_Token::VAR_END_TYPE */ 4); $rv[] = new Twig_Node_Print($expr, $token->getLine()); break; case /* Twig_Token::BLOCK_START_TYPE */ 1: $this->stream->next(); $token = $this->getCurrentToken(); if (/* Twig_Token::NAME_TYPE */ 5 !== $token->getType()) { throw new Twig_Error_Syntax('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext()); } if (null !== $test && $test($token)) { if ($dropNeedle) { $this->stream->next(); } if (1 === count($rv)) { return $rv[0]; } return new Twig_Node($rv, array(), $lineno); } if (!isset($this->handlers[$token->getValue()])) { if (null !== $test) { $e = new Twig_Error_Syntax(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) { $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno)); } } else { $e = new Twig_Error_Syntax(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); $e->addSuggestions($token->getValue(), array_keys($this->env->getTags())); } throw $e; } $this->stream->next(); $subparser = $this->handlers[$token->getValue()]; $node = $subparser->parse($token); if (null !== $node) { $rv[] = $node; } break; default: throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext()); } } if (1 === count($rv)) { return $rv[0]; } return new Twig_Node($rv, array(), $lineno); } public function getBlockStack() { return $this->blockStack; } public function peekBlockStack() { return $this->blockStack[count($this->blockStack) - 1]; } public function popBlockStack() { array_pop($this->blockStack); } public function pushBlockStack($name) { $this->blockStack[] = $name; } public function hasBlock($name) { return isset($this->blocks[$name]); } public function getBlock($name) { return $this->blocks[$name]; } public function setBlock($name, Twig_Node_Block $value) { $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getTemplateLine()); } public function hasMacro($name) { return isset($this->macros[$name]); } public function setMacro($name, Twig_Node_Macro $node) { $this->macros[$name] = $node; } public function isReservedMacroName($name) { return false; } public function addTrait($trait) { $this->traits[] = $trait; } public function hasTraits() { return count($this->traits) > 0; } public function embedTemplate(Twig_Node_Module $template) { $template->setIndex(mt_rand()); $this->embeddedTemplates[] = $template; } public function addImportedSymbol($type, $alias, $name = null, Twig_Node_Expression $node = null) { $this->importedSymbols[0][$type][$alias] = array('name' => $name, 'node' => $node); } public function getImportedSymbol($type, $alias) { foreach ($this->importedSymbols as $functions) { if (isset($functions[$type][$alias])) { return $functions[$type][$alias]; } } } public function isMainScope() { return 1 === count($this->importedSymbols); } public function pushLocalScope() { array_unshift($this->importedSymbols, array()); } public function popLocalScope() { array_shift($this->importedSymbols); } /** * @return Twig_ExpressionParser */ public function getExpressionParser() { return $this->expressionParser; } public function getParent() { return $this->parent; } public function setParent($parent) { $this->parent = $parent; } /** * @return Twig_TokenStream */ public function getStream() { return $this->stream; } /** * @return Twig_Token */ public function getCurrentToken() { return $this->stream->getCurrent(); } private function filterBodyNodes(Twig_Node $node, $nested = false) { // check that the body does not contain non-empty output nodes if ( ($node instanceof Twig_Node_Text && !ctype_space($node->getAttribute('data'))) || // the "&& !$node instanceof Twig_Node_Spaceless" part of the condition must be removed in 3.0 (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && ($node instanceof Twig_NodeOutputInterface && !$node instanceof Twig_Node_Spaceless)) ) { if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) { throw new Twig_Error_Syntax('A template that extends another one cannot start with a byte order mark (BOM); it must be removed.', $node->getTemplateLine(), $this->stream->getSourceContext()); } throw new Twig_Error_Syntax('A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag?', $node->getTemplateLine(), $this->stream->getSourceContext()); } // bypass nodes that "capture" the output if ($node instanceof Twig_NodeCaptureInterface) { // a "block" tag in such a node will serve as a block definition AND be displayed in place as well return $node; } // to be removed completely in Twig 3.0 if (!$nested && $node instanceof Twig_Node_Spaceless) { @trigger_error(sprintf('Using the spaceless tag at the root level of a child template in "%s" at line %d is deprecated since version 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), E_USER_DEPRECATED); } // "block" tags that are not captured (see above) are only used for defining // the content of the block. In such a case, nesting it does not work as // expected as the definition is not part of the default template code flow. if ($nested && $node instanceof Twig_Node_BlockReference) { //throw new Twig_Error_Syntax('A block definition cannot be nested under non-capturing nodes.', $node->getTemplateLine(), $this->stream->getSourceContext()); @trigger_error(sprintf('Nesting a block definition under a non-capturing node in "%s" at line %d is deprecated since version 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), E_USER_DEPRECATED); return; } // the "&& !$node instanceof Twig_Node_Spaceless" part of the condition must be removed in 3.0 if ($node instanceof Twig_NodeOutputInterface && !$node instanceof Twig_Node_Spaceless) { return; } // here, $nested means "being at the root level of a child template" // we need to discard the wrapping "Twig_Node" for the "body" node $nested = $nested || get_class($node) !== 'Twig_Node'; foreach ($node as $k => $n) { if (null !== $n && null === $this->filterBodyNodes($n, $nested)) { $node->removeNode($k); } } return $node; } } class_alias('Twig_Parser', 'Twig\Parser', false); class_exists('Twig_Node'); class_exists('Twig_TokenStream'); PK JLW lib/Twig/Node.phpnu W+A */ class Twig_Node implements Countable, IteratorAggregate { protected $nodes; protected $attributes; protected $lineno; protected $tag; private $name; /** * Constructor. * * The nodes are automatically made available as properties ($this->node). * The attributes are automatically made available as array items ($this['name']). * * @param array $nodes An array of named nodes * @param array $attributes An array of attributes (should not be nodes) * @param int $lineno The line number * @param string $tag The tag name associated with the Node */ public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null) { foreach ($nodes as $name => $node) { if (!$node instanceof self) { throw new InvalidArgumentException(sprintf('Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a Twig_Node instance.', is_object($node) ? get_class($node) : null === $node ? 'null' : gettype($node), $name, get_class($this))); } } $this->nodes = $nodes; $this->attributes = $attributes; $this->lineno = $lineno; $this->tag = $tag; } public function __toString() { $attributes = array(); foreach ($this->attributes as $name => $value) { $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true))); } $repr = array(get_class($this).'('.implode(', ', $attributes)); if (count($this->nodes)) { foreach ($this->nodes as $name => $node) { $len = strlen($name) + 4; $noderepr = array(); foreach (explode("\n", (string) $node) as $line) { $noderepr[] = str_repeat(' ', $len).$line; } $repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr))); } $repr[] = ')'; } else { $repr[0] .= ')'; } return implode("\n", $repr); } public function compile(Twig_Compiler $compiler) { foreach ($this->nodes as $node) { $node->compile($compiler); } } public function getTemplateLine() { return $this->lineno; } public function getNodeTag() { return $this->tag; } /** * @return bool */ public function hasAttribute($name) { return array_key_exists($name, $this->attributes); } /** * @return mixed */ public function getAttribute($name) { if (!array_key_exists($name, $this->attributes)) { throw new LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this))); } return $this->attributes[$name]; } /** * @param string $name * @param mixed $value */ public function setAttribute($name, $value) { $this->attributes[$name] = $value; } public function removeAttribute($name) { unset($this->attributes[$name]); } /** * @return bool */ public function hasNode($name) { return isset($this->nodes[$name]); } /** * @return Twig_Node */ public function getNode($name) { if (!isset($this->nodes[$name])) { throw new LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this))); } return $this->nodes[$name]; } public function setNode($name, self $node) { $this->nodes[$name] = $node; } public function removeNode($name) { unset($this->nodes[$name]); } public function count() { return count($this->nodes); } public function getIterator() { return new ArrayIterator($this->nodes); } public function setTemplateName($name) { $this->name = $name; foreach ($this->nodes as $node) { $node->setTemplateName($name); } } public function getTemplateName() { return $this->name; } } class_alias('Twig_Node', 'Twig\Node\Node', false); class_exists('Twig_Compiler'); PK JLN N lib/Twig/Markup.phpnu W+A */ class Twig_Markup implements Countable, JsonSerializable { private $content; private $charset; public function __construct($content, $charset) { $this->content = (string) $content; $this->charset = $charset; } public function __toString() { return $this->content; } public function count() { return mb_strlen($this->content, $this->charset); } public function jsonSerialize() { return $this->content; } } class_alias('Twig_Markup', 'Twig\Markup', false); PK JL.f lib/Twig/Source.phpnu W+A */ final class Twig_Source { private $code; private $name; private $path; /** * @param string $code The template source code * @param string $name The template logical name * @param string $path The filesystem path of the template if any */ public function __construct($code, $name, $path = '') { $this->code = $code; $this->name = $name; $this->path = $path; } public function getCode() { return $this->code; } public function getName() { return $this->name; } public function getPath() { return $this->path; } } class_alias('Twig_Source', 'Twig\Source', false); PK JLܕC C lib/Twig/TokenParser/Import.phpnu W+A * {% import 'forms.html' as forms %} * */ final class Twig_TokenParser_Import extends Twig_TokenParser { public function parse(Twig_Token $token) { $macro = $this->parser->getExpressionParser()->parseExpression(); $this->parser->getStream()->expect('as'); $var = new Twig_Node_Expression_AssignName($this->parser->getStream()->expect(/* Twig_Token::NAME_TYPE */ 5)->getValue(), $token->getLine()); $this->parser->getStream()->expect(/* Twig_Token::BLOCK_END_TYPE */ 3); $this->parser->addImportedSymbol('template', $var->getAttribute('name')); return new Twig_Node_Import($macro, $var, $token->getLine(), $this->getTag()); } public function getTag() { return 'import'; } } class_alias('Twig_TokenParser_Import', 'Twig\TokenParser\ImportTokenParser', false); PK JLjW lib/Twig/TokenParser/For.phpnu W+A *