
本文介绍在 Laravel 应用中,如何安全、灵活地解析用户输入的 textarea 内容(如 {{name}}、{{nickname}}、{{email}}),并将其动态替换为对应用户数据,适用于邮件模板、通知消息等场景。
本文介绍在 laravel 应用中,如何安全、灵活地解析用户输入的 textarea 内容(如 `{{name}}`、`{{nickname}}`、`{{email}}`),并将其动态替换为对应用户数据,适用于邮件模板、通知消息等场景。
在构建用户可自定义内容的系统(如通知模板、站内信、邮件正文)时,常需支持类似 {{name}} 的占位符语法。这类需求不能简单依赖 explode() —— 它仅适用于固定分隔符的字符串切割,而模板变量是嵌入式、非结构化且可能重复出现的,必须通过正则匹配 + 动态回调的方式精准识别与替换。
✅ 正确实现思路:正则提取 + 方法映射
核心逻辑分为三步:
- 使用 preg_match_all() 提取所有形如 {{xxx}} 的变量名;
- 将变量名(如 name)拼接为方法名(如 replace_name);
- 检查当前类是否定义了该方法,并调用它返回真实值,再用 str_replace() 全局替换。
? 示例代码(推荐放在 Controller 或专用 TemplateService 类中)
public function renderTemplate($content, $user)
{
// 保存上下文(如当前用户对象),便于替换方法访问
$this->user = $user;
// 匹配所有 {{key}} 格式,捕获 key(不包含花括号)
preg_match_all('/{{(\w+)}}/', $content, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$key = $match[1]; // 如 'name', 'nickname', 'email'
$methodName = 'replace_' . $key;
if (method_exists($this, $methodName)) {
$replacement = $this->$methodName($match);
$content = str_replace($match[0], $replacement, $content);
}
// ⚠️ 可选:对未定义的变量保留原样或替换为 '[undefined]'
// else {
// $content = str_replace($match[0], '[undefined]', $content);
// }
}
return $content;
}? 对应的替换方法(按需定义)
protected $user;
public function replace_name($match)
{
return $this->user->name ?? '';
}
public function replace_nickname($match)
{
return $this->user->nickname ?? $this->user->name ?? '';
}
public function replace_email($match)
{
return $this->user->email ?? '';
}? 为什么不用 explode()?
原问题中 $new_content = explode($content, '{{name}}', ...) 是错误用法:explode() 第一个参数是分隔符,不是被分割的字符串;且它无法处理多个不同变量、嵌套或重复出现的场景(如 {{name}} says {{name}} is here)。正则才是语义化匹配的可靠选择。
✅ 进阶建议与注意事项
- 安全性优先:若用户可编辑模板内容,务必对输出做 htmlspecialchars() 转义(尤其在 Blade 中直接 {{ $rendered }} 渲染时),防止 XSS。
- 变量隔离:避免直接使用 $this->user->xxx,建议将用户数据封装为数组或 DTO 传入,提升可测试性与解耦度。
- 性能优化:对于高频调用场景,可缓存正则匹配结果或预编译替换规则。
- 扩展性设计:支持 {{ config('app.name') }} 或 {{ now()->format('Y-m-d') }} 等函数式写法,只需升级正则为 /{{\s*([^}]+)\s*}}/ 并配合 eval()(⚠️慎用)或白名单函数执行器。
✅ 最终使用示例(Controller 中)
public function send(Request $request)
{
$content = $request->input('content'); // 来自 <textarea name="content">
$user = auth()->user();
$rendered = $this->renderTemplate($content, $user);
// 发送逻辑...
return response()->json(['message' => $rendered]);
}输入:
Hello, {{name}} has {{nickname}} and {{email}}!
输出:
Hello, Alice has A-Lice and alice@example.com!
掌握这一模式后,你不仅能处理基础字段,还可轻松扩展支持日期、计数、条件渲染等高级模板能力——让静态文本真正“活”起来。










