☰
Twig 的 sort 过滤器:序列与映射排序的完整实战指南
2026/9/26 7:30:44 网站建设 项目流程
  • 后端

【免费下载链接】Twig

Twig, the flexible, fast, and secure template language for PHP

项目地址:https://gitcode.com/gh_mirrors/tw/Twig
点击查看免费下载

本文围绕 Twig 模板语言(PHP 模板引擎)内置的sort过滤器展开,讲解它如何对序列(sequence)与映射(mapping)进行排序、底层对 PHPasort/uasort的调用机制,以及如何通过箭头函数 + 太空船操作符实现自定义排序。读完本文,你将能在 Twig 模板中熟练完成基础排序、对象属性排序与映射键值保持等实战场景。

一、sort过滤器是什么

sort是 Twig 核心扩展(CoreExtension)内置的数组辅助过滤器,用于对序列与映射进行排序。它定义于 src/Extension/CoreExtension.php,注册时声明了needs_environment与needs_is_sandboxed两个选项,说明该过滤器需要环境对象与沙箱状态作为运行时参数。

最基础的用法是直接对一个数组(或可迭代对象)排序:

{% for user in users|sort %} ... {% endfor %}

排序后,users中的元素会按从小到大的顺序参与循环。从源码看,sort的签名是:

public static function sort(Environment $env, bool $isSandboxed, $array, $arrow = null): array

其定义于 src/Extension/CoreExtension.php,实现逻辑分为三步:

  1. 输入归一化:若$array是\Traversable(如\ArrayObject、生成器等),先通过iterator_to_array()转为数组;
  2. 类型校验:若转换后仍不是数组,则抛出RuntimeError,错误信息为The "sort" filter expects a sequence or a mapping, got "%s".(%s为实际类型);
  3. 执行排序:有箭头函数时使用uasort(),否则使用asort(),最后返回排序后的数组。

二、内部实现:为什么是asort而不是sort

原文档明确说明:内部 Twig 使用 PHP 的asort函数来保持索引关联(index association)。这一点在源码中得到印证——无箭头函数分支调用的是asort($array)(src/Extension/CoreExtension.php)。

asort与sort的关键区别在于:sort()会重建数组索引(从 0 开始重新编号),而asort()会保留原始键名。因此:

  • 对序列(如[4, 1])排序后,键仍为0, 1,只是顺序变为[1, 4];
  • 对映射(如['a' => 4, 'b' => 1])排序后,键'a'、'b'依然保留,仅值顺序改变,可用于后续按键取值的场景。

Traversable 支持:由于实现中先执行iterator_to_array(),任何实现Traversable接口的对象(如\ArrayObject)都能直接传入sort。仓库中的集成测试 tests/Fixtures/filters/sort.test 验证了这一行为:

{{ array1|sort|join }} {{ array2|sort|join }} {{ traversable|sort|join }}

测试数据中traversable为new \ArrayObject([0 => 3, 1 => 2, 2 => 1]),期望输出为123——证明ArrayObject被正确转换为数组并完成升序排序。

三、传入箭头函数进行自定义排序

仅靠asort的默认升序无法满足对象排序、倒序、多字段比较等需求,因此sort接受一个可选的arrow(箭头函数)参数:

{% set fruits = [ {name: 'Apples', quantity: 5}, {name: 'Oranges', quantity: 2}, {name: 'Grapes', quantity: 4}, ] %} {% for fruit in fruits|sort((a, b) => a.quantity <=> b.quantity)|column('name') %} {{ fruit }} {% endfor %} {# output in this order: Oranges, Grapes, Apples #}

这里有几个要点:

  • 箭头函数(a, b) => ...接收两个元素,返回负数、0 或正数分别表示a小于、等于、大于b;
  • 示例中使用了**太空船操作符(spaceship operator,<=>)**来简化比较:a.quantity <=> b.quantity天然返回上述三种结果之一;
  • column('name')是另一个数组辅助过滤器,用于从排序后的结果中提取每个元素的name字段,得到['Oranges', 'Grapes', 'Apples'](其用法可参考 doc/filters/column.rst)。

带有箭头函数的完整模板测试见 tests/Fixtures/filters/sort_with_arrow.test,其中同时验证了传统三元比较与太空船操作符两种写法,二者输出一致:

{{ fruits|sort((a, b) => a.quantity == b.quantity ? 0 : (a.quantity > b.quantity ? 1 : -1))|column('name')|join(', ') }} {{ fruits|sort((a, b) => a.quantity <=> b.quantity)|column('name')|join(', ') }}

两条模板的期望输出均为Oranges, Grapes, Apples。

原理:传入箭头函数后,底层改用uasort($array, $arrow)(src/Extension/CoreExtension.php)。uasort同样保留键名,且将你的比较函数作为排序回调交给 PHP 内部排序算法执行。

四、沙箱模式下的约束与废弃行为

由于sort注册了needs_is_sandboxed,其实现会调用checkArrow()(src/Extension/CoreExtension.php)对箭头函数做额外校验:

  • 沙箱模式下:传入的必须是 PHP 原生\Closure,否则抛出RuntimeError:The callable passed to the "sort" filter must be a Closure in sandbox mode.这是出于安全考虑——沙箱环境不允许执行任意可调用对象;
  • 非沙箱模式:自 Twig 3.15 起,向sort等过滤器传入非\Closure的可调用参数会触发弃用警告(deprecation),Twig 4 将不再支持。

因此在新代码中,建议始终使用 Twig 箭头函数语法(编译后即生成\Closure),这是兼容当前版本与未来版本的推荐写法。

五、常见错误与排查

sort只接受数组或Traversable对象。若传入标量(如字符串、数字)或null,会抛出:

The "sort" filter expects a sequence or a mapping, got "string".

此时应检查模板变量来源,确保其是数组或实现了Traversable。若数据来自数据库查询对象(ORM 集合),确认其是否实现了Traversable——多数集合类满足该条件,可直接排序;否则需先转换为数组。

六、小结

  • sort可对序列与映射排序,内部使用asort保持键名关联,支持Traversable自动转数组;
  • 传入箭头函数后改用uasort,可配合太空船操作符<=>实现简洁的自定义排序;
  • 沙箱模式下箭头函数必须是\Closure,非沙箱传非闭包自 Twig 3.15 起废弃;
  • 配套的column过滤器可提取排序结果中的指定字段,二者常组合使用。

相关实现与测试文件:核心实现见 src/Extension/CoreExtension.php,过滤器注册见 src/Extension/CoreExtension.php,集成测试见 tests/Fixtures/filters/sort.test 与 tests/Fixtures/filters/sort_with_arrow.test。

  • 后端

【免费下载链接】Twig

Twig, the flexible, fast, and secure template language for PHP

项目地址:https://gitcode.com/gh_mirrors/tw/Twig
点击查看免费下载
上一篇:零代码搞定邮件智能分流:Postal路由引擎3步配置主题/发件人规则
下一篇:无需复杂代码!WebGL流体模拟中的光影魔法:打造超真实流体效果

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询