- 后端
【免费下载链接】Twig
Twig, the flexible, fast, and secure template language for PHP
Twig 的shuffle滤镜自 3.11 版本引入,用于对序列(sequence)、映射(mapping)或字符串进行随机打乱,是模板层实现"随机展示、随机推荐、抽签式列表"等场景的内置工具。本文基于官方文档 shuffle 滤镜说明 展开,并结合 CoreExtension.php 的源码实现与集成测试用例,覆盖其完整用法、键丢失的陷阱、字符集处理机制与非法 UTF-8 的报错行为,帮助你在模板中正确、安全地使用该滤镜。
基本用法:对循环变量直接打乱
shuffle滤镜在 Twig 3.11 中添加,作用对象可以是序列、映射或字符串。最典型的用法是在for循环中打乱集合,例如随机展示用户列表:
{% for user in users|shuffle %} ... {% endfor %}需要注意的核心语义是:每次渲染模板都会产生一个新的随机顺序,且结果不保证稳定——同一个输入在多次渲染之间没有顺序承诺。
序列示例:打乱 list 并渲染列表
对纯序列使用时,滤镜返回的元素是打乱后的值,遍历顺序随之变化。文档给出的完整示例如下:
{% set items = [ 'a', 'b', 'c', ] %} <ul> {% for item in items|shuffle %} <li>{{ item }}</li> {% endfor %} </ul>上述示例的一次可能渲染结果为:
<ul> <li>a</li> <li>c</li> <li>b</li> </ul>文档明确指出,其余所有排列都是合法结果:"a, b, c" 或 "b, a, c" 或 "b, c, a" 或 "c, a, b" 或 "c, b, a"。这也说明了测试shuffle类功能的正确姿势:不要断言具体顺序,而是断言结果属于所有可能排列之一。
映射示例:键会被丢弃(重要陷阱)
shuffle文档中有一个明确的 caution 提示:打乱后的数组不保留键。如果输入原本是非顺序索引(例如用用户 ID 作为键),打乱之后这些键就不再存在,元素会被重新编号为0, 1, 2, ...。
文档示例 2 完整展示了这一行为:
{% set items = { 'a': 'd', 'b': 'e', 'c': 'f', } %} <ul> {% for index, item in items|shuffle %} <li>{{ index }} - {{ item }}</li> {% endfor %} </ul>一次可能的渲染结果为:
<ul> <li>0 - d</li> <li>1 - f</li> <li>2 - e</li> </ul>注意这里index已经变成了0、1、2,而不是原始的'a'、'b'、'c';值 "d, e, f" 的六种排列("d, e, f"、"e, d, f"、"e, f, d"、"f, d, e"、"f, e, d" 等)都可能出现。因此,如果你依赖键(比如用用户 ID 作为键并在循环内引用index),不要在打乱后继续使用原始键,而应把键放入值对象内部传递。
字符串示例:按字符打乱
对字符串使用时,shuffle会把字符串拆分为字符数组打乱后再拼接:
{% set string = 'ghi' %} <p>{{ string|shuffle }}</p>一次可能的渲染结果为:
<p>gih</p>其余五种排列("ghi"、"hgi"、"hig"、"igh"、"ihg")同样是合法输出。
源码剖析:注册方式与实现细节
滤镜注册:needs_charset选项
在 CoreExtension.php 中,shuffle的注册行为:
new TwigFilter('shuffle', [self::class, 'shuffle'], ['needs_charset' => true]),needs_charset => true表示调用实现方法时会自动注入当前环境的默认字符集(charset)作为第一个参数。这也是title、upper、lower、reverse、split等字符串类滤镜共有的选项(见 CoreExtension.php 的注册区)。正因为注入了字符集,shuffle才能正确区分多字节字符与字节。
核心实现:CoreExtension::shuffle()
实现位于 CoreExtension.php,签名是shuffle(string $charset, $item),按输入类型分三条路径:
字符串路径:
- 若环境字符集不是
UTF-8,先用iconv转换到 UTF-8(内部convertEncoding()在缺少iconv函数时会抛出RuntimeError,提示安装 ext-iconv 或 symfony/polyfill-iconv); - 调用
splitIntoCharacters()把字符串按字符(而非字节)拆分为数组; - 对字符数组执行 PHP 内置
shuffle(),再implode还原为字符串; - 若非 UTF-8 环境,最后转换回原字符集。
这条路径保证了多字节字符(如中文、带重音符号的拉丁字符)作为整体被打乱,而不会被拆碎成乱码字节。
- 若环境字符集不是
可迭代对象路径:对
array或Traversable,先经toArray($item, false)转为数组(注意第二个参数为false,即不保留键——这正是文档 caution 中"键不保留"的底层原因),然后调用 PHP 原生shuffle()。其他类型:原样返回
$item,不做打乱。
按字符拆分的工具方法与错误处理
splitIntoCharacters()定义在 CoreExtension.php,内部使用 Unicode 感知的正则preg_split('/(?<!^)(?!$)/u', $string),是str_split()的 Unicode 版本。当传入的字符串不是合法 UTF-8 时,preg_split失败,方法会抛出Twig\Error\RuntimeError,消息形如Unable to split the string passed to "shuffle" into characters: ...。
测试用例印证行为边界
仓库的集成测试对shuffle的行为做了系统验证:
- tests/Fixtures/filters/shuffle.test:分别覆盖字符串
'ok'、整数数组[3, 1]、字符串数组['foo', 'bar']、映射{'a': 'd', 'b': 'e'}以及\Traversable(ArrayObject)五类输入,断言方式正是"结果等于所有可能排列之一则输出 ok",与上文序列/映射示例的语义完全对应。其中映射用例{'a': 'd', 'b': 'e'}的期望值被断言为['d', 'e']或['e', 'd']——键'a'、'b'已被丢弃,直接印证了文档的键丢失警告。 - tests/Fixtures/filters/shuffle_invalid_utf8.test:传入非合法 UTF-8 字符串
"\xC3\x28abc",期望抛出Twig\Error\RuntimeError,消息为Unable to split the string passed to "shuffle" into characters: Malformed UTF-8 characters, possibly incorrectly encoded。这说明对字符串使用shuffle时,输入必须是合法 UTF-8,否则会在运行时(而非编译时)抛错。
shuffle的引入记录可追溯到 CHANGELOG 中的 "Add theshufflefilter" 条目。
使用建议与相关滤镜
- 需要稳定随机时:
shuffle的结果每次渲染都不同。若需要可复现的"随机"顺序(如分页场景),应在应用层打乱后传入模板,或自行基于固定种子的逻辑处理,不要在模板中依赖shuffle的稳定性。 - 需要保留键时:若循环逻辑依赖映射的原始键,
shuffle不适用;可先打乱值再重新组合,或在模板外处理。 - 与相邻能力配合:
shuffle与同为needs_charset的字符串类滤镜(reverse、split、first、last)及序列类滤镜(sort、slice)可自由组合,例如users|shuffle|first可取打乱后的第一个元素(仓库另提供了random函数用于取随机元素,可结合 CoreExtension.php 的注册处对比选择)。
小结
shuffle滤镜覆盖序列、映射与字符串三类输入,源码实现位于 CoreExtension.php,通过needs_charset注入字符集并按字符级拆分保证多字节安全;使用时必须牢记两点:打乱后的映射不保留原始键,以及字符串输入必须是合法 UTF-8(否则抛RuntimeError)。这些行为均有 shuffle.test 与 shuffle_invalid_utf8.test 测试用例佐证,可直接作为模板编写与自动化验证的参考基准。
- 后端
【免费下载链接】Twig
Twig, the flexible, fast, and secure template language for PHP
相关推荐
Twig reverse 过滤器深度解析:序列、映射与字符串反转的完整用法及源码实现
Twig reverse 过滤器深度解析:序列、映射与字符串反转的完整用法及源码实现 本文以 Twig 官方文档 doc/filters/reverse.rst
后端Twig `filter` 滤镜深度解析:用箭头函数过滤序列与映射
Twig filter 滤镜深度解析:用箭头函数过滤序列与映射 本文基于 Twig 官方文档 doc/filters/filter.rst https://li
后端yq 的 shuffle 操作符:用非加密安全随机打乱数组顺序
yq 的 shuffle 操作符:用非加密安全随机打乱数组顺序 shuffle 是 yq( pkg/yqlib/doc/operators/shuffle.md
开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考