diff --git a/CHANGELOG b/CHANGELOG index e64b0a2a9..d90440883 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ * 1.37.0 (2019-XX-XX) + * fixed embedded templates starting with a BOM * fixed using a Twig_TemplateWrapper instance as an argument to extends * switched generated code to use the PHP short array notation * dropped PHP 5.3 support diff --git a/lib/Twig/Parser.php b/lib/Twig/Parser.php index 7c5dfd695..ff4440116 100644 --- a/lib/Twig/Parser.php +++ b/lib/Twig/Parser.php @@ -382,7 +382,11 @@ class Twig_Parser implements Twig_ParserInterface (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface) ) { 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()); + $t = substr($node->getAttribute('data'), 3); + if ('' === $t || ctype_space($t)) { + // bypass empty nodes starting with a BOM + return; + } } 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()); diff --git a/test/Twig/Tests/ParserTest.php b/test/Twig/Tests/ParserTest.php index 4f0f1a450..d9ad0ab35 100644 --- a/test/Twig/Tests/ParserTest.php +++ b/test/Twig/Tests/ParserTest.php @@ -99,13 +99,21 @@ class Twig_Tests_ParserTest extends \PHPUnit\Framework\TestCase } /** - * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage A template that extends another one cannot start with a byte order mark (BOM); it must be removed at line 1 + * @dataProvider getFilterBodyNodesWithBOMData */ - public function testFilterBodyNodesWithBOM() + public function testFilterBodyNodesWithBOM($emptyNode) { - $parser = $this->getParser(); - $parser->filterBodyNodes(new Twig_Node_Text(chr(0xEF).chr(0xBB).chr(0xBF), 1)); + $this->assertSame(null, $this->getParser()->filterBodyNodes(new Twig_Node_Text(chr(0xEF).chr(0xBB).chr(0xBF).$emptyNode, 1))); + } + + public function getFilterBodyNodesWithBOMData() + { + return [ + [' '], + ["\t"], + ["\n"], + ["\n\t\n "], + ]; } public function testParseIsReentrant()