PHP-CS-Fixerphpdoc_types规则完全指南:统一 PHPDoc 标准类型的大小写
【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址: https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer
导读
本文围绕 PHP-CS-Fixer 中的phpdoc_types规则展开,介绍它如何在 PHPDoc 注释中强制使用 PHP 标准类型的正确大小写(如把STRING修正为string、inT修正为int),并深入讲解exclude与groups两个可配置选项、典型配置示例、底层实现原理以及它在@PhpCsFixer、@Symfony规则集中的地位。读完本文,你将掌握如何在项目中启用、定制和排查该规则,并能理解其与 TypeExpression、AbstractPhpdocTypesFixer 等源码组件的协作机制。
规则概述:为什么 PHPDoc 中的类型大小写很重要
phpdoc_types是 PHP-CS-Fixer 提供的 PHPDoc 类规则之一,其唯一职责是:PHPDoc 中的标准 PHP 类型必须使用正确的大小写。它并不改变类型语义,也不替换别名类型(那是phpdoc_scalar规则的工作),只专注于让string、int、bool、array、mixed、void等标准类型的书写规范统一。
源码中的定义位于 src/Fixer/Phpdoc/PhpdocTypesFixer.php:
public function getDefinition(): FixerDefinitionInterface { return new FixerDefinition( 'The correct case must be used for standard PHP types in PHPDoc.', ... ); }它继承自AbstractPhpdocTypesFixer(见 src/AbstractPhpdocTypesFixer.php),该抽象基类负责在 Tokenizer 层面扫描所有T_DOC_COMMENT,逐一解析注释中的注解并提取类型表达式,最终交由子类实现的具体normalize()逻辑完成大小写归一。
适用场景
phpdoc_types最典型的使用场景包括:
- 团队协作项目:不同开发者习惯书写
STRING、Bool、integer、Mixed等不同大小写风格,规则可一键统一; - 与静态分析工具配合:PHPStan、Psalm 等工具对 PHPDoc 类型解析更严格,统一的大小写能减少误报;
- 编码规范落地:作为
@Symfony、@PhpCsFixer规则集的组成部分,随规则集开箱即用。
支持的注解标签范围
该规则不只处理@param和@return。通过基类中的applyFix()实现(src/AbstractPhpdocTypesFixer.php)可以看到,它处理所有属于Annotation::TAGS_WITH_TYPES的注解。该常量定义于 src/DocBlock/Annotation.php,包括:
extends, implements, method, param, param-out, phpstan-import-type, phpstan-type, phpstan-var, property, property-read, property-write, psalm-import-type, psalm-type, psalm-var, return, throws, type, var也就是说,@var、@property、@method、@throws、@phpstan-type等注解中的类型同样会被检查和修正。
配置选项
phpdoc_types是CONFIGURABLE(可配置)规则,支持exclude与groups两个选项。其配置解析逻辑在 src/Fixer/Phpdoc/PhpdocTypesFixer.php 的createConfigurationDefinition()中定义。
exclude
- 类型:
string[](字符串数组) - 作用:从待修复类型中排除指定类型,无论其属于哪个分组
- 允许值:
['$this', 'array', 'bool', 'boolean', 'callable', 'double', 'false', 'float', 'int', 'integer', 'iterable', 'mixed', 'null', 'object', 'parent', 'resource', 'scalar', 'self', 'static', 'string', 'true', 'void']的子集 - 默认值:
[](不排除任何类型)
// 示例:不修复 resource 类型 ->setRules([ 'phpdoc_types' => ['exclude' => ['resource']], ])groups
- 类型:
string[](字符串数组) - 作用:指定要修复的类型分组
- 允许值:
['alias', 'meta', 'simple']的子集 - 默认值:
['alias', 'meta', 'simple'](全部分组)
类型分组定义于 src/Fixer/Phpdoc/PhpdocTypesFixer.php 的POSSIBLE_TYPES常量:
| 分组 | 包含类型 | 说明 |
|---|---|---|
alias | boolean、double、integer | 传统别名,规范大小写后保持原样(不替换为bool等) |
meta | $this、false、mixed、parent、resource、scalar、self、static、true、void | 语义型/伪类型 |
simple | array、bool、callable、float、int、iterable、null、object、string | PHP 原生简单类型 |
// 示例:只修复 simple 与 alias 分组 ->setRules([ 'phpdoc_types' => ['groups' => ['simple', 'alias']], ])注意:
groups只决定哪些分组参与修复;属于选定分组但大小写已经正确的类型不会被改动。
配置示例与预期效果
原文档(doc/rules/phpdoc/phpdoc_types.rst)给出了三个可复现的示例,以下逐一说明。
示例 1:默认配置
默认配置(groups与exclude均使用默认值)下,@param STRING|String[] $bar与@return inT[]会被修正为:
/** - * @param STRING|String[] $bar + * @param string|string[] $bar * - * @return inT[] + * @return int[] */注意String[]中作为数组元素类型的String同样被修正,说明规则会深入到复合类型内部。
示例 2:['groups' => ['simple', 'alias']]
当只启用simple与alias分组时:
/** - * @param BOOL $foo + * @param bool $foo * * @return MIXED */BOOL属于simple分组被修正为bool;而MIXED属于meta分组,因该分组未启用而保持原样。
示例 3:['exclude' => ['resource']]
当排除了resource类型时:
/** * @param Resource $foo * - * @return VOID + * @return void */Resource因被exclude排除而保持原样;VOID属于meta分组仍被修正为void。测试用例 tests/Fixer/Phpdoc/PhpdocTypesFixerTest.php 也明确验证了exclude resource preserves Resource casing的行为。
在实际配置文件中启用
在项目的.php-cs-fixer.dist.php配置文件中,可以按需组合使用。完整示例可参考 doc/config.rst:
<?php $finder = (new PhpCsFixer\Finder()) ->in(__DIR__) ; return (new PhpCsFixer\Config()) ->setRules([ // 方式一:直接使用规则集(推荐,规则默认开启) '@Symfony' => true, // 方式二:单独启用并定制 'phpdoc_types' => ['groups' => ['simple', 'alias'], 'exclude' => ['resource']], ]) ->setFinder($finder) ;提示:若使用
@Symfony或@PhpCsFixer规则集,phpdoc_types已默认启用,无需再单独声明;只有需要覆盖默认行为时才显式配置。
底层实现原理
1. 候选判定
基类 src/AbstractPhpdocTypesFixer.php 通过isCandidate()检查文件 Token 流中是否存在T_DOC_COMMENT:
public function isCandidate(Tokens $tokens): bool { return $tokens->isTokenKindFound(\T_DOC_COMMENT); }2. 注解解析与类型提取
applyFix()遍历所有文档注释,使用DocBlock与Annotation类解析出所有带类型的注解,然后对每个注解调用fixType(),最终通过TypeExpression的类型表达式树逐层处理。完整链路为:
Tokenizer Tokens → DocBlock → Annotation::getTypeExpression() → TypeExpression::mapTypes() → 子类 normalize() → 回写 Token其中TypeExpression::mapTypes()(src/DocBlock/TypeExpression.php)会递归遍历类型表达式中的每个子类型,并对变更做精确的字符串替换,保证不误伤其他字符。
3. 大小写归一策略
PhpdocTypesFixer::normalize()(src/Fixer/Phpdoc/PhpdocTypesFixer.php)是核心逻辑:
protected function normalize(string $type): string { $typeExpression = new TypeExpression($type, null, []); $newTypeExpression = $typeExpression->mapTypes(function (TypeExpression $type) { if ($type->isUnionType()) { return $type; } $value = $type->toString(); $valueLower = strtolower($value); if (isset($this->typesSetToFix[$valueLower])) { return new TypeExpression($valueLower, null, []); } return $type; }); return $newTypeExpression->toString(); }关键点:
- 先将类型字符串转小写,再去查
typesSetToFix哈希表(由configurePostNormalisation()依据groups合并、再剔除exclude后构建,见 src/Fixer/Phpdoc/PhpdocTypesFixer.php),因此无论原大小写如何都能命中; - 只对标准类型进行归一:类名如
Foo、Callback、命名空间类如DoNotChangeThisAsThisIsAClass不会被改动(测试用例"Callback" class in phpdoc must not be lowered验证了这一点,见 tests/Fixer/Phpdoc/PhpdocTypesFixerTest.php); - 联合类型(union)外层不直接转换,而是递归进入内部各成员分别处理,例如
SELF|Array|Foo中只修正SELF与Array。
4. 优先级设计
getPriority()返回16(src/Fixer/Phpdoc/PhpdocTypesFixer.php),并声明:
- 必须早于
phpdoc_scalar、phpdoc_align、phpdoc_types_order、phpdoc_no_empty_return、phpdoc_to_param_type等大量 PHPDoc 相关规则运行; - 必须晚于
phpdoc_indent运行。
原因在于:类型大小写的改变可能影响参数对齐(phpdoc_align)与别名替换(phpdoc_scalar)等后续规则的输入,而phpdoc_indent需要先完成缩进,因此该规则被安排在 PHPDoc 修复管线中较早的位置执行。
支持的语法形态(基于测试用例)
官方测试 tests/Fixer/Phpdoc/PhpdocTypesFixerTest.php 覆盖了大量现代 PHPDoc 语法,这些行为都属于向后兼容承诺的一部分:
| 语法形态 | 示例 | 行为 |
|---|---|---|
| 可空类型 | @return ?inT→@return ?int | 修正?后的类型 |
| 嵌套数组 | @return INT[][][]→@return int[][][] | 递归修正 |
| 泛型 | @param ARRAY<INT, OBJECT>→@param array<int, object> | 修正泛型内部类型;Foo\Int\Bar这类命名空间类名不动 |
| callable 签名 | @param CALLABLE(BOOL, INT): FLOAT→@param callable(bool, int): float | 修正参数与返回值类型 |
| 数组形状(shape) | @return array{FOO: BOOL, ...} | 修正值类型;键名FOO不视为类型 |
| 字符串字面量类型 | 'NULL'、"NULL"保持不动,裸NULL修正为null | 区分字面量与类型名 |
| 方法名与类型同名 | @method bool BOOL(): void | 返回类型修正,方法名BOOL不动 |
| 行内文档 | @param array $stuffs { @var Bool $foo } | 递归修正嵌套注解 |
| 多行数组泛型 | 跨行的array< INT, STRING > | 修正跨行类型 |
| 窗口换行(CRLF) | 含\r\n的注释 | 正常修正 |
同时,无效配置会被拒绝:['groups' => ['__TEST__']]与['exclude' => ['__INVALID__']]都会抛出InvalidFixerConfigurationException(见 tests/Fixer/Phpdoc/PhpdocTypesFixerTest.php),错误信息形如[phpdoc_types] Invalid configuration: The option "groups" ...。
规则集归属
phpdoc_types是以下官方规则集的组成部分:
@PhpCsFixer(见 doc/ruleSets/PhpCsFixer.rst)@Symfony(见 doc/ruleSets/Symfony.rst)
这意味着启用@Symfony或@PhpCsFixer的项目无需额外配置即可获得该规则的自动修复能力。
与其他 PHPDoc 规则的关系
在 PHP-CS-Fixer 的 PHPDoc 规则生态中,phpdoc_types只负责"大小写",与其他规则分工明确:
phpdoc_scalar:负责别名替换(如boolean→bool、integer→int、double→float),运行于phpdoc_types之后;phpdoc_types_order:负责联合类型排序(如null置后);phpdoc_align:负责@param等注解的对齐,依赖已修正的类型宽度。
正因如此,phpdoc_types被设计为 PHPDoc 修复管线中最早执行的规则之一(优先级 16),确保后续规则基于规范化的类型输入工作。
参考链接
- 规则文档:doc/rules/phpdoc/phpdoc_types.rst
- Fixer 实现:src/Fixer/Phpdoc/PhpdocTypesFixer.php
- 抽象基类:src/AbstractPhpdocTypesFixer.php
- 类型表达式解析器:src/DocBlock/TypeExpression.php
- 注解标签常量:src/DocBlock/Annotation.php
- 官方测试:tests/Fixer/Phpdoc/PhpdocTypesFixerTest.php
- 配置文件指南:doc/config.rst
【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址: https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考