尧图精选

PHP-CS-Fixer 的 return_to_yield_from 规则:将 iterable 函数的 return 数组自动改写为 yield from

🕒 发布时间:2026/9/23 20:56:59 📁 来源:尧图网络
PHP-CS-Fixer 的 return_to_yield_from 规则将 iterable 函数的 return 数组自动改写为 yield from【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址: https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer导读return_to_yield_from是 PHP-CS-Fixer 中ArrayNotation数组符号类别下的一条自动修复规则当函数显式返回一个数组字面量、且函数声明的返回类型为iterable时规则会将return [...]改写为yield from [...]从而把函数真正转换为惰性求值的生成器Generator。本文将以 官方规则文档 为骨架结合 规则源码 与 单元测试 的实证完整讲解该规则的触发条件、改写逻辑、边界情况、与其他规则的协作方式以及实际启用方法帮助你安全地将它纳入日常代码风格治理流程。规则概述何时触发、改写什么按照 doc/rules/array_notation/return_to_yield_from.rst 的定义该规则的核心约束只有一条如果函数显式返回一个数组且返回类型声明为iterable那么必须使用yield from代替return。换句话说它处理的是声明自己是iterable却仍然用return一次性返回整个数组的写法。这类写法虽然语法上合法但违背了iterable类型所暗示的惰性、可遍历语义调用方拿到的其实是一个完整数组而非按需生成的生成器。官方示例文档给出的唯一官方示例是一个最小可复现的 diff--- Original New ?php function giveMeData(): iterable { - return [1, 2, 3]; yield from [1, 2, 3]; }改写前后的行为对调用方基本透明foreach遍历结果一致但函数内部从一次性构造并返回数组变为惰性产出元素内存占用与启动开销更优尤其是在数据量较大的场景下。源码级实现剖析改写发生在哪里规则的实际逻辑位于 src/Fixer/ArrayNotation/ReturnToYieldFromFixer.php 中的ReturnToYieldFromFixer类它继承自AbstractFixer。核心工作分两步1. 候选预筛isCandidatepublic function isCandidate(Tokens $tokens): bool { return $tokens-isAllTokenKindsFound([\T_FUNCTION, \T_RETURN]) $tokens-isAnyTokenKindsFound([\T_ARRAY, CT::T_ARRAY_BRACKET_OPEN]); }对应 源码 L49-L52候选条件是文件中同时存在函数T_FUNCTION与returnT_RETURN关键字并且出现过数组字面量——无论是array(...)旧语法T_ARRAY还是[...]短数组语法CT::T_ARRAY_BRACKET_OPEN。不满足该条件的文件会被直接跳过这是性能层面的第一道过滤。2. 逐个改写 returnapplyFixprotected function applyFix(\SplFileInfo $file, Tokens $tokens): void { foreach ($tokens-findGivenKind(\T_RETURN) as $index $token) { if (!$this-shouldBeFixed($tokens, $index)) { continue; } $tokens[$index] new Token([\T_YIELD_FROM, yield from]); } }对应 源码 L65-L74对于每个returntoken先交给私有方法shouldBeFixed()做严格的上下文校验只有全部通过才会把该 token 原地替换为yield from。整个过程不会重排代码只做最小化的 token 替换。shouldBeFixed 的五重校验精确理解触发条件shouldBeFixed()对应 源码 L76-L112是整条规则的核心它按顺序执行以下检查return 后面紧跟着数组字面量取return的下一个有意义 token必须是array关键字或[数组括号。因此return $z;、return foo();这类返回变量或调用结果的写法不会被触碰。该数组是整个函数体的最后一条语句定位数组的结束 token 后继续向后扫描期间只允许出现分号;空语句之后必须直接遇到函数体的结束大括号}。这意味着函数体内如果在return之后还有别的语句则不会改写。函数返回类型必须紧邻函数体大括号从结束大括号向前取前一个有意义的 token作为返回类型标识符T_STRING。返回类型必须是iterable将该标识符小写化后与iterable比较源码 L105。因此ITERABLE、Iterable等大小写变体同样命中而array、MyAwesomeIterableType等自定义类型不会命中。该返回类型必须由类型冒号CT::T_TYPE_COLON引入即必须是显式的: iterable返回类型声明排除了返回类型省略或经由 docblock 等间接途径声明的情况。只有同时满足上述五条return [ ... ];才会被改写为yield from [ ... ];。为什么需要这些严格约束从 单元测试 的provideFixCases数据提供器可以清楚看到这些约束的边界测试中只给expected、不给input的用例表示输入本身已合规、不应改动无返回类型不处理?php function foo() { return [1, 2, 3]; }保持原样返回类型缺失。返回类型为array不处理function foo(): array { return []; }保持不变——array类型函数本就应该返回数组。自定义类型不处理function foo(): MyAwesomeIterableType { return [1, 2, 3]; }保持不变。函数体内多语句function foo(): iterable { $x 0; return [1, 2, 3]; }会被改写为$x 0; yield from [1, 2, 3];return前的赋值语句不影响但return必须是函数体最后一条实质语句。返回数组可以是旧语法return array(1, 2, 3);同样支持改写为yield from array(1, 2, 3);。匿名函数同样适用$f function(): iterable { return [1, 2, 3]; };会被改写而顶层脚本中的普通return [1, 2, 3];不在函数体内不受影响。类方法同样适用class Foo { function bar(): iterable { return [1, 2, 3]; } }改写为yield from。不会改写的特殊情况可空类型?iterablefunction foo(): ?iterable { return [1, 2, 3]; }保持不变。PHP 8.0 联合类型null|iterable、iterable|null、ITERABLE|null、Bar|iterable均保持不变见provideFix80Cases要求 PHP 8.0。从源码结构看这是因为shouldBeFixed()只检查返回类型前一个 token 是否为?之外的单个标识符联合类型场景下紧邻大括号的 token 并非纯iterable标识符。if/else内部分支返回function foo(): iterable { if (true) { return [1]; } else { return [2]; } }保持不变——数组后紧跟的是}之前还有别的结构不满足最后一条语句约束。抽象方法声明abstract public function bar(): iterable;无函数体自然不处理。优先级与规则协作getPriority 的调度语义ReturnToYieldFromFixer通过getPriority()返回优先级1源码 L60-L63并在注释中明确了执行顺序约束Must run beforeyield_from_array_to_yieldsMust run afterphp_unit_data_provider_return_type、phpdoc_to_return_type。这一顺序在 tests/Fixtures/Integration/priority/ 下的集成测试中被逐一验证与 yield_from_array_to_yields 串联把数组彻底打散yield_from_array_to_yields是一条 risky 规则会把yield from [数组]展开为逐条yield。集成测试 return_to_yield_from,yield_from_array_to_yields.test 展示了完整链路// 输入 function foo(): iterable { return [ 1, 2, 3, ]; } // 依次经过 return_to_yield_from - yield_from_array_to_yields 后 function foo(): iterable { yield 1; yield 2; yield 3; }与 phpdoc_to_return_type 串联补全类型后再改写集成测试 phpdoc_to_return_type,return_to_yield_from.test 说明phpdoc_to_return_type先把 docblock 中的return iterable提升为显式返回类型return_to_yield_from再完成改写// 输入 /** * return iterable */ function foo() { return [1, 2, 3]; } // 输出 /** * return iterable */ function foo(): iterable { yield from [1, 2, 3]; }与 php_unit_data_provider_return_type 串联数据提供器的迭代化集成测试 php_unit_data_provider_return_type,return_to_yield_from.test 展示了 PHPUnit 数据提供器场景php_unit_data_provider_return_type为 data provider 方法补充: iterable返回类型后return_to_yield_from将其return [[1], [2], [3]];改写为yield from [[1], [2], [3]];使数据提供器变为生成器——这正是 PHPUnit 官方推荐的 data provider 写法。如何启用与运行该规则属于非 risky 规则官方文档中未标注 risky可安全加入常规配置。但需要注意从 规则集目录 的搜索结果看该规则默认不包含在Symfony、PhpCsFixer等内置规则集内相关内置规则集文档见 doc/ruleSets需要显式启用。在项目的.php-cs-fixer.php配置文件中启用?php $finder PhpCsFixer\Finder::create() -in(__DIR__ . /src); return (new PhpCsFixer\Config()) -setRules([ return_to_yield_from true, ]) -setFinder($finder);命令行方式--rules支持 JSON 语法php php-cs-fixer fix path/to/file.php --rules{return_to_yield_from: true}完整规则清单可在 doc/rules/index.rst其中 Array Notation 一节即包含本条规则见 index.rst L63查阅。向后兼容性承诺规则文档末尾明确指出测试类定义了官方支持的行为每个测试用例都是向后兼容性承诺backward compatibility promise的一部分。这意味着 tests/Fixer/ArrayNotation/ReturnToYieldFromFixerTest.php 中provideFixCases与provideFix80Cases枚举的全部输入/输出对都是本项目保证长期稳定的行为契约。任何后续版本对触发条件、改写结果或边界情况的调整都必须先通过这些用例。对于使用者而言只要你的代码形态落在上述测试覆盖范围内就可以放心依赖该规则的行为而任何不在测试覆盖内的形态如联合类型、可空类型从兼容性承诺的角度也不应假设会被改写。实践建议适用场景重构遗留代码中声明iterable却return数组的函数让惰性求值名副其实配合php_unit_data_provider_return_type使用可自动把 PHPUnit data provider 转换为生成器写法。注意事项改写为生成器后函数变为惰性执行——如果调用方依赖调用函数时立即计算全部数据或对返回值做count()、is_array()等数组专属操作需要先确认调用方兼容遍历语义后再启用。风险提示如需进一步将yield from [...]展开为逐条yield需同时启用 risky 规则yield_from_array_to_yields并确保项目接受其行为变更。【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址: https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →