Skip to content

Commit 2ec5479

Browse files
committed
bug #4774 Ensure filters/attributes aren't mistaken for operators (brandonkelly)
This PR was merged into the 3.x branch. Discussion ---------- Ensure filters/attributes aren't mistaken for operators Updates the regex in `Lexer::getOperatorRegex()` to account for filters/attributes that have a space between their `|`/`.` operator and the filter/attribute name, to ensure they aren’t mistaken for operators. A test is included that checks the following template. ```twig {{ 'foo'|and }} {{ 'bar' | and }} {{ foo.and }} {{ bar . and }} {{ foo and bar }} ``` (Only the `and` in the last tag should be considered an operator.) Fixes #4767 Commits ------- a9ac993 Ensure filters/attributes aren't mistaken for operators
2 parents 206ad9f + a9ac993 commit 2ec5479

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

src/Lexer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ private function getOperatorRegex(): string
544544

545545
// an operator that begins with a character must not have a dot or pipe before
546546
if (ctype_alpha($expressionParser[0])) {
547-
$r = '(?<![\.\|])'.$r;
547+
$r = '(?<![\.\|]\s|.[\.\|])'.$r;
548548
}
549549

550550
// an operator with a space can be any amount of whitespaces

tests/LexerTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,43 @@ public function testOperatorEndingWithALetterAtTheEndOfALine()
420420
$this->addToAssertionCount(1);
421421
}
422422

423+
public function testFilterAndAttributeNamedAfterOperator()
424+
{
425+
// Ensure that filters/attributes aren't mistaken for operators when their names conflict
426+
// (see https://github.com/twigphp/Twig/issues/4767)
427+
$template = '{{ \'foo\'|and }}'
428+
.'{{ \'bar\' | and }}'
429+
.'{{ foo.and }}'
430+
.'{{ bar . and }}'
431+
.'{{ foo and bar }}';
432+
433+
$lexer = new Lexer(new Environment(new ArrayLoader()));
434+
$stream = $lexer->tokenize(new Source($template, 'index'));
435+
foreach (['foo', 'bar'] as $value) {
436+
$stream->expect(Token::VAR_START_TYPE);
437+
$stream->expect(Token::STRING_TYPE, $value);
438+
$stream->expect(Token::OPERATOR_TYPE, '|');
439+
$stream->expect(Token::NAME_TYPE, 'and');
440+
$stream->expect(Token::VAR_END_TYPE);
441+
}
442+
foreach (['foo', 'bar'] as $value) {
443+
$stream->expect(Token::VAR_START_TYPE);
444+
$stream->expect(Token::NAME_TYPE, $value);
445+
$stream->expect(Token::OPERATOR_TYPE, '.');
446+
$stream->expect(Token::NAME_TYPE, 'and');
447+
$stream->expect(Token::VAR_END_TYPE);
448+
}
449+
$stream->expect(Token::VAR_START_TYPE);
450+
$stream->expect(Token::NAME_TYPE, 'foo');
451+
$stream->expect(Token::OPERATOR_TYPE, 'and');
452+
$stream->expect(Token::NAME_TYPE, 'bar');
453+
$stream->expect(Token::VAR_END_TYPE);
454+
455+
// add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above
456+
// can be executed without throwing any exceptions
457+
$this->addToAssertionCount(1);
458+
}
459+
423460
public function testUnterminatedVariable()
424461
{
425462
$template = '

0 commit comments

Comments
 (0)