揭秘laravel-blade-javascript责任链模式:6个Transformer协作转换任意值到JavaScript
【免费下载链接】laravel-blade-javascriptA Blade directive to export variables to JavaScript项目地址: https://gitcode.com/gh_mirrors/la/laravel-blade-javascript
laravel-blade-javascript是一个 Laravel 扩展包,它提供了一个@javascriptBlade 指令,让你可以把 PHP 变量一键导出到 JavaScript中:在模板里写一行@javascript('key', 'value'),页面渲染后浏览器里就能直接用key这个变量。
那么问题来了:PHP 里什么都有——字符串、布尔、数组、对象、null……它是怎么把"任意值"安全地变成合法的 JavaScript 的?答案就是本项目源码中隐藏的责任链模式(Chain of Responsibility)🎯
先看效果:一个指令打通 PHP 与 JavaScript
安装只需一条命令:
composer require spatie/laravel-blade-javascript在 Blade 模板中使用:
@javascript('key', 'value')页面最终输出:
<script>window['key'] = 'value';</script>如果不想污染全局,还可以在配置文件 config/blade-javascript.php 里设置namespace,把变量收进独立命名空间,避免命名冲突。
责任链模式是什么?为什么这里需要它
想象一支流水线:一个值进入后,依次询问每个转换器——"你能处理它吗?"谁能处理,谁来接手,其他人继续待命。这就是责任链模式:
- 每个节点只负责一种职责(单一职责,代码短小好测试)
- 新增类型只需加一个节点,不用改老代码(开闭原则)
- 拒绝处理时自动"传递"给下一个,链路末段统一兜底报错
laravel-blade-javascript 正是这样实现的。所有转换器都实现同一个接口,定义在 src/Transformers/Transformer.php:
interface Transformer { public function canTransform($value): bool; // 我能处理吗? public function transform($value); // 那就交给我 }两个方法、一种契约,六个实现。
Renderer:责任链的"总调度"
整条链的发起者是 src/Renderer.php。它在构造函数里注册了 6 个转换器,按固定顺序组成链条:
protected $transformers = [ ArrayTransformer::class, BooleanTransformer::class, NullTransformer::class, NumericTransformer::class, ObjectTransformer::class, StringTransformer::class, ];真正的"传递"发生在getTransformer()方法中——逐个询问,命中即返回:
public function getTransformer($value): Transformer { foreach ($this->getAllTransformers() as $transformer) { if ($transformer->canTransform($value)) { return $transformer; } } throw Untransformable::noTransformerFound($value); }一句话概括:先问遍所有人,没人能处理就抛Untransformable异常,错误信息直接告诉你是哪个值没被转换,排查体验极佳。
6个Transformer逐一揭秘:各自认领什么任务?
1️⃣ ArrayTransformer —— 数组与集合的"翻译官"
位于 src/Transformers/ArrayTransformer.php,用is_array()认领所有数组,直接json_encode()变成 JSON 对象。这是 PHP 数组与 JS 对象之间最自然的桥梁。
2️⃣ BooleanTransformer —— 布尔值的类型对齐器
位于 src/Transformers/BooleanTransformer.php。PHP 的true/false恰好和 JavaScript 同名,所以转换几乎"零成本",保证前端拿到的是布尔类型而不是字符串"1"。
3️⃣ NullTransformer —— 空值的兜底者
位于 src/Transformers/NullTransformer.php。PHP 的null对应 JS 的null,短小精悍但必不可少——否则一个 null 值就可能整条链跑空。
4️⃣ NumericTransformer —— 数字的直通车
位于 src/Transformers/NumericTransformer.php,同时认领int和float。它做的几乎是"原样放行",因为数字在两种语言里的字面量写法一致,是整条链里最轻松的节点。
5️⃣ ObjectTransformer —— 对象的多策略专家
位于 src/Transformers/ObjectTransformer.php,是链中最"有想法"的一位,按优先级尝试三种策略:
- 对象有
toJson()方法?直接调用(兼容 Laravel Eloquent 模型) - 对象实现了
JsonSerializable或是StdClass?交给json_encode() - 有
__toString()方法?退化成字符串处理
三条路都走不通,才会抛出Untransformable异常。
6️⃣ StringTransformer —— 字符串的"安全卫士"
位于 src/Transformers/StringTransformer.php。它不只是加引号,内部还有一套转义逻辑:对反斜杠、单引号、换行以及<>做替换——最后一步防止用户输入提前闭合 script 标签,规避 XSS 注入风险。这是整条链里安全含金量最高的节点。
协作全景图:一个值如何走完责任链
| 顺序 | Transformer | 认领条件 | 转换结果示例 |
|---|---|---|---|
| 1 | ArrayTransformer | is_array() | {"id":1} |
| 2 | BooleanTransformer | is_bool() | true/false |
| 3 | NullTransformer | is_null() | null |
| 4 | NumericTransformer | is_int()或is_float() | 42/3.14 |
| 5 | ObjectTransformer | is_object() | JSON 或转义字符串 |
| 6 | StringTransformer | is_string() | 'hello' |
以@javascript('user', $user)为例,一次完整的调用流程:
- Renderer 接收参数:
normalizeArguments()把多种传参写法(键值对、数组、Collection)统一成key => value结构 - 逐个变量走链:
buildVariableInitialization()为每个值调用optimizeValueForJavaScript() - 责任链选人:
getTransformer()按上表顺序询问 6 位 Transformer,命中者完成转换 - 拼装输出:所有
window['key'] = 值;语句拼接到模板视图 src/Renderer.php 中渲染成<script>标签
如果 6 位都拒绝,异常定义在 src/Exceptions/Untransformable.php 中的Untransformable会被抛出,开发阶段立刻暴露问题,而不是让前端拿到一段坏代码。
想扩展?加一个节点就行
这正是责任链模式的红利:假设你想支持DateTime类型,只需新建一个类实现Transformer接口(canTransform判断instanceof DateTime,transform输出new Date('...')),再把它加进 Renderer 的$transformers数组——原有 6 个转换器一行都不用动。测试代码 tests/BladeTest.php 展示了如何断言转换结果,可作为扩展时的参考。
小结:小而美的设计教科书
laravel-blade-javascript 只有十几行核心代码,却把责任链模式用得恰到好处:
- ✅单一职责:6 个 Transformer,一个文件一类值,各司其职
- ✅统一契约:
canTransform+transform两方法接口,扩展零门槛 - ✅链路兜底:
Untransformable异常让"无法转换"变得可见、可排查 - ✅安全内建:StringTransformer 的转义逻辑堵住 XSS 后路
如果你正在写自己的 Laravel 包,不妨看看 src/ 下的这份源码——它证明了:经典设计模式哪怕用在一个小工具里,也能让代码结构清晰得令人愉悦 ✨
【免费下载链接】laravel-blade-javascriptA Blade directive to export variables to JavaScript项目地址: https://gitcode.com/gh_mirrors/la/laravel-blade-javascript
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考