From c9c3b23a77ca033c2e1203645298d152f6c04902 Mon Sep 17 00:00:00 2001 From: Pascal CESCON - Amoifr Date: Wed, 19 Aug 2026 17:35:38 +0200 Subject: [PATCH] Fix the empty comment "{##}" being lexed as a documentation comment opening --- src/Lexer.php | 6 +++++- tests/ParserTest.php | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Lexer.php b/src/Lexer.php index c2e9f31ba..25b9f2143 100644 --- a/src/Lexer.php +++ b/src/Lexer.php @@ -95,6 +95,10 @@ class Lexer return; } + // when the comment closing tag starts with "#" (as "#}" does), the empty comment ("{##}") + // starts like a documentation comment ("{##"); make sure it keeps lexing as a comment + $emptyCommentLookahead = str_starts_with($this->options['tag_comment'][1], '#') ? '(?!'.preg_quote(substr($this->options['tag_comment'][1], 1), '#').')' : ''; + // when PHP 7.3 is the min version, we will be able to remove the '#' part in preg_quote as it's part of the default $this->regexes = [ // }} @@ -187,7 +191,7 @@ class Lexer '|'. preg_quote($this->options['tag_block'][0], '#'). // {% '|'. - preg_quote($this->options['tag_comment'][0].'#', '#'). // {## + preg_quote($this->options['tag_comment'][0].'#', '#').$emptyCommentLookahead. // {## (but not the empty comment {##}) '|'. preg_quote($this->options['tag_comment'][0], '#'). // {# ')('. diff --git a/tests/ParserTest.php b/tests/ParserTest.php index bff1fa34d..f90fa46c5 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -349,6 +349,19 @@ TWIG, 'index'))); $this->assertNull($body->getNode('4')->getDocumentation()); } + public function testEmptyCommentIsNotLexedAsDocumentation(): void + { + $twig = new Environment(new ArrayLoader(), ['autoescape' => false]); + $module = $twig->parse($twig->tokenize(new Source(<<<'TWIG' +{##}{{ answer }} +{% block a %}x{##}{% endblock %}{% block b %}y{% endblock %} +TWIG, 'index'))); + $body = $module->getNode('body')->getNode('0'); + + $this->assertInstanceOf(PrintNode::class, $body->getNode('0')); + $this->assertNull($body->getNode('0')->getDocumentation()); + } + public function testInlineDocumentationBeforeATagNameIsIgnored(): void { $twig = new Environment(new ArrayLoader(), ['autoescape' => false]); @@ -440,7 +453,7 @@ TWIG, 'index'))); { $twig = new Environment(new ArrayLoader()); - $this->expectDeprecation('Since twig/twig 3.29: Defining the macro "input" more than once in "index" is deprecated and will throw a SyntaxError in Twig 4.0 (first definition at line 1, second at line 1).'); + $this->expectDeprecation('Since twig/twig 3.29: Defining the macro "input" more than once in "index" is deprecated and will throw a SyntaxError in Twig 4.0 (first definition at line 1, second at line 1). The last definition is used in Twig 3.'); $module = $twig->parse($twig->tokenize(new Source('{## First #}{% macro input() %}{% endmacro %}{## Second #}{% macro input() %}{% endmacro %}', 'index')));