PHP-CS-Fixer `lowercase_keywords` 规则深度解析:强制 PHP 关键字小写的原理与实战
2026/9/23 1:16:31 网站建设 项目流程
  • 开发工具
  • 代码质量
  • 静态分析
  • Lint
  • 格式化

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

A tool to automatically fix PHP Coding Standards issues

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

lowercase_keywords是 PHP-CS-Fixer 在casing(大小写)分类下提供的基础性代码风格规则,其职责是保证代码中出现的 PHP 语言关键字一律使用小写形式。本文以 规则文档 为主体,结合仓库内 Fixer 实现、Tokenizer 底层能力与测试用例,讲解该规则的作用范围、修复原理、边界行为以及在@PSR12@Symfony等主流规则集中的应用方式,帮助读者理解并安全地使用这条规则。

规则核心语义:PHP 关键字必须小写

该规则的定义(FixerDefinition)非常明确:

PHP keywords MUST be in lower case.

即:代码中所有 PHP 关键字必须使用小写形式。规则注释中说明它对应 PSR-2 规范的 §2.5 小节(Fixer for rules defined in PSR2 ¶2.5,见 LowercaseKeywordsFixer.php),因此它是 PSR-2/PSR-12 风格体系中对关键字大小写的强制性约束。

需要注意,PHP 语言本身对关键字是大小写不敏感的(FOREACHForeachforeach均可被解释器接受),但这会导致同一代码库中出现五花八门的写法,破坏可读性与一致性。lowercase_keywords的目的就是消除这种随意性,统一为规范的小写形式。

官方示例

规则文档给出了如下标准示例(来源:lowercase_keywords.rst),可以看到修复前后的完整差异:

--- Original +++ New <?php - FOREACH($a AS $B) { - TRY { - NEW $C($a, ISSET($B)); - WHILE($B) { - INCLUDE "test.php"; + foreach($a as $B) { + try { + new $C($a, isset($B)); + while($B) { + include "test.php"; } - } CATCH(\Exception $e) { - EXIT(1); + } catch(\Exception $e) { + exit(1); } }

示例中FOREACHASTRYNEWISSETWHILEINCLUDECATCHEXIT等关键字全部被转换为小写;而变量$a$B$C不受影响(它们不是关键字),\Exception类名中的大写E也保持不变。这一点揭示了该规则的一个重要边界:它只改写关键字本身,绝不触碰标识符、类名、变量名和字符串内容

底层实现:一行代码完成的关键字归一化

LowercaseKeywordsFixerAbstractFixer的子类(LowercaseKeywordsFixer.php),整个修复逻辑非常精炼,分为两个阶段。

候选判定(isCandidate):快速过滤无关文件

public function isCandidate(Tokens $tokens): bool { return $tokens->isAnyTokenKindsFound(Token::getKeywords()); }

在 AbstractFixer::fix 的统一调用链中,只有当文件 token 流里确实存在关键字时,才会进入实际修复流程。这种"候选判定 + 实际修复"的两段式设计是 PHP-CS-Fixer 所有 Fixer 的通用范式:先用开销极低的全量扫描做快速过滤,避免对不含关键字的文件做无谓的遍历。

实际修复(applyFix):逐 token 小写化

protected function applyFix(\SplFileInfo $file, Tokens $tokens): void { foreach ($tokens as $index => $token) { if ($token->isKeyword() && !$token->isGivenKind(\T_HALT_COMPILER)) { $tokens[$index] = new Token([$token->getId(), strtolower($token->getContent())]); } } }

实现要点有三:

  1. 基于 token 语义而非文本匹配:规则遍历整个 token 流,通过$token->isKeyword()判断是否为关键字。这意味着它工作在内核分词结果之上,而非简单的字符串替换,因此天然不会误伤字符串字面量、注释或普通标识符中的大写单词。
  2. 保留 token 类型:修复时构造新Token时沿用$token->getId()(token 类型 ID 不变),仅通过strtolower()改写内容,保证后续规则与语法分析仍然正确。
  3. 显式豁免T_HALT_COMPILER__HALT_COMPILER();是 PHP 中用于生成 Phar 档案的特殊结构。测试用例yield ['<?php __HALT_COMPILER();'];(LowercaseKeywordsFixerTest.php)表明该结构保持原样,不参与小写化。从实现看,这是修复循环中唯一的显式例外分支,属于刻意保留的向后兼容行为。

关键字清单:从 Tokenizer 源码看覆盖范围

isKeyword()的实现位于 Token.php,其判定依赖Token::getKeywords()返回的关键字 ID 集合。从 getKeywords() 的源码可以看到,这个集合分为两大类:

  • 原生 token 关键字T_ABSTRACTT_ARRAYT_AST_BREAKT_CALLABLET_CASET_CATCHT_CLASST_CLONET_CONSTT_CONTINUET_DECLARET_DEFAULTT_DOT_ECHOT_ELSET_ELSEIFT_EMPTYT_ENDDECLARET_ENDFORT_ENDFOREACHT_ENDIFT_ENDSWITCHT_ENDWHILET_EVALT_EXITT_EXTENDST_FINALT_FINALLYT_FNT_FORT_FOREACHT_FUNCTIONT_GLOBALT_GOTOT_HALT_COMPILERT_IFT_IMPLEMENTST_INCLUDET_INCLUDE_ONCET_INSTANCEOFT_INSTEADOFT_INTERFACET_ISSETT_LISTT_LOGICAL_ANDT_LOGICAL_ORT_LOGICAL_XORT_NAMESPACET_NEWT_PRINTT_PRIVATET_PROTECTEDT_PUBLICT_REQUIRET_REQUIRE_ONCET_RETURNT_STATICT_SWITCHT_THROWT_TRAITT_TRYT_UNSETT_USET_VART_WHILET_YIELDT_YIELD_FROM
  • 自定义兼容 token(CT/FCT)CT::T_ARRAY_TYPEHINTCT::T_CLASS_CONSTANTCT::T_CONST_IMPORTCT::T_CONSTRUCTOR_PROPERTY_PROMOTION_*CT::T_FUNCTION_IMPORTCT::T_NAMESPACE_OPERATORCT::T_USE_LAMBDACT::T_USE_TRAIT,以及FCT::T_ENUMFCT::T_MATCHFCT::T_READONLYFCT::T_PRIVATE_SETFCT::T_PROTECTED_SETFCT::T_PUBLIC_SET

其中FCT(Forward Compatibility Tokens,FCT.php)是 PHP-CS-Fixer 为了兼容旧版 PHP 运行环境而引入的"前向兼容 token":当运行环境不支持某 token 时,用负整数值代替原生 token 常量(例如 PHP 8.0 之前的T_MATCH记为-802)。这意味着lowercase_keywords的覆盖范围会随运行环境的 PHP 版本动态扩展——例如在 PHP 8.1+ 环境下运行时会覆盖enumreadonly,在 PHP 8.4+ 环境下会覆盖非对称可见性private(set)等,而无需升级规则本身。

同时需要说明边界:truefalsenull等属于原生常量(isNativeConstant 所辖范畴),魔术常量(__CLASS____DIR____FILE__等,见 getMagicConstants)也不在isKeyword()的判定范围内,它们分别由constant_casemagic_constant_casing等其他 Casing 规则负责。

行为契约:测试用例揭示的官方支持范围

规则文档明确声明(lowercase_keywords.rst):

The test class defines officially supported behaviour. Each test case is a part of our backward compatibility promise.

即测试类是官方支持行为的定义者,每个用例都属于向后兼容承诺的一部分。阅读 LowercaseKeywordsFixerTest.php 可以精确掌握该规则的边界行为:

测试场景输入(节选)输出说明
逻辑运算符(1 AND 2)(1 and 2)AND属于T_LOGICAL_AND
循环与控制流FOREACH(... AS $val) {}foreach(... as $val) {}关键字与变量名分离处理
字符串内容echo "GOOD AS NEW";原样保留字符串内的"关键字"不受影响
类常量访问X::ClASsX::class混合大小写也会被归一
箭头函数FN() => truefn() => truePHP 7.4+ 的fn
Phar 结构__HALT_COMPILER();原样保留唯一显式豁免
match 表达式(PHP 8.0+)MATCH (1) {...}match (1) {...}覆盖T_MATCH
构造器属性提升(PHP 8.0+)PUBLIC float $xpublic float $x提升可见性关键字
readonly(PHP 8.1+)public READONLY string $proppublic readonly string $proprEADONLY等混合写法
enum(PHP 8.1+)ENUM Suit {...}enum Suit {...}覆盖FCT::T_ENUM
非对称可见性(PHP 8.4+)PUBLIC(SET) Bar $apublic(set) Bar $a覆盖FCT::T_PUBLIC_SET

这些用例按 PHP 版本分层(#[RequiresPhp]注解与@requires标注),充分说明该规则对较新语言特性同样提供关键字小写化支持,且行为随运行版本自适应。

规则集传播链:一条规则如何进入主流风格体系

lowercase_keywords没有配置项(属于无参规则,直接启用即可),它主要通过规则集(Rule Set)被引入。规则文档列出了它所属的全部规则集(lowercase_keywords.rst),结合 src/RuleSet/Sets 目录下的集合定义源码,可以还原出完整的传播链:

  • @PSR2:在 PSR2Set.php 中直接启用'lowercase_keywords' => true,这是该规则的源头;
  • @PSR12:通过'@PSR2' => true继承(PSR12Set.php);
  • @PER-CS1x0:通过'@PSR12' => true继承(PERCS1x0Set.php);
  • @PER-CS2x0:通过'@PER-CS1x0' => true继承(PERCS2x0Set.php);
  • @PER-CS3x0:通过'@PER-CS2x0' => true继承(PERCS3x0Set.php);
  • @Symfony:通过'@PER-CS3x0' => true继承(SymfonySet.php);
  • @PhpCsFixer:通过'@Symfony' => true继承(PhpCsFixerSet.php)。

此外文档还列出@PER@PER-CS1.0@PER-CS2.0@PER-CS3.0四个标记为(deprecated)的旧命名规则集,对应新的@PER-CS@PER-CS1x0@PER-CS2x0@PER-CS3x0命名体系(如 PERSet.php 实现了DeprecatedRuleSetDefinitionInterface)。

由此可以看出:只要项目使用了@PSR2及以上任何一级主流规则集,就会自动获得关键字小写化的保障。各规则集对应的完整规则清单可查阅 doc/ruleSets 目录(如 PSR2.rst、PSR12.rst、Symfony.rst)。

实战使用:如何启用与验证

命令行方式

无需修改任何配置文件,直接通过命令行指定规则运行(以仓库根目录下的 php-cs-fixer 可执行入口为例):

php php-cs-fixer fix path/to/your/file.php --rules=lowercase_keywords

配合--dry-run--diff可以先预览将要发生的修改而不落盘:

php php-cs-fixer fix path/to/your/dir --rules=lowercase_keywords --dry-run --diff

配置文件方式

.php-cs-fixer.php(或.php-cs-fixer.dist.php)配置文件中,既可以单独启用,也可以借助规则集批量启用:

<?php return (new PhpCsFixer\Config()) ->setRules([ 'lowercase_keywords' => true, // 单独启用 // 或直接使用 '@PSR12' => true 等规则集整体引入 ]) ->setFinder(PhpCsFixer\Finder::create()->in(__DIR__));

运行测试验证行为

仓库为每条规则配备了完整的单元测试。针对本规则,可运行以下命令验证其官方支持行为:

php vendor/bin/phpunit tests/Fixer/Casing/LowercaseKeywordsFixerTest.php

测试通过即代表当前 PHP 环境下规则行为符合官方向后兼容承诺。需要注意的是,matchreadonlyenum、非对称可见性等用例带有 PHP 版本要求注解,低版本环境下这些用例会被自动跳过,这是预期行为而非测试失败。

与其他 Casing 规则的协同

lowercase_keywords属于 src/Fixer/Casing 目录下的 10 个大小写类规则之一,它与同族的其他规则各司其职、互不重叠:

  • constant_case:负责true/false/null等原生常量的写法归一;
  • magic_constant_casing:负责__CLASS____DIR__等魔术常量的大小写;
  • native_function_casingnative_type_declaration_casing:分别处理内置函数名与内置类型声明的大小写;
  • class_reference_name_casinglowercase_static_reference:处理类引用与self/static/parent等静态引用;
  • integer_literal_casemagic_method_casing:处理整数字面量与魔术方法名。

在实际项目中,通常无需单独操心该规则——@PSR12@Symfony等规则集已将其纳入,配合上述同族规则即可实现完整的"大小写一致性"治理。

小结

lowercase_keywords是 PHP-CS-Fixer 中最基础也最安全的规则之一:它以 token 语义而非文本匹配为核心,将全部 PHP 关键字(含fnmatchreadonlyenumset可见性等新特性关键字)归一为小写,同时严格排除字符串、注释、标识符与__HALT_COMPILER结构。通过@PSR2向上逐级传播,它覆盖了@PSR12@PER-CS*@Symfony@PhpCsFixer等主流规则集,是任何 PHP 项目代码风格基线中不可或缺的一环。

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

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

A tool to automatically fix PHP Coding Standards issues

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

相关推荐

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

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

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

立即咨询