/** * @param \Twig_TokenStream $tokens * @param integer $position * @param message $target * @param boolean $acceptNewLines */ protected function assertSpacing(\Twig_TokenStream $tokens, $position, $spacing, $acceptNewLines = true) { $current = $tokens->getCurrent(); $token = $tokens->look($position); $orientation = round($position / abs($position)); $positionName = $orientation > 0 ? 'after' : 'before'; if ($this->whitelist && !$this->whitelist->pass($tokens, $orientation)) { return; } if ($acceptNewLines && $token->getType() == Token::NEWLINE_TYPE) { return; } // special case of no spaces allowed. if ($spacing === 0) { if ($token->getType() === Token::WHITESPACE_TYPE) { $this->addViolation($tokens->getFilename(), $current->getLine(), $current->getColumn(), sprintf('There should be no space %s "%s".', $positionName, $current->getValue())); } if ($token->getType() === Token::NEWLINE_TYPE) { $this->addViolation($tokens->getFilename(), $current->getLine(), $current->getColumn(), sprintf('There should be no new line %s "%s".', $positionName, $current->getValue())); } return; } if ($token->getType() !== Token::WHITESPACE_TYPE || strlen($token->getValue()) < $spacing) { $this->addViolation($tokens->getFilename(), $current->getLine(), $current->getColumn(), sprintf('There should be %d space(s) %s "%s".', $spacing, $positionName, $current->getValue())); } if ($token->getType() === Token::WHITESPACE_TYPE && strlen($token->getValue()) > $spacing) { $this->addViolation($tokens->getFilename(), $current->getLine(), $current->getColumn(), sprintf('More than %d space(s) found %s "%s".', $spacing, $positionName, $current->getValue())); } }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->violations = []; $arrayDepth = 0; $skip = false; while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getType() === \Twig_Token::PUNCTUATION_TYPE && $token->getValue() === '(') { $skip = true; // Ignore function arguments or embedded expressions (eg. [ func(1, 2) ] ) // This prevents this rule from having influence on arguments spacing. } if ($token->getType() === \Twig_Token::PUNCTUATION_TYPE && $token->getValue() === '?') { $skip = true; } if ($token->getValue() === '[' && $token->getType() === \Twig_Token::PUNCTUATION_TYPE) { if ($tokens->look(-1)->getType() === \Twig_Token::NAME_TYPE) { break; // This is not an array declaration, but an array access ( eg. : foo[1] ) } $arrayDepth++; $skip = false; // We entered a new array or hash, from now on do not skip anything. } if ($token->getValue() === ']' && $token->getType() === \Twig_Token::PUNCTUATION_TYPE) { $arrayDepth--; } if (!$skip && $arrayDepth > 0 && $token->getType() === \Twig_Token::PUNCTUATION_TYPE && $token->getValue() === ',') { $this->assertSpacing($tokens, Lexer::NEXT_TOKEN, $this->spaceAfter); $this->assertSpacing($tokens, Lexer::PREVIOUS_TOKEN, $this->spaceBefore, false); } $tokens->next(); } return $this->violations; }
/** * @param \Twig_TokenStream $tokens * * @return \Twig_Token */ protected function seekTernaryElse(\Twig_TokenStream $tokens) { $i = 1; $depth = 0; $found = false; $token = null; while ($depth || !$found) { $token = $tokens->look($i); if ($token->getType() === \Twig_Token::VAR_END_TYPE || $token->getType() === \Twig_Token::INTERPOLATION_END_TYPE) { return; } // End of hash value means end of short ternary (eg. "foo ? bar" syntax) if ($token->getType() === \Twig_Token::PUNCTUATION_TYPE && $token->getValue() === ',') { return; } if ($token->getType() === \Twig_Token::PUNCTUATION_TYPE && in_array($token->getValue(), ['(', '[', '{'])) { $depth++; } if ($depth && $token->getType() === \Twig_Token::PUNCTUATION_TYPE && in_array($token->getValue(), [')', ']', '}'])) { $depth--; } $found = $token->getType() === \Twig_Token::PUNCTUATION_TYPE && $token->getValue() === ':'; $i++; } return $token; }
/** * @throws \Twig_Error_Syntax * @param $filename * @param \Twig_TokenStream $stream */ private function getAssetFromStream($filename, \Twig_TokenStream $stream) { $this->expect($filename, $stream->next(), \Twig_Token::PUNCTUATION_TYPE, '('); $token = $stream->next(); $this->expect($filename, $token, \Twig_Token::STRING_TYPE); $this->expect($filename, $stream->next(), \Twig_Token::PUNCTUATION_TYPE, ')'); return $token->getValue(); }
/** * @expectedException Twig_Error_Syntax * @expectedMessage Unexpected end of template */ public function testEndOfTemplateLook() { $stream = new Twig_TokenStream(array(new Twig_Token(Twig_Token::BLOCK_START_TYPE, 1, 1))); while (!$stream->isEOF()) { $stream->look(); $stream->next(); } }
public function testNext() { $stream = new Twig_TokenStream(self::$tokens); $repr = array(); while (!$stream->isEOF()) { $token = $stream->next(); $repr[] = $token->getValue(); } $this->assertEquals('1, 2, 3, 4, 5, 6, 7', implode(', ', $repr), '->next() advances the pointer and returns the current token'); }
/** * @param \Twig_TokenStream $tokens * @param integer $position * @param message $target */ private function assertSpacing(\Twig_TokenStream $tokens, $position, $target) { $token = $tokens->look($position); if ($token->getType() !== Token::WHITESPACE_TYPE || strlen($token->getValue()) < $this->spacing) { $this->addViolation($tokens->getFilename(), $token->getLine(), $token->getColumn(), sprintf('There should be %d space(s) %s.', $this->spacing, $target)); } if ($token->getType() === Token::WHITESPACE_TYPE && strlen($token->getValue()) > $this->spacing) { $this->addViolation($tokens->getFilename(), $token->getLine(), $token->getColumn(), sprintf('More than %d space(s) found %s.', $this->spacing, $target)); } }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->violations = []; while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getType() === \Twig_Token::PUNCTUATION_TYPE && in_array($token->getValue(), $this->punctuations)) { $this->assertSpacing($tokens, Lexer::PREVIOUS_TOKEN, $this->spacing); $this->assertSpacing($tokens, Lexer::NEXT_TOKEN, $this->spacing); } $tokens->next(); } return $this->violations; }
/** * {@inheritdoc} */ public function pass(\Twig_TokenStream $tokens, $orientation) { foreach ($this->offsets as $offset) { $token = $tokens->look($offset * $orientation); if (in_array($token->getValue(), $this->tokens)) { return true; } if (in_array($token->getType(), $this->tokens)) { return true; } } return false; }
/** * @param \Twig_TokenStream $tokens * @param integer $skip * * @return null|Token */ protected function getPreviousSignicantToken(\Twig_TokenStream $tokens, $skip = 0) { $i = 1; $token = null; while ($token = $tokens->look(-$i)) { if (!in_array($token->getType(), [Token::WHITESPACE_TYPE, Token::NEWLINE_TYPE])) { if ($skip === 0) { return $token; } $skip--; } $i++; } return null; }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->violations = []; while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getType() === \Twig_Token::OPERATOR_TYPE && in_array($token->getValue(), $this->operators)) { // allows unary operators to be next to an opening parenthesis. if (!($this->isUnary($token->getValue()) && $tokens->look(-1)->getValue() == '(')) { $this->assertSpacing($tokens, Lexer::PREVIOUS_TOKEN, $this->spacing); } $this->assertSpacing($tokens, Lexer::NEXT_TOKEN, $this->spacing); } $tokens->next(); } return $this->violations; }
private function parseInline(\Twig_TokenStream $stream, $lineno) { $stream->expect(\Twig_Token::BLOCK_END_TYPE); $this->parser->subparse(function ($token) { return $token->test(['end' . $this->getTag()]); }, true); $stream->expect(\Twig_Token::BLOCK_END_TYPE); $file = $this->parser->getEnvironment()->getLoader()->getCacheKey($stream->getFilename()); if (!isset($this->inline_blocks[$file])) { $this->inline_blocks[$file] = 0; } $file_name = md5($file . $this->inline_blocks[$file]) . ".js"; $assets = $this->extension->webpackAsset('cache.' . $file_name); $this->inline_blocks[$file]++; return new WebpackInlineNode(['js_file' => $assets['js'], 'css_file' => $assets['css']], $lineno, $this->getTag()); }
/** * Twig_TokenStreamからTokenを自動で解析する * * @access private * @param Twig_TokenStream * @return Array */ private function _autoParse(Twig_TokenStream $s) { $return = array(); while (!$s->test(Twig_Token::BLOCK_END_TYPE)) { if ($s->test(Twig_Token::OPERATOR_TYPE)) { $params = array(); // 自動的に配列Expressionを取得 $expressions = $this->parser->getExpressionParser()->parseExpression(); //$params = $this->_parseExpression($expressions); $return[] = $expressions; } else { $return[] = $this->_getNode($s); } } return $return; }
/** * Método que revisa cada l�nea de la plantilla * @param \Twig_TokenStream $stream * @return \Twig_TokenStream */ protected function checkTemplateLine(\Twig_TokenStream $stream) { $value = $stream->getCurrent(); switch ($value->getType()) { case \Twig_Token::STRING_TYPE: $this->values[] = $this->parser->getExpressionParser()->parseExpression(); break; case \Twig_Token::BLOCK_END_TYPE: $this->end = true; $stream->next(); break; default: $stream->next(); break; } return $stream; }
/** * {@inheritdoc} */ public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false) { // push all variables into the stack to keep the current state of the parser // using get_object_vars() instead of foreach would lead to https://bugs.php.net/71336 $vars = array(); foreach ($this as $k => $v) { $vars[$k] = $v; } 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->getUnaryOperators(), $this->env->getBinaryOperators()); } $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(); 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->getTemplateFile()) { $e->setTemplateFile($this->getFilename()); } 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, $this->getFilename(), $stream->getSource()); $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; }
protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node) { if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) { $attribute = $node->getNode('attribute'); if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) { throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition', $attribute->getAttribute('value')), $node->getLine(), $stream->getFilename()); } } // should check for parent.loop.XXX usage if ($node instanceof Twig_Node_For) { return; } foreach ($node as $n) { if (!$n) { continue; } $this->checkLoopUsageBody($stream, $n); } }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->violations = []; $sliceOpened = false; while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getValue() === '[' && $tokens->look(-1)->getType() === \Twig_Token::NAME_TYPE) { $sliceOpened = true; } if ($sliceOpened > 0 && $token->getValue() === ':') { $this->assertSpacing($tokens, Lexer::NEXT_TOKEN, $this->spaces); $this->assertSpacing($tokens, Lexer::PREVIOUS_TOKEN, $this->spaces, false); } if ($token->getValue() === ']') { $sliceOpened = false; } $tokens->next(); } return $this->violations; }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->violations = []; while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getValue() === '(' && $token->getType() === \Twig_Token::PUNCTUATION_TYPE) { $this->assertSpacing($tokens, Lexer::NEXT_TOKEN, $this->spacing); // Space allowed if previous token is not a function name. // Space also allowed in case of control structure if ($tokens->look(-2)->getType() === \Twig_Token::NAME_TYPE) { $value = $tokens->look(-2)->getValue(); $spacing = in_array($value, ['if', 'elseif', 'in']) ? $this->controlStructureSpacing : $this->spacing; $this->assertSpacing($tokens, Lexer::PREVIOUS_TOKEN, $spacing); } } if ($token->getValue() === ')' && $token->getType() === \Twig_Token::PUNCTUATION_TYPE && $tokens->look(Lexer::PREVIOUS_TOKEN)->getType() === Token::WHITESPACE_TYPE) { $this->assertSpacing($tokens, Lexer::PREVIOUS_TOKEN, $this->spacing); } $tokens->next(); } return $this->violations; }
public function testRewind() { $stream = new Twig_TokenStream(self::$tokens, '', false); $this->assertEquals(2, $stream->look()->getValue(), '->look() returns the next token'); $this->assertEquals(3, $stream->look()->getValue(), '->look() can be called several times to look more than one upcoming token'); $this->assertEquals(4, $stream->look()->getValue(), '->look() can be called several times to look more than one upcoming token'); $this->assertEquals(5, $stream->look()->getValue(), '->look() can be called several times to look more than one upcoming token'); $stream->rewind(); $repr = array(); while (!$stream->isEOF()) { $token = $stream->next(false); $repr[] = $token->getValue(); } $this->assertEquals('1, 2, 3, 4, 5, 6, 7', implode(', ', $repr), '->rewind() pushes all pushed tokens to the token array'); }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->reset(); while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getType() === \Twig_Token::NAME_TYPE && preg_match('/[A-Z]/', $token->getValue())) { if ($tokens->look(Lexer::PREVIOUS_TOKEN)->getType() === Token::WHITESPACE_TYPE && $tokens->look(-2)->getValue() === 'set') { $this->addViolation($tokens->getFilename(), $token->getLine(), $token->getColumn(), sprintf('The "%s" variable should be in lower case (use _ as a separator).', $token->getValue())); } } $tokens->next(); } return $this->violations; }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->reset(); while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getType() === Token::NEWLINE_TYPE && $tokens->look(-1)->getType() === Token::WHITESPACE_TYPE || $token->getType() === Token::TEXT_TYPE) { if (preg_match("/[[:blank:]]+\n/", $token->getValue())) { $this->addViolation($tokens->getFilename(), $token->getLine(), $token->getColumn(), 'A line should not end with blank space(s).'); } } $tokens->next(); } return $this->violations; }
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->reset(); $variables = []; while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getType() === \Twig_Token::NAME_TYPE) { if ($tokens->look(Lexer::PREVIOUS_TOKEN)->getType() === Token::WHITESPACE_TYPE && $tokens->look(-2)->getValue() === 'set') { $variables[$token->getValue()] = $token; } else { unset($variables[$token->getValue()]); } } $tokens->next(); } foreach ($variables as $name => $originalToken) { $this->addViolation($tokens->getFilename(), $originalToken->getLine(), $originalToken->getColumn(), sprintf('Unused variable "%s".', $name)); } return $this->violations; }
/** * Get string value from stream * * @param \Twig_TokenStream $stream * @return string */ protected function parseStringValue(\Twig_TokenStream $stream) { $stream->next(); $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); return $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); }
$repr = array(); while (!$stream->isEOF()) { $token = $stream->next(); $repr[] = $token->getValue(); } $t->is(implode(', ', $repr), '1, 2, 3, 4, 5, 6, 7', '->look() pushes the token to the stack'); $stream = new Twig_TokenStream($tokens, '', false); $t->is($stream->look()->getValue(), 2, '->look() returns the next token'); $t->is($stream->look()->getValue(), 3, '->look() can be called several times to look more than one upcoming token'); $t->is($stream->look()->getValue(), 4, '->look() can be called several times to look more than one upcoming token'); $t->is($stream->look()->getValue(), 5, '->look() can be called several times to look more than one upcoming token'); $repr = array(); while (!$stream->isEOF()) { $token = $stream->next(); $repr[] = $token->getValue(); } $t->is(implode(', ', $repr), '1, 2, 3, 4, 5, 6, 7', '->look() pushes the token to the stack'); // ->rewind() $t->diag('->rewind()'); $stream = new Twig_TokenStream($tokens, '', false); $t->is($stream->look()->getValue(), 2, '->look() returns the next token'); $t->is($stream->look()->getValue(), 3, '->look() can be called several times to look more than one upcoming token'); $t->is($stream->look()->getValue(), 4, '->look() can be called several times to look more than one upcoming token'); $t->is($stream->look()->getValue(), 5, '->look() can be called several times to look more than one upcoming token'); $stream->rewind(); $repr = array(); while (!$stream->isEOF()) { $token = $stream->next(false); $repr[] = $token->getValue(); } $t->is(implode(', ', $repr), '1, 2, 3, 4, 5, 6, 7', '->rewind() pushes all pushed tokens to the token array');
/** * {@inheritdoc} */ public function check(\Twig_TokenStream $tokens) { $this->reset(); $macros = []; while (!$tokens->isEOF()) { $token = $tokens->getCurrent(); if ($token->getType() === \Twig_Token::NAME_TYPE && $token->getValue() === 'import') { while ($tokens->getCurrent()->getValue() !== 'as') { $tokens->next(); } $tokens->next(); while (in_array($tokens->getCurrent()->getType(), [\Twig_Token::NAME_TYPE, \Twig_Token::PUNCTUATION_TYPE, Token::WHITESPACE_TYPE])) { $next = $tokens->getCurrent(); if ($next->getType() === \Twig_Token::NAME_TYPE) { $macros[$next->getValue()] = $next; } $tokens->next(); } } elseif ($token->getType() === \Twig_Token::NAME_TYPE && array_key_exists($token->getValue(), $macros)) { unset($macros[$token->getValue()]); } $tokens->next(); } foreach ($macros as $name => $originalToken) { $this->addViolation($tokens->getFilename(), $originalToken->getLine(), $originalToken->getColumn(), sprintf('Unused macro "%s".', $name)); } return $this->violations; }
/** * get a primitive (string, quoted string, integer or float) * argument * * @param Twig_TokenStream $s * @return mixed */ protected function _getPrimitiveArgument(Twig_TokenStream $s) { // integer & float if ($s->test(Twig_Token::NUMBER_TYPE)) { return $s->expect(Twig_Token::NUMBER_TYPE)->getValue(); } // boolean if ($s->test(Twig_Token::NAME_TYPE, array('true', 'false'))) { return $s->expect(Twig_Token::NAME_TYPE)->getValue() == 'true'; } // string if ($s->test(Twig_Token::NAME_TYPE)) { return $s->expect(Twig_Token::NAME_TYPE)->getValue(); } // quoted string return $s->expect(Twig_Token::STRING_TYPE)->getValue(); }
protected function getTwigArgument(\Twig_TokenStream $stream) { $buffer = ""; // 1. Move to open bracket while (!$stream->isEOF()) { if ($stream->next()->getValue() === '(') { break; } } // 2. Fetch until close bracket while (!$stream->isEOF()) { $token = $stream->next()->getValue(); if ($token === ')' || $token === ',') { break; } $buffer .= $token; } return $buffer; }
/** * Get value from stream * * @param \Twig_TokenStream $stream * @param bool $isBool * * @return bool|string */ protected function parseValue(\Twig_TokenStream $stream, $isBool = true) { $stream->next(); $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); if ($isBool) { return 'true' == $stream->expect(\Twig_Token::NAME_TYPE, ['true', 'false'])->getValue(); } return $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); }
/** * @param \Twig_TokenStream $stream * * @throws \Twig_Error_Syntax */ private function throwSyntaxError(\Twig_TokenStream $stream) { $token = $stream->getCurrent(); throw new \Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', \Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $stream->getFilename()); }
/** * Helper method for the common operation of grabbing the next boolean value * from the stream * * @param \Twig_TokenStream $stream * @param string $optionName * @return string */ protected function getNextExpectedBoolValueFromStream(\Twig_TokenStream $stream, $optionName) { $stream->next(); $stream->expect(\Twig_Token::PUNCTUATION_TYPE); $expr = $this->parser->getExpressionParser()->parseExpression(); if (!$expr instanceof \Twig_Node_Expression_Constant || !is_bool($expr->getAttribute('value'))) { throw new SyntaxException(sprintf('The %s option must be boolean true or false (i.e. %s:false)', $optionName, $optionName), $stream->getCurrent()->getLine(), $stream->getFilename()); } return $expr->getAttribute('value'); }