feature #4199 Do not hide unnecessary escape characters (ruudk)

This PR was squashed before being merged into the 4.x branch.

Discussion
----------

Do not hide unnecessary escape characters

Follow up of #4176, #4123 and #2712.

Now that we have deprecated unnecessary escape in v3, we can change the behavior in v4.

With this change, unnecessary will no longer be ignored, but handled like any other character.

This allows for writing fully qualified class names like this:
```twig
{{ constant('App\Entity\User::SOME_CONSTANT') }}
```

Instead of having to escape the `\` in v3:
```twig
{{ constant('App\\Entity\\User::SOME_CONSTANT') }}
```

/cc `@fabpot` `@stof`

Commits
-------

53c24bfc Do not hide unnecessary escape characters
This commit is contained in:
Fabien Potencier
2024-08-21 19:42:34 +02:00
6 changed files with 36 additions and 60 deletions
+1
View File
@@ -7,3 +7,4 @@
* Change the compilation of `for` loops to throw an exception when a `loop.*` variable is not defined
* Make `Environment::getGlobals()` private
* Drop support for PHP < 8.2
* Escape characters are no longer ignored
+2 -8
View File
@@ -428,12 +428,7 @@ class Lexer
if (isset(self::SPECIAL_CHARS[$nextChar])) {
$result .= self::SPECIAL_CHARS[$nextChar];
} elseif ('\\' === $nextChar) {
$result .= $nextChar;
} elseif ("'" === $nextChar || '"' === $nextChar) {
if ($nextChar !== $quoteType) {
trigger_deprecation('twig/twig', '3.12', 'Character "%s" at position %d should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.', $nextChar, $i + 1);
}
} elseif ($nextChar === '\\' || $nextChar === $quoteType) {
$result .= $nextChar;
} elseif ('#' === $nextChar && $i + 1 < $length && '{' === $str[$i + 1]) {
$result .= '#{';
@@ -451,8 +446,7 @@ class Lexer
}
$result .= \chr(octdec($octal));
} else {
trigger_deprecation('twig/twig', '3.12', 'Character "%s" at position %d should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.', $nextChar, $i + 1);
$result .= $nextChar;
$result .= '\\'.$nextChar;
}
++$i;
+14
View File
@@ -0,0 +1,14 @@
--TEST--
Exception with bad line number
--TEMPLATE--
{{ 'Foo\Bar' }}
{{ 'Foo\\Bar' }}
{{ 'Foo\\\Bar' }}
{{ 'Foo\\\\Bar' }}
--DATA--
return []
--EXPECT--
Foo\Bar
Foo\Bar
Foo\\Bar
Foo\\Bar
@@ -1,10 +0,0 @@
--TEST--
"enum_cases" function with missing \ escaping
--CONDITION--
\PHP_VERSION_ID >= 80100
--TEMPLATE--
{% for c in enum_cases('Twig\Tests\DummyBackedEnum') %}
{{~ c.name }}
{% endfor %}
--EXCEPTION--
Twig\Error\SyntaxError: The first argument of the "enum_cases" function must be the name of an enum, "TwigTestsDummyBackedEnum" given in "index.twig" at line 2.
@@ -6,10 +6,13 @@
{% for c in enum_cases('Twig\\Tests\\DummyBackedEnum') %}
{{~ c.name }}: {{ c.value }}
{% endfor %}
{% for c in enum_cases('Twig\\Tests\\DummyUnitEnum') %}
{% for c in enum_cases('Twig\Tests\DummyBackedEnum') %}
{{~ c.name }}: {{ c.value }}
{% endfor %}
{% for c in enum_cases('Twig\Tests\DummyUnitEnum') %}
{{~ c.name }}
{% endfor %}
{% set from_variable='Twig\\Tests\\DummyUnitEnum' %}
{% set from_variable='Twig\Tests\DummyUnitEnum' %}
{% for c in enum_cases(from_variable) %}
{{~ c.name }}
{% endfor %}
@@ -18,6 +21,8 @@ return []
--EXPECT--
FOO: foo
BAR: bar
FOO: foo
BAR: bar
BAR
BAZ
BAR
+12 -40
View File
@@ -12,7 +12,6 @@ namespace Twig\Tests;
*/
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Twig\Environment;
use Twig\Error\SyntaxError;
use Twig\Lexer;
@@ -22,8 +21,6 @@ use Twig\Token;
class LexerTest extends TestCase
{
use ExpectDeprecationTrait;
public function testNameLabelForTag()
{
$template = '{% § %}';
@@ -195,6 +192,18 @@ class LexerTest extends TestCase
public function getStringWithEscapedDelimiter()
{
yield '{{ \'App\Test\' }} => App\Test' => [
'{{ \'App\\Test\' }}',
'App\Test',
];
yield '{{ "foo \\\' bar" }} => foo \' bar' => [
'{{ "foo \\\' bar" }}',
"foo \' bar",
];
yield '{{ \'foo \" bar\' }} => foo \" bar' => [
'{{ \'foo \\" bar\' }}',
'foo \" bar',
];
yield '{{ \'\x6\' }} => \x6' => [
'{{ \'\x6\' }}',
"\x6",
@@ -229,43 +238,6 @@ class LexerTest extends TestCase
];
}
/**
* @group legacy
* @dataProvider getStringWithEscapedDelimiterProducingDeprecation
*/
public function testStringWithEscapedDelimiterProducingDeprecation(string $template, string $expected, string $expectedDeprecation)
{
$this->expectDeprecation($expectedDeprecation);
$lexer = new Lexer(new Environment(new ArrayLoader()));
$stream = $lexer->tokenize(new Source($template, 'index'));
$stream->expect(Token::VAR_START_TYPE);
$stream->expect(Token::STRING_TYPE, $expected);
// 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 getStringWithEscapedDelimiterProducingDeprecation()
{
yield '{{ \'App\Test\' }} => AppTest' => [
'{{ \'App\\Test\' }}',
'AppTest',
'Since twig/twig 3.12: Character "T" at position 5 should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.',
];
yield '{{ "foo \\\' bar" }} => foo \' bar' => [
'{{ "foo \\\' bar" }}',
'foo \' bar',
'Since twig/twig 3.12: Character "\'" at position 6 should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.',
];
yield '{{ \'foo \" bar\' }} => foo " bar' => [
'{{ \'foo \\" bar\' }}',
'foo " bar',
'Since twig/twig 3.12: Character """ at position 6 should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.',
];
}
public function testStringWithInterpolation()
{
$template = 'foo {{ "bar #{ baz + 1 }" }}';