- 开发工具
- 代码质量
- 静态分析
- Lint
- 格式化
【免费下载链接】PHP-CS-Fixer
A tool to automatically fix PHP Coding Standards issues
导读
encoding是 PHP-CS-Fixer 中一条基础且高频触发的编码规则,它强制 PHP 源文件必须使用「无 BOM 的 UTF-8」编码,并在修复过程中自动剥离文件头部的 UTF-8 BOM 字节序列(EF BB BF)。本文以 规则文档 为核心骨架,结合 EncodingFixer 源码 与 官方测试用例,讲清该规则的判定逻辑、执行优先级、规则集归属与实战用法,读完即可在自己的项目中独立配置与验证。
规则定义:PHP 代码必须使用无 BOM 的 UTF-8
规则文档给出的定义只有一句话,但语义非常明确:
PHP code MUST use only UTF-8 without BOM (remove BOM).
即:PHP 代码只允许使用不带 BOM 的 UTF-8 编码,若检测到 BOM 则将其移除。这里的 MUST 对应 PSR-1 规范第 2.2 节的要求,规则文档与源码注释均明确标注了这一点(见 EncodingFixer 源码 中的Fixer for rules defined in PSR1 ¶2.2注释)。
文档中的标准示例展示了规则的效果——同样一行<?php,带 BOM 的文件在修复后 BOM 被移除,而echo "Hello!";等正文内容原样保留:
--- Original +++ New -<?php +<?php echo "Hello!";实现原理:EncodingFixer 如何定位并剥离 BOM
BOM 的字节本质
BOM(Byte Order Mark,字节序标记)在 UTF-8 编码下是固定的 3 个字节:0xEF 0xBB 0xBF。在 EncodingFixer 构造函数 中,这一字节序列通过pack('CCC', 0xEF, 0xBB, 0xBF)生成并保存在私有属性$bom中,供后续比对使用。
核心修复逻辑
applyFix方法(EncodingFixer.php)的实现非常轻量,分三步完成 BOM 剥离:
- 取出首个 Token 的内容:
$content = $tokens[0]->getContent();。由于 BOM 必然出现在文件最开头,而文件开头会被 Tokenizer 并入第一个 Token,因此只需检查第一个 Token 即可,无需扫描整个文件。 - 比对 BOM 前缀:用
str_starts_with($content, $this->bom)判断该 Token 内容是否以EF BB BF开头。 - 裁剪或清除:若命中,则用
substr($content, 3)去掉前 3 个字节:- 如果去掉后内容为空(即文件里只有 BOM 没有其他内容),则调用
$tokens->clearAt(0)直接清除该 Token; - 否则用裁剪后的内容构造新 Token 替换原 Token(
$tokens[0] = new Token([$tokens[0]->getId(), $newContent])),Token 的类型 ID 保持不变,仅内容被净化。
- 如果去掉后内容为空(即文件里只有 BOM 没有其他内容),则调用
全文件候选与最高执行优先级
源码中还有两个值得注意的设计点:
isCandidate()恒返回true(EncodingFixer.php):每个 PHP 文件都可能是候选,因为 BOM 检测与文件内容本身无关。getPriority()返回100(EncodingFixer.php):注释明确说明"必须最先运行(至少要在使用 Tokens 的 Fixer 之前),以提升整个修复流程的速度"。优先级数值越大越先执行,100是该项目中的最高优先级,确保 BOM 在其余规则读取 Token 内容之前就被清除,避免脏字节干扰后续规则对首个 Token 的处理。
测试用例:官方支持的三种典型场景
EncodingFixerTest 定义了该规则被官方支持的全部行为,每个用例都属于项目的向后兼容承诺范围。测试通过provideFixCases数据提供器驱动doTest($expected, $input, $file)完成「输入 → 期望输出」的比对:
| 场景 | 输入 | 期望输出 | 覆盖点 |
|---|---|---|---|
| BOM 位于文件首部 | test-utf8.case1-bom.php(以EF BB BF开头) | test-utf8.case1.php | 最常见的"文件开头带 BOM"情形 |
| BOM 前还有其他文本 | test-utf8.case2-bom.php(BOM 出现在文件中间,如abc之后) | test-utf8.case2.php | BOM 不在绝对文件头、被并入非首 Token 的情形 |
| 无 BOM 输入 | 无(input为null) | <?php原样保留 | 不含 BOM 的文件不做任何改动 |
第二个场景尤其有价值:它验证了修复逻辑并非只盯着文件第一个字节,而是检查每一个 Token 的内容是否以 BOM 开头——只要任意 Token 内容带有 BOM 前缀都会被清理。以 test-utf8.case2.php 为例,输入文件内容为abc\n<?php\necho 'ą';,其中abc文本先于<?php出现,BOM 字节便落在这个文本 Token 中而非首 Token,测试确保此类文件同样能被正确修复,且正文中的多字节 UTF-8 字符(如ą)不受影响。
规则集归属:一条覆盖所有主流风格集的编码基线
规则文档列出了该规则所属的全部规则集,按文档内链接可整理为(相对路径已转换为仓库根目录视角):
- PER 系列:
@PER(已弃用)、@PER-CS、@PER-CS1.0(已弃用)、@PER-CS1x0、@PER-CS2.0(已弃用)、@PER-CS2x0、@PER-CS3.0(已弃用)、@PER-CS3x0 - PSR 系列:
@PSR1、@PSR2、@PSR12 - 综合风格集:
@PhpCsFixer、@Symfony
从源码看,encoding的直接定义点在 PSR1Set::getRules() 中('encoding' => true),其余规则集通过规则集继承链间接引入:
- PSR2Set 包含
'@PSR1' => true; - PSR12Set 包含
'@PSR2' => true; - PERCS1x0Set 包含
'@PSR12' => true,而 PERSet 仅是@PER-CS的别名(已弃用,官方建议改用稳定的@PER-CS3.0); - 各版本化 PER-CS 集(
@PER-CS1.0、@PER-CS2.0、@PER-CS3.0等)则通过AbstractMajorMinorDeprecationSetDefinition代理到对应的x0命名集。
由此可以推断:任何基于 PSR 或 PER 风格的项目,encoding都会默认启用,这也解释了为什么它是几乎所有 PHP 项目在接入 PHP-CS-Fixer 后最先被触发的规则之一。
实战用法:如何启用、验证与规避 BOM
单独启用该规则
如果只想启用这一条规则,在配置文件(如.php-cs-fixer.php)中指定即可:
<?php // .php-cs-fixer.php return (new PhpCsFixer\Config()) ->setRules([ 'encoding' => true, ]) ->setFinder(PhpCsFixer\Finder::in(__DIR__));也可以直接在命令行通过--rules参数临时指定:
php php-cs-fixer fix --rules=encoding /path/to/src执行fix后,所有以 BOM 开头的 PHP 文件都会在文件首部被自动去除 3 个字节,配合--diff参数可查看具体改动:
php php-cs-fixer fix --rules=encoding --diff /path/to/src手动检查与规避
在 IDE 或编辑器中将「UTF-8 with BOM」改为「UTF-8」是最常见的规避手段;也可以用命令行快速检查仓库中是否存在 BOM 文件:
grep -rl $'\xEF\xBB\xBF' --include='*.php' .需要留意的是:encoding只处理 PHP 文件内的 BOM,若 BOM 出现在.php之外的资源文件(如.css、.js、.md)中,该规则不会介入,仍需通过编辑器或脚本处理。
小结
encoding规则的实现与测试都极其聚焦:检测以EF BB BF开头的 Token 内容并剥离前缀,通过最高优先级(100)保证在其余规则之前完成净化,同时以覆盖"文件首部 BOM""非首 Token 内 BOM""无 BOM"三种场景的测试锁定其向后兼容行为。由于它内嵌于 PSR-1 并被 PER、PSR、Symfony、PhpCsFixer 等全部主流规则集继承,几乎每个接入 PHP-CS-Fixer 的项目都会受益于此规则——从根源上杜绝因 BOM 引发的"文件头输出空白字符导致 header 已发送"一类运行时问题。若想进一步深入,可继续阅读 编码规则文档、Fixer 实现 与其 测试类。
- 开发工具
- 代码质量
- 静态分析
- Lint
- 格式化
【免费下载链接】PHP-CS-Fixer
A tool to automatically fix PHP Coding Standards issues
相关推荐
PHP-CS-Fixer 规则详解:phpdoc_no_access 移除 PHPDoc 中过时的 @access 注解
PHP CS Fixer 规则详解:phpdoc_no_access 移除 PHPDoc 中过时的 @access 注解 phpdoc_no_access 是
开发工具代码质量静态分析Lint格式化Feather框架:革命性同步Rust Web框架,告别async/await的复杂性
Feather框架:革命性同步Rust Web框架,告别async/await的复杂性 Feather框架是一个革命性的同步Rust Web框架,它通过创新的同
开发工具代码质量静态分析Lint格式化PHP-CS-Fixer @autoPHPMigration 规则集详解:按 composer.json 最低 PHP 版本自动应用迁移规则
PHP CS Fixer @autoPHPMigration 规则集详解:按 composer.json 最低 PHP 版本自动应用迁移规则 本文以 PHP C
开发工具代码质量静态分析Lint格式化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考