PHP-CS-Fixer 规则详解:no_whitespace_in_empty_array 清理空数组中的空白
【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址: https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer
导读
no_whitespace_in_empty_array是 PHP-CS-Fixer(PHP Coding Standards Fixer)中ArrayNotation(数组记法)类别下的一条轻量级代码风格规则,用于把"只包含空白符的空数组"(如$foo = [\n];)自动压缩为紧凑形式$foo = [];。本文基于本仓库的官方规则文档,结合规则源码、单元测试与规则集配置,完整讲解该规则的行为边界、启用方式、底层实现原理及其与其他规则的协作关系,读完即可在项目中安全落地这条规则。
规则概述
规则全名:no_whitespace_in_empty_array。
官方定义(FixerDefinition,见 NoWhitespaceInEmptyArrayFixer.php):
Empty arrays should not contain only whitespace.
即:空数组内不应仅包含空白字符。当数组字面量[]中没有任何元素、只有空格、换行等空白时,规则会将其折叠为单行紧凑形式[]。
该规则属于"非 Risky"规则——它只做纯格式层面的空白移除,不涉及任何语义转换,因此可以安全地加入默认规则集。
修复示例
官方文档给出的标准示例(CodeSample):
--- Original +++ New <?php -$foo = [ -]; +$foo = [];修复前,空数组占用了三行,括号间仅有一个换行符;修复后压缩为一行$foo = [];。
结合 NoWhitespaceInEmptyArrayFixerTest.php 中的测试用例,可以确认该规则覆盖的更多形态:
| 输入(修复前) | 输出(修复后) | 说明 |
|---|---|---|
$foo = [\n]; | $foo = []; | 括号间仅一个换行 |
$foo = [ ]; | $foo = []; | 括号间仅水平空格 |
$foo = [\n\n]; | $foo = []; | 括号间多个空行 |
$foo = [\n \n]; | $foo = []; | 括号间仅含空白行 |
private const Foo = [\n]; | private const Foo = []; | 类常量场景 |
$foo = [ ];(方法体内) | $foo = []; | 方法体赋值场景 |
public array $ignore = [\n]; | public array $ignore = []; | 类型化属性默认值场景 |
注意:括号之间仅由空白(空格、制表符、换行)组成时才会被压缩。若括号间存在注释或其他有效内容,规则会跳过。
行为边界:注释与内容会被保留
该规则不会删除"看似空白"的注释,也不会删除任何真实元素。测试用例明确验证了以下两种场景不会被修复:
// 场景一:括号间只有一行注释(含 `// foo`) $foo = [ // foo ]; // 场景二:括号间有块注释 $foo = [ /* test */ ];这两类输入在测试中作为expected(期望输出)出现,即原样保留。原理很简单:实现只清空"括号之间唯一的那一个空白 token",一旦中间存在注释 token,getPrevNonWhitespace($index) !== $index - 2的判断就不成立,直接跳过。
此外,混合场景也能一次修复多处:
// 修复前 $a = [ ]; $b = [ ]; $c = []; $d = [ /**/ ]; $e = [ ]; $f = [ ]; // 修复后 $a = []; $b = []; $c = []; $d = [ /**/ ]; // 含块注释,保持原样 $e = []; $f = [];启用方式
方式一:命令行临时指定
只针对单条规则执行修复:
vendor/bin/php-cs-fixer fix /path/to/project \ --rules='{"no_whitespace_in_empty_array": true}'--rules接受 JSON 格式的规则配置,true表示启用该规则(默认参数为空,无需额外配置项)。
方式二:配置文件.php-cs-fixer.php
在项目根目录的配置文件(或--config指定的文件)中启用:
<?php // .php-cs-fixer.php $finder = PhpCsFixer\Finder::create() ->in(__DIR__ . '/src') ->in(__DIR__ . '/tests'); return (new PhpCsFixer\Config()) ->setRules([ 'no_whitespace_in_empty_array' => true, ]) ->setFinder($finder);方式三:随官方规则集一键开启
该规则是@PhpCsFixer规则集的成员,见 PhpCsFixerSet.php 中的'no_whitespace_in_empty_array' => true。@PhpCsFixer是 PHP CS Fixer 团队推荐的、高度主观(highly opinionated)的规则集,扩展自@PER-CS与@Symfony。启用方式:
vendor/bin/php-cs-fixer fix --rules=@PhpCsFixer或在配置文件中使用:
->setRules(['@PhpCsFixer' => true])该规则集的完整说明见 doc/ruleSets/PhpCsFixer.rst,其中第 45 行列出了指向本规则文档的索引链接。
源码实现原理
候选检测(isCandidate)
public function isCandidate(Tokens $tokens): bool { return $tokens->isTokenKindFound(CT::T_ARRAY_BRACKET_OPEN); }规则会先扫描整个 token 流,只有当文件里存在"数组方括号开括号"的 CT token 时才进入修复流程,否则直接跳过,保证性能。该常量定义于 CT.php:CT::T_ARRAY_BRACKET_OPEN = 10_004,对应的CT::T_ARRAY_BRACKET_CLOSE = 10_003。
为什么用自定义的CT::T_ARRAY_BRACKET_OPEN而不是原生的T_ARRAY?因为 PHP 原生 tokenizer 对[]短数组语法不会产生独立的括号 token(它们被吞并进T_ARRAY附近的空白处理中),PHP-CS-Fixer 在 Tokenizer/Transformer 阶段会把方括号统一改写成这些带语义的 CT 常量,从而让各规则能精确、稳定地定位数组括号。这是整个ArrayNotation家族规则的共同基础。
修复逻辑(applyFix)
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void { for ($index = \count($tokens) - 1; $index > 0; --$index) { if (!$tokens[$index]->isGivenKind(CT::T_ARRAY_BRACKET_CLOSE)) { continue; } if ($tokens->getPrevNonWhitespace($index) !== $index - 2) { continue; } if (!$tokens[$index - 2]->isGivenKind(CT::T_ARRAY_BRACKET_OPEN)) { continue; } $tokens->clearAt($index - 1); } }核心思路可以拆成四步:
- 从后向前遍历token 流,先找到每一个"数组方括号闭括号"(
CT::T_ARRAY_BRACKET_CLOSE)。从尾部开始遍历是 PHP-CS-Fixer 的通用惯例,因为清除 token 会改变索引,倒序遍历可以安全地在同一轮内处理多个候选; - 验证括号位置相邻:
getPrevNonWhitespace($index)返回闭括号前最近的非空白 token 索引,只有当它等于$index - 2时,才说明"开括号与闭括号之间只有一个 token"——这个 token 就是空白; - 验证开括号存在:确认
$index - 2位置确实是CT::T_ARRAY_BRACKET_OPEN,避免误伤其它括号配对; - 清除空白 token:
$tokens->clearAt($index - 1)把夹在括号之间的唯一空白 token 标记为清除,最终得到[]。
优先级与执行顺序(getPriority)
/** * {@inheritdoc} * * Must run after ArraySyntaxFixer, NoEmptyCommentFixer. */ public function getPriority(): int { return 0; }getPriority()返回0(默认优先级),但源码注释明确要求必须在ArraySyntaxFixer和NoEmptyCommentFixer之后运行。原因可以通过仓库中的集成测试(tests/Fixtures/Integration/priority)得到印证:
- array_syntax,no_whitespace_in_empty_array.test:输入
array(\n\n );需先由array_syntax转成短数组[,随后本规则才能识别并压缩; - no_empty_comment,no_whitespace_in_empty_array.test:输入
[\n //\n]中括号间是一个空注释,先由no_empty_comment删除空注释,剩下来的纯空白再由本规则折叠。
这两条集成测试以--RULESET--+--INPUT--+--EXPECT--三段式断言了"多规则串联"的最终输出,是理解 fixer 协作关系的直观材料。
官方测试与向后兼容承诺
官方文档特别强调(见 no_whitespace_in_empty_array.rst):
The test class defines officially supported behaviour. Each test case is a part of our backward compatibility promise.
即 NoWhitespaceInEmptyArrayFixerTest.php 中的每个用例都属于 PHP-CS-Fixer 的向后兼容承诺——测试里定义的行为(包括"修复什么"和"不修复什么")在后续版本中都会保持稳定,不会随意改变。这也是官方文档为每条规则都附上 Fixer 类与测试类链接的原因:
- Fixer 类:PhpCsFixer\Fixer\ArrayNotation\NoWhitespaceInEmptyArrayFixer
- 测试类:PhpCsFixer\Tests\Fixer\ArrayNotation\NoWhitespaceInEmptyArrayFixerTest
测试类继承自AbstractFixerTestCase,通过@dataProvider provideFixCases批量驱动testFix($expected, $input),每个yield都是一组"输入 → 期望输出"对;当input为null时表示"已符合规范,不应被修改"。如果读者想本地复现,可运行:
vendor/bin/phpunit tests/Fixer/ArrayNotation/NoWhitespaceInEmptyArrayFixerTest.php实际使用建议
- 放心纳入 CI 与 pre-commit:该规则不涉及 Risky 操作,也不会改动注释与真实元素,适合作为
@PhpCsFixer或显式规则集的一部分在提交前/CI 中执行。 - 结合配套规则:
array_syntax(把array()转为[])与no_empty_comment(清除空注释)与本规则存在执行顺序依赖,三者同时开启时 PHP-CS-Fixer 会依据优先级自动编排,无需手动干预,最终效果是"空数组彻底干净"。 - 理解"非空即不动"原则:只要括号间有任何非空白 token(注释、元素),本规则一律跳过,因此不需要担心它误删注释或代码。
- 查阅更多数组类规则:本规则位于
ArrayNotation类别,同类规则还包括array_syntax、no_trailing_comma_in_singleline_array、trim_array_spaces、whitespace_after_comma_in_array等,完整清单见 doc/rules/array_notation 目录;命令行工具的整体用法可参考 doc/usage.rst 与 doc/config.rst。
小结
no_whitespace_in_empty_array虽然只解决一个很小的格式问题,但其实现体现了 PHP-CS-Fixer 的典型工程范式:通过 Tokenizer 的 CT 常量统一数组括号语义、以getPrevNonWhitespace做精确的相邻性判定、用clearAt最小化改动,再以"规则集 + 优先级注释 + 集成测试 + 单元测试"四层机制保障多规则协作下的行为稳定。理解了这条规则的实现与测试,也就掌握了阅读 PHP-CS-Fixer 任意一条空白类 fixer 源码的通用方法论。
【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址: https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考