diff --git a/src/Lexer.php b/src/Lexer.php index 508a79c02..3d4e0b863 100644 --- a/src/Lexer.php +++ b/src/Lexer.php @@ -544,7 +544,7 @@ class Lexer // an operator that begins with a character must not have a dot or pipe before if (ctype_alpha($expressionParser[0])) { - $r = '(?addToAssertionCount(1); } + public function testFilterAndAttributeNamedAfterOperator() + { + // Ensure that filters/attributes aren't mistaken for operators when their names conflict + // (see https://github.com/twigphp/Twig/issues/4767) + $template = '{{ \'foo\'|and }}' + .'{{ \'bar\' | and }}' + .'{{ foo.and }}' + .'{{ bar . and }}' + .'{{ foo and bar }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + foreach (['foo', 'bar'] as $value) { + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, $value); + $stream->expect(Token::OPERATOR_TYPE, '|'); + $stream->expect(Token::NAME_TYPE, 'and'); + $stream->expect(Token::VAR_END_TYPE); + } + foreach (['foo', 'bar'] as $value) { + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::NAME_TYPE, $value); + $stream->expect(Token::OPERATOR_TYPE, '.'); + $stream->expect(Token::NAME_TYPE, 'and'); + $stream->expect(Token::VAR_END_TYPE); + } + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'foo'); + $stream->expect(Token::OPERATOR_TYPE, 'and'); + $stream->expect(Token::NAME_TYPE, 'bar'); + $stream->expect(Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + public function testUnterminatedVariable() { $template = '