bug #4906 Deprecate prefixed macro definedness checks (fabpot)

This PR was merged into the 3.x branch.

Discussion
----------

Deprecate prefixed macro definedness checks

Testing a macro through a legacy `macro_`-prefixed name would have silently returned `false` in Twig 4.0 without ever warning; `has()` now triggers the same deprecation as `call()`.

Commits
-------

977aef7172 Deprecate prefixed macro definedness checks
This commit is contained in:
Fabien Potencier
2026-08-27 17:57:48 +02:00
2 changed files with 20 additions and 8 deletions
+7 -1
View File
@@ -39,7 +39,13 @@ final class MacroNamespace
return true;
}
return str_starts_with($name, 'macro_') && null !== $this->findDeclaredName(substr($name, \strlen('macro_')), $context);
if (!str_starts_with($name, 'macro_') || null === $this->findDeclaredName($bareName = substr($name, \strlen('macro_')), $context)) {
return false;
}
trigger_deprecation('twig/twig', '3.29', 'Testing whether the macro "%s" is defined via the "macro_"-prefixed name "%s" is deprecated; pass the bare macro name to "%s" instead.', $bareName, $name, MacroReferenceExpression::class);
return true;
}
/**
+13 -7
View File
@@ -11,7 +11,9 @@
namespace Twig\Tests;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Twig\Environment;
use Twig\Error\RuntimeError;
use Twig\Extension\CoreExtension;
@@ -24,6 +26,8 @@ use Twig\Template;
class CallMacroTest extends TestCase
{
use ExpectDeprecationTrait;
public function testCallMacroResolvesPositionalAndNamedArguments(): void
{
$template = $this->load([
@@ -168,17 +172,19 @@ class CallMacroTest extends TestCase
$template->getMacroNamespace()->call('macro_missing', [], [], 1, new Source('', 'index'));
}
public function testHasMacroResolvesALegacyPrefixedNameSilently(): void
/**
* @group legacy
*/
#[Group('legacy')]
public function testHasMacroResolvesALegacyPrefixedNameWithADeprecation(): void
{
$template = $this->load(['index' => '{% macro greet(name) %}Hi {{ name }}{% endmacro %}']);
$deprecations = $this->collectDeprecations(function () use ($template) {
$namespace = $template->getMacroNamespace();
$this->assertTrue($namespace->has('macro_greet', []));
$this->assertFalse($namespace->has('macro_missing', []));
});
$this->expectDeprecation('Since twig/twig 3.29: Testing whether the macro "greet" is defined via the "macro_"-prefixed name "macro_greet" is deprecated; pass the bare macro name to "Twig\Node\Expression\MacroReferenceExpression" instead.');
$this->assertSame([], $deprecations);
$namespace = $template->getMacroNamespace();
$this->assertTrue($namespace->has('macro_greet', []));
$this->assertFalse($namespace->has('macro_missing', []));
}
public function testCallMacroThrowsForAnUnknownMacro(): void