必须继承 twig\extension\abstractextension 并重写 getfunctions() 返回 twigfunction 实例数组;构造时传模板函数名和可调用对象,方法需 public 且 return 字符串;twig 2.0+ 废弃 twig_simplefunction,须用 \twig\twigfunction。

如何注册自定义 Twig 函数(TwigFunction)
自定义函数必须通过继承 Twig\Extension\AbstractExtension 实现,不能直接在模板里调用普通 PHP 函数。核心是重写 getFunctions() 方法,返回一个 TwigFunction 实例数组。
每个 TwigFunction 构造时需传入两个必要参数:第一个是模板中使用的函数名(如 'price'),第二个是可调用对象(推荐用 [$this, 'methodName'] 形式)。
- 函数体里不要直接 echo 或输出内容,必须 return 字符串;否则模板渲染会错乱
- 若函数需访问当前 Twig 环境(比如获取当前请求或渲染子模板),要加配置项
['needs_environment' => true],并在方法签名里声明Twig\Environment $env参数 - 若返回 HTML 片段且已确保安全(比如你控制了所有输入),加上
['is_safe' => ['html']],否则 Twig 会自动转义
示例:formatPrice() 方法:
public function formatPrice(float $amount): string
{
return '¥' . number_format($amount, 2, '.', ',');
}
对应注册:
new TwigFunction('price', [$this, 'formatPrice'])
为什么不能用 Twig_SimpleFunction(旧版类)
Twig 2.0+ 已废弃 Twig_SimpleFunction 和 Twig_SimpleFilter。如果你在老项目里看到它,说明还在用 Twig 1.x 或兼容层——升级后必须换成 TwigFunction、TwigFilter 等新类,否则运行时报 Class "Twig_SimpleFunction" not found。
Symfony 5.4+ 默认绑定 Twig 3.x,6.x 绑定 Twig 4.x,都不再支持旧命名空间和类名。
- 错误写法:
new \Twig_SimpleFunction('foo', ...) - 正确写法:
new \Twig\TwigFunction('foo', ...) - 别漏掉
\Twig\命名空间前缀,否则类加载失败
自定义标签(TwigTokenParser + TwigNode)不是常规需求
绝大多数场景下,你根本不需要写自定义标签。函数(TwigFunction)和过滤器(TwigFilter)能覆盖 95% 的模板逻辑需求。只有当你需要类似 {% cache %}...{% endcache %} 这种带区块、解析语法、控制渲染流程的结构时,才考虑标签。
写标签意味着你要同时实现:
- 一个继承
Twig\TokenParser\AbstractTokenParser的解析器,负责识别{% mytag %}并提取参数 - 一个继承
Twig\Node\Node的节点类,描述运行时行为 - 一个编译方法,把节点转成 PHP 代码(Twig 最终是编译为 PHP 执行的)
这属于 Twig 底层机制,调试困难、易出错、维护成本高。除非你在开发通用扩展(比如集成某 SDK 的完整控制块),否则绕开它。
服务注册和自动发现的坑
Symfony 默认启用 Twig 扩展自动注册,但有两个硬性前提:
- 类必须放在
src/Twig/目录下(注意是Twig/,不是Twig/Extension/或其他子目录) - 类必须继承
Twig\Extension\AbstractExtension(或实现Twig\Extension\ExtensionInterface) - 如果类路径不满足,就得手动注册服务并打上
twig.extension标签
手动注册示例(config/services.yaml):
App\Twig\MyCustomExtension:
tags: [{ name: 'twig.extension' }]
常见失败现象:模板里调用函数报 Unknown "xxx" function —— 先检查类路径、继承关系、服务是否被扫描到,而不是怀疑函数逻辑。
真正容易被忽略的是:扩展类里的方法必须是 public,且参数类型声明要与实际传入值兼容(比如模板传字符串,方法却声明 int $x,会触发 TypeError)。











