@@ -26,23 +26,15 @@ class MacroReferenceExpression extends AbstractExpression implements SupportDefi
2626 use SupportDefinedTestTrait;
2727
2828 /**
29- * @param string|AbstractExpression $name A static macro method name (e.g. "macro_foo") or, for a dynamic
30- * call, an expression resolving to the macro name (without the
31- * "macro_" prefix, which is added at runtime)
29+ * @param string|AbstractExpression $name The bare macro name (a static identifier) or, for a dynamic
30+ * call, an expression resolving to the macro name
3231 */
3332 public function __construct (TemplateVariable $ template , string |AbstractExpression $ name , AbstractExpression $ arguments , int $ lineno )
3433 {
3534 $ nodes = ['template ' => $ template , 'arguments ' => $ arguments ];
3635 $ attributes = ['name ' => null ];
3736
3837 if (\is_string ($ name )) {
39- // The name is emitted as raw PHP in compile() via "->{$name}(...)",
40- // so it must be a valid PHP method identifier. Reject anything else
41- // as a defense-in-depth against accidental PHP code injection from
42- // a caller that forgot to validate user-controlled input.
43- if (!preg_match ('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D ' , $ name )) {
44- throw new \LogicException (\sprintf ('Macro name "%s" is not a valid PHP identifier. ' , $ name ));
45- }
4638 $ attributes ['name ' ] = $ name ;
4739 } else {
4840 $ nodes ['name ' ] = $ name ;
@@ -63,74 +55,41 @@ public function __clone()
6355
6456 public function compile (Compiler $ compiler ): void
6557 {
66- if ($ this ->hasNode ('name ' )) {
67- $ this ->compileDynamic ($ compiler );
68-
69- return ;
70- }
58+ $ compiler ->subcompile ($ this ->getNode ('template ' ));
7159
7260 if ($ this ->definedTest ) {
73- $ compiler
74- ->subcompile ($ this ->getNode ('template ' ))
75- ->raw ('->hasMacro( ' )
76- ->repr ($ this ->getAttribute ('name ' ))
77- ->raw (', $context ' )
78- ->raw (') ' )
79- ;
61+ $ compiler ->raw ('->hasMacro( ' );
62+ $ this ->compileName ($ compiler );
63+ $ compiler ->raw (', $context) ' );
8064
8165 return ;
8266 }
8367
68+ $ compiler ->raw ('->callMacro( ' );
69+ $ this ->compileName ($ compiler );
8470 $ compiler
85- ->subcompile ($ this ->getNode ('template ' ))
86- ->raw ('->getTemplateForMacro( ' )
87- ->repr ($ this ->getAttribute ('name ' ))
71+ ->raw (', ' )
72+ ->subcompile ($ this ->getNode ('arguments ' ))
8873 ->raw (', $context, ' )
8974 ->repr ($ this ->getTemplateLine ())
9075 ->raw (', $this->getSourceContext()) ' )
91- ->raw (\sprintf ('->%s ' , $ this ->getAttribute ('name ' )))
92- ->raw ('(... ' )
93- ->subcompile ($ this ->getNode ('arguments ' ))
94- ->raw (') ' )
9576 ;
9677 }
9778
9879 public function getStringCoercedChildNames (): array
9980 {
100- // Dynamic macro names are prefixed via PHP string concatenation at runtime.
81+ // Dynamic macro names are string-coerced at runtime.
10182 return $ this ->hasNode ('name ' ) ? ['name ' ] : [];
10283 }
10384
104- private function compileDynamic (Compiler $ compiler ): void
85+ private function compileName (Compiler $ compiler ): void
10586 {
106- // The macro method name is resolved at runtime from a context value;
107- // prefixing it with "macro_" constrains the dynamic method call to the
108- // template's macro methods only, and getTemplateForMacro()/hasMacro()
109- // validate that the method actually exists.
110- $ var = $ compiler ->getVarName ();
111-
112- if ($ this ->definedTest ) {
113- $ compiler
114- ->subcompile ($ this ->getNode ('template ' ))
115- ->raw ('->hasMacro( \'macro_ \'. ' )
116- ->subcompile ($ this ->getNode ('name ' ))
117- ->raw (', $context) ' )
118- ;
119-
120- return ;
87+ // A dynamic macro name is resolved at runtime from a context value and
88+ // string-coerced before the registry lookup.
89+ if ($ this ->hasNode ('name ' )) {
90+ $ compiler ->raw ('(string) ' )->subcompile ($ this ->getNode ('name ' ));
91+ } else {
92+ $ compiler ->repr ($ this ->getAttribute ('name ' ));
12193 }
122-
123- $ compiler
124- ->subcompile ($ this ->getNode ('template ' ))
125- ->raw (\sprintf ('->getTemplateForMacro($%s = \'macro_ \'. ' , $ var ))
126- ->subcompile ($ this ->getNode ('name ' ))
127- ->raw (', $context, ' )
128- ->repr ($ this ->getTemplateLine ())
129- ->raw (', $this->getSourceContext()) ' )
130- ->raw (\sprintf ('->{$%s} ' , $ var ))
131- ->raw ('(... ' )
132- ->subcompile ($ this ->getNode ('arguments ' ))
133- ->raw (') ' )
134- ;
13594 }
13695}
0 commit comments