PHP-CS-Fixer 的 native_type_declaration_casing 规则:原生类型声明大小写规范化实战指南
2026/9/23 1:38:46 网站建设 项目流程
  • 开发工具
  • 代码质量
  • 静态分析
  • Lint
  • 格式化

【免费下载链接】PHP-CS-Fixer

A tool to automatically fix PHP Coding Standards issues

项目地址:https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer
点击查看免费下载

本文围绕 PHP-CS-Fixer 中的native_type_declaration_casing规则展开,深入讲解它如何将INTCALLABLEBOOL等大小写不规范的原生类型声明统一为小写形式,覆盖可修复的场景、被刻意排除的边界情况、底层 Token 处理原理,以及如何在@Symfony/@PhpCsFixer规则集中启用它。读完本文,你将能够在实际项目中安全地启用该规则,并理解它为什么不会误伤类名、常量名与命名空间片段。

规则概述:什么是 native_type_declaration_casing

native_type_declaration_casing是 PHP-CS-Fixer 中位于 Casing(大小写)分类下的一个内置规则,官方定义为:原生类型声明(Native type declarations)应使用正确的大小写("Native type declarations should be used in the correct case.")。

PHP 是大小写不敏感的语言,因此下面两种写法在运行时完全等价:

function Foo(INT $bar): VOID {} // 可以运行,但风格混乱 function foo(int $bar): void {} // 符合主流编码规范

该规则的作用就是扫描代码中的类型声明位置,把所有大写的原生类型关键字统一改写成小写,从而让代码风格保持一致。它对应源码为 NativeTypeDeclarationCasingFixer.php,测试覆盖见 NativeTypeDeclarationCasingFixerTest.php。

需要特别强调的是:这里只处理原生(内置)类型,例如intstringarray等。你自定义的类名、接口名、命名空间别名等不在处理范围内——它们通常需要遵循PascalCase等其他约定,不应被强行小写。

官方文档示例:两种典型场景

规则文档(native_type_declaration_casing.rst)给出了两个 diff 形式的示例,直观展示了修复前后变化。

示例一:函数参数与返回类型

--- Original +++ New <?php class Bar { - public function Foo(CALLABLE $bar): INT + public function Foo(callable $bar): int { return 1; } }

这里同时处理了参数类型CALLABLE和返回类型INT,分别改为callableint

示例二:类常量类型声明(PHP 8.3+)

--- Original +++ New <?php class Foo { - const INT BAR = 1; + const int BAR = 1; }

PHP 8.3 起支持为类常量声明类型,该规则同样会将其中的原生类型关键字规范化。

支持修复的类型清单:按 PHP 版本演进

从源码 NativeTypeDeclarationCasingFixer.php 可以看到,规则内部维护了一个$types集合,只有命中该集合的类型才会被改写。集合内容与 PHP 版本强相关,完整清单如下:

类型关键字引入版本说明
selfPHP 5.0指向当前类
arrayPHP 5.1数组
callablePHP 5.4可调用类型
bool/float/int/stringPHP 7.0标量类型
iterable/voidPHP 7.1可迭代 / 无返回值
objectPHP 7.2对象类型
staticPHP 8.0仅作返回类型
mixedPHP 8.0混合类型
false/nullPHP 8.0联合返回类型中可用
neverPHP 8.1仅作返回类型
truePHP 8.2独立类型(true-type RFC)
false/nullPHP 8.2独立类型(RFC)

代码中的判定逻辑是:

$this->types = [ 'array' => true, 'bool' => true, 'callable' => true, 'float' => true, 'int' => true, 'iterable' => true, 'object' => true, 'parent' => true, 'self' => true, 'static' => true, 'string' => true, 'void' => true, ]; if (\PHP_VERSION_ID >= 8_00_00) { /* false, mixed, null */ } if (\PHP_VERSION_ID >= 8_01_00) { $this->types['never'] = true; } if (\PHP_VERSION_ID >= 8_02_00) { $this->types['true'] = true; }

可以看到,falsenull等关键字在低版本 PHP 中不是类型而是普通标识符,因此规则会根据当前运行环境(PHP_VERSION_ID)动态决定是否把它们视为类型。例如在 PHP 8.0 以下环境中,MIXED会被当作普通类名而保持不变——测试类中的testFixPre80用例(NativeTypeDeclarationCasingFixerTest.php)专门验证了这一点:PHP < 8.0 时private MIXED $m;不会被改写。

实际修复效果与边界场景

结合测试类 NativeTypeDeclarationCasingFixerTest.php 中的大量用例,可以归纳出规则的实际行为边界。先看它会修复的场景:

// 函数参数与返回类型 function Foo(BOOL $a, FLOAT $b, INT $c, STRING $d): INT {} // 修复为:function Foo(bool $a, float $b, int $c, string $d): int {} // 可空类型 ?type function Foo(?INT $A): VOID {} // 修复为:function Foo(?int $A): void {} // 类属性类型 class Foo { private BOOL $c = false; } // 修复为:class Foo { private bool $c = false; } // 联合类型(PHP 8.0+) function foo(INT|BOOL $x): INT|BOOL {} // 修复为:function foo(int|bool $x): int|bool {} // 交叉类型、析取范式(DNF)类型 private (A&B)|INT|D $d5; // INT 被修复为 int,(A&B) 中的类名不受影响 // 构造器属性提升(promoted properties) public function __construct(public INT $i, ...) {} // 修复为:public function __construct(public int $i, ...) {} // 闭包与箭头函数 return fn (CALLABLE $c): INT => 1; // 修复为:return fn (callable $c): int => 1; // readonly 属性(PHP 8.1+) private readonly ARRAY $ax; // 修复为 private readonly array $ax; // 类常量类型声明(PHP 8.3+) const INT SOME_INT = 3; // 修复为 const int SOME_INT = 3;

再看它不会修复的场景,这些正是避免误伤的关键:

  • 类名与命名空间片段INTEGERFoo\INT\BString\A等不会被改写,因为INTEGER不是原生类型关键字,且A\INT\B中的INT是命名空间段;
  • 常量名称const INT = "A";INT是常量名而非类型,保持不变;
  • 属性名private $INT = 1;INT是属性名,保持不变;
  • 动态属性访问$this->Object->doBar();Object是属性名,保持不变;
  • switch case 与比较表达式case True:True === $x等中True/False是常量字面量,不属于类型声明,保持不变;
  • 全局常量:类外的const INT = "A";不会被修改。

底层实现原理:如何精准定位"类型声明"位置

从源码结构看,规则通过三层防线避免误伤,这也是理解该规则稳健性的关键。

第一步:isCandidate 快速筛选

isCandidate()(源码)在遍历 Token 前先做一次快速判断,只有在以下情况才进入正式修复流程:

  • 存在T_FUNCTIONT_FN(普通函数、方法、闭包、箭头函数);
  • 或存在类/接口/trait/enum 关键字(Token::getClassyTokenKinds())且同时存在T_STRING
  • 或 PHP >= 8.3 时存在T_CONST且上下文中有类声明(对应类常量类型声明场景)。

这个设计保证了绝大多数无关文件会被直接跳过,提升整体处理性能。

第二步:类型白名单校验

applyFix()(源码)遍历所有 Token,对每个 Token 执行strtolower后检查是否命中$this->types白名单。大小写已经正确的 Token 直接跳过($content === $lowercaseContentcontinue)。

第三步:前后 Token 语境判定

这是最关键的一步。即使命中了白名单,还要检查该 Token 的前一个有意义 Token(prev)后一个有意义 Token(next)

  • 如果前一个是=T_CASET_OBJECT_OPERATOR->)、T_DOUBLE_COLON::)、T_NS_SEPARATOR\),则跳过——这说明当前 Token 很可能是常量名、属性名或命名空间段;
  • 如果后一个是=T_NS_SEPARATOR,同样跳过——const INT = 1Foo\INT这类场景被排除;
  • 只有前一个是T_CONSTCT::T_NULLABLE_TYPE?)、CT::T_TYPE_ALTERNATION|)、CT::T_TYPE_COLON:返回类型分隔符),或者后一个是T_VARIABLE$a)、CT::T_TYPE_ALTERNATION|)时,才确认当前 Token 确实处于类型声明位置,执行小写替换。

这套语境判定正是规则"不误伤"的核心保障。测试类中大量"do not fix"用例(如public const ?INT\A X=C;INT保持不动、const ?BAR B = null;BAR保持不动)验证了这些边界条件(测试用例)。

如何启用该规则

native_type_declaration_casing没有配置选项(非可配置规则),启用方式只有"开启或关闭"两种。

方式一:通过规则集启用

根据文档与规则集源码,该规则属于以下两个内置规则集:

  • @Symfony:在 SymfonySet.php 中以'native_type_declaration_casing' => true显式启用,对应规则集文档 Symfony.rst;
  • @PhpCsFixerPhpCsFixerSet组合了@Symfony等规则集,因此同样包含该规则。

.php-cs-fixer.php配置文件中直接使用规则集即可:

<?php return (new PhpCsFixer\Config()) ->setRules([ '@Symfony' => true, ]) ;

方式二:单独启用

如果不想引入整个规则集,可以只开启这一条规则:

<?php return (new PhpCsFixer\Config()) ->setRules([ 'native_type_declaration_casing' => true, ]) ;

运行命令

# 检查模式下查看哪些文件会被修改 php php-cs-fixer fix --dry-run --diff --rules=native_type_declaration_casing . # 直接应用修复 php php-cs-fixer fix --rules=native_type_declaration_casing .

注意事项与兼容性提示

  1. 运行环境决定能力边界:规则会根据当前 PHP 版本动态启用falsenullmixednevertrue等类型的修复。例如在 PHP 8.0 环境运行,never(PHP 8.1)与true(PHP 8.2)不会被识别为类型关键字,相关代码会保持原样;类常量类型声明的修复(const INT FOO = 6;)仅在 PHP >= 8.3 时生效(对应VersionSpecification(8_03_00)版本限定示例,见 源码)。
  2. native_function_casing的区别native_function_casing处理的是函数调用(如STRLEN改为strlen),而本规则只处理类型声明,两者作用域不同。
  3. 类常量类型场景的兼容性:PHP 8.3 之前类常量不支持类型声明,因此示例二中的代码在旧版本上是语法错误——该规则只是将写法规范化为小写,并不会改变 PHP 版本的兼容性边界。
  4. 向后兼容承诺:官方在规则文档末尾明确指出,测试类(NativeTypeDeclarationCasingFixerTest.php)中定义的每个测试用例都是官方支持的正式行为,属于向后兼容承诺的一部分。如果你的代码依赖规则对某个边界场景的处理,可以参考该测试类确认预期行为。

小结

native_type_declaration_casing是一个简单但覆盖面很广的规范化规则:它将参数类型、返回类型、属性类型、联合/交叉类型、可空类型以及 PHP 8.3 类常量类型中的原生类型关键字统一为小写,同时通过精确的 Token 语境判定,确保类名、常量名、属性名和命名空间段完全不受影响。配合@Symfony@PhpCsFixer规则集使用,可以低成本地消除代码库中类型声明大小写混用的风格问题。

  • 开发工具
  • 代码质量
  • 静态分析
  • Lint
  • 格式化

【免费下载链接】PHP-CS-Fixer

A tool to automatically fix PHP Coding Standards issues

项目地址:https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询