ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PHP-CS-Fixer 的 no_alias_functions 规则:用主函数替换别名函数,避免已弃用 API

PHP-CS-Fixer 的 no_alias_functions 规则:用主函数替换别名函数,避免已弃用 API 开发工具代码质量静态分析Lint格式化【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer点击查看免费下载导读no_alias_functions是 PHP-CS-Fixer 中位于Alias别名命名空间下的一条可配置CONFIGURABLE且标记为 RISKY有风险的修复规则它会把 PHP 中一类同一功能、两个名字的**别名函数alias functions**统一替换为官方推荐的主函数master functions例如chop()→rtrim()、join()→implode()、sizeof()→count()、is_integer()→is_int()。本文以 no_alias_functions.rst 为核心结合 NoAliasFunctionsFixer.php 的实现与 NoAliasFunctionsFixerTest.php 的测试用例系统讲解该规则的设计意图、sets配置项的全部取值与默认行为、RISKY 警告的成因以及它如何被内置规则集如Symfony:risky、PHP8x0Migration:risky组合使用。读完本文你将能够按需定制修复范围并理解为什么调用别名函数这种看似无害的写法在现代化代码库中应当被消除。规则核心Master Functions Shall Be Used Instead of Aliases规则的定义只有一句话Master functions shall be used instead of aliases.应使用主函数而非别名函数见 NoAliasFunctionsFixer.php 中的getDefinition()。PHP 语言演进过程中保留了大量历史遗留的别名函数。它们功能与主函数完全等价但往往命名风格与 PHP 官方推荐不一致如chopvsrtrim、joinvsimplode可能已被标记为 deprecated如mbereg_*系列在ext-mbstring中已被mb_ereg_*取代在新版本中可能被移除例如 PHP 8.0 移除create_function()、each()等历史上许多别名函数也在逐步清理。no_alias_functions的作用就是在函数调用点把别名改写为主函数名让代码库统一使用主API从而获得更好的可维护性与向前兼容性。需要特别说明的是该规则只处理全局函数调用。它并不会修改方法名、类名、常量名、命名空间内的函数调用、字符串字面量中的函数名等这一点在后文实现原理与测试验证中会详细展开。风险提示为什么这条规则是 RISKY文档中明确标注This rule is RISKY— Risky when any of the alias functions are overridden.当任何别名函数被覆盖时该规则是有风险的。RISKY 的含义是修复结果可能改变代码的行为需要人工审查 diff 后才能合入。风险的具体场景是你或第三方库在代码中自定义了一个与别名函数同名的函数例如// 自定义了一个名为 chop() 的全局函数 function chop(string $s): string { return strrev($s); } echo chop(hello); // 期望调用自定义函数此时 PHP-CS-Fixer 无法区分这个chop()到底是你自定义的函数还是 PHP 内置别名函数从静态分析角度两者的调用语法完全一致。如果规则将其改写为rtrim(hello)就会改变程序的真实行为。因此使用该规则前请确认项目中没有覆盖内置别名函数建议在 CI 中先以--dry-run或--diff模式预览改动再合入与所有 risky 规则一样它默认不会被Symfony、PER-CS等非 risky 规则集启用必须显式引入见下文规则集集成。配置项sets详解no_alias_functions只有一个配置项sets一个字符串数组用于选择要修复的集合。每个集合对应一组 PHP 扩展或分类下的别名函数映射。全部可选值及含义文档中列出的集合如下与 createConfigurationDefinition() 中的描述一一对应集合名含义覆盖的典型别名函数all以下所有集合的总和全部别名映射internal原生core函数chop→rtrim、join→implode、sizeof→count、is_integer→is_int、key_exists→array_key_exists、show_source→highlight_file等exifEXIF 函数read_exif_data→exif_read_dataftpFTP 函数ftp_quit→ftp_closeIMAPIMAP 函数imap_header→imap_headerinfo、imap_create→imap_createmailbox等ldapLDAP 函数ldap_close→ldap_unbind、ldap_modify→ldap_mod_replacembreg来自ext-mbstring的正则函数mbereg→mb_ereg、mbereg_search_getregs→mb_ereg_search_getregs、mbsplit→mb_split等mysqlimysqli 函数mysqli_execute→mysqli_stmt_execute、mysqli_escape_string→mysqli_real_escape_stringocioci 函数oci_free_cursor→oci_free_statementodbcodbc 函数odbc_do→odbc_exec、odbc_field_precision→odbc_field_lenopensslopenssl 函数openssl_get_publickey→openssl_pkey_get_public等pcntlPCNTL 函数pcntl_errno→pcntl_get_last_errorpgpgPostgreSQL函数pg_exec→pg_queryposixPOSIX 函数posix_errno→posix_get_last_errorsnmpSNMP 函数当前为空集源码中标注TODO Remove on next major 4.0 as this set is now empty见 NoAliasFunctionsFixer.phpsodiumlibsodium 函数sodium_crypto_scalarmult_base→sodium_crypto_box_publickey_from_secretkeytime时间函数特殊集合mktime/gmmktime在零参数时改写为time()允许值[all, exif, ftp, IMAP, internal, ldap, mbreg, mysqli, oci, odbc, openssl, pcntl, pg, posix, snmp, sodium, time]的任意子集。配置校验由AllowedValueSubset完成见 FixerConfiguration/AllowedValueSubset.php传入列表之外的集合名会抛出InvalidFixerConfigurationException。默认值[internal, IMAP, pg]—— 即默认只修复原生核心函数、IMAP 函数、PostgreSQL 的pg_exec。如何在配置文件中使用在项目根目录的.php-cs-fixer.php或.php-cs-fixer.dist.php中启用?php $config new PhpCsFixer\Config(); $config-getRules() -addRule(no_alias_functions, [ sets [all], // 或 [internal, mbreg, time] 等自定义组合 ]); return $config;需要提醒的是由于该规则是 risky 的PHP-CS-Fixer 在运行时也会打印risky rule相关提示建议在 CI 中显式开启--allow-riskyyes才能让规则真正生效。行为演示默认配置与自定义配置文档给出了两个 diff 示例这里完整还原并补充说明。示例一默认配置[internal, IMAP, pg]原始代码?php $a chop($b); close($b); $a doubleval($b); $a fputs($b, $c); $a get_required_files(); ini_alter($b, $c); $a is_double($b); $a is_integer($b); $a is_long($b); $a is_real($b); $a is_writeable($b); $a join($glue, $pieces); $a key_exists($key, $array); magic_quotes_runtime($new_setting); $a pos($array); $a show_source($filename, true); $a sizeof($b); $a strchr($haystack, $needle); $a imap_header($imap_stream, 1); user_error($message); mbereg_search_getregs();修复后?php $a rtrim($b); closedir($b); $a floatval($b); $a fwrite($b, $c); $a get_included_files(); ini_set($b, $c); $a is_float($b); $a is_int($b); $a is_int($b); $a is_float($b); $a is_writable($b); $a implode($glue, $pieces); $a array_key_exists($key, $array); set_magic_quotes_runtime($new_setting); $a current($array); $a highlight_file($filename, true); $a count($b); $a strstr($haystack, $needle); $a imap_headerinfo($imap_stream, 1); trigger_error($message); mbereg_search_getregs(); // 未改动mbreg 不在默认集合内注意最后一行mbereg_search_getregs()属于mbreg集合而默认配置不含mbreg因此保持原样。示例二配置[sets [mbreg]]原始代码?php $a is_double($b); mbereg_search_getregs();修复后?php $a is_double($b); // 未改动internal 不在该配置内 mb_ereg_search_getregs();这两个示例精确对应 NoAliasFunctionsFixer.php 中的两个CodeSample也与测试数据provideFixCases()中的internal、IMAP、mbreg、all等用例一致见 NoAliasFunctionsFixerTest.php。实现原理从 Token 流到别名映射该规则的实现位于 NoAliasFunctionsFixer.php核心分为三部分。1. 静态别名映射表SETS私有常量SETSL52-L171以集合名 [别名 主函数]的形式硬编码了全部映射关系。大多数映射是别名 → 主函数名的字符串但有两个值得注意的特例time集合的mktime与gmmktime映射为[time, 0]这表示只有当调用参数个数为 0时才替换为time()。因为mktime()在传参时是有意义的按指定时间生成时间戳只有无参调用才是time()的别名snmp集合当前为空数组源码中通过TODO注明将在下一个大版本4.0移除该集合。2. 配置归一化合并集合configurePostNormalisation()L245-L260在配置校验后执行遍历sets若包含all则直接合并所有集合mergeSets(self::SETS)使用array_merge(...array_values($sets))拍平否则逐个array_merge指定集合。最终生成一个扁平的$this-aliases映射表供修复阶段使用。3. 修复阶段精确识别全局函数调用applyFix()L262-L300是核心逻辑按 Token 流处理遍历所有T_STRINGToken将其内容strtolower()后查表——因此规则对大小写不敏感测试中MKTIME()、ECHO GMMKTIME(...)也能被识别见 NoAliasFunctionsFixerTest.php确认该 Token 后紧跟(否则是常量、类名等跳过调用FunctionsAnalyzer::isGlobalFunctionCall()FunctionsAnalyzer.php判断它确实是全局函数调用而不是方法调用、静态调用、命名空间内函数、new表达式等若映射值是[主函数, 参数个数]形式如time集合再用ArgumentsAnalyzer::countArguments()核对参数数量全部通过后用new Token([T_STRING, $alias])原地替换函数名 Token。由于步骤 3 的严格判断以下写法都不会被误改这些场景在 provideAllCases() 中被逐一验证$obj-join($a); // 方法调用 Scope::is_long(); // 静态调用 new \join(); // new 表达式类名 namespace\is_long(); // 命名空间内函数 Smth\chop($a); // 命名空间内函数 SELECT join($a); // 字符串字面量 $fputs is_double(\$a);\n; // 字符串字面量优先级与协作getPriority()返回40L230-L233并注释Must run before ImplodeCallFixer, PhpUnitDedicateAssertFixer。这是因为join()会被本规则先改写成implode()随后implode_call规则才能基于implode()进一步统一调用方式。集成测试文件 tests/Fixtures/Integration/priority/no_alias_functions,implode_call.test 验证了这一协作// 输入 join($pieces, ); join($morePieces); // 输出 implode(, $pieces); implode(, $morePieces);规则集集成随迁移规则集自动生效文档Rule sets一节列出了该规则所属的规则集其中既有直接启用默认配置也有以[sets [all]]方式启用规则集配置PHP7x4Migration:risky直接启用默认配置见 PHP7x4MigrationRiskySet.phpPHP8x0Migration:risky[sets [all]]见 PHP8x0MigrationRiskySet.phpPHP8x1Migration:riskyPHP8x5Migration:risky[sets [all]]由PHP8x0Migration:risky继承而来PHP74Migration:risky直接启用已废弃deprecatedPHP80Migration:risky、PHP82Migration:risky[sets [all]]已废弃PhpCsFixer:risky[sets [all]]见 PhpCsFixerRiskySet.phpSymfony:risky直接启用默认配置见 SymfonyRiskySet.php对应文档可查看 PHP8x0MigrationRisky.rst、SymfonyRisky.rst 等。这些规则集文档均以RISKY警告开头再次印证该规则的风险属性。实际项目中最常见的用法是直接引入迁移规则集让别名替换随 PHP 版本升级自动生效return (new PhpCsFixer\Config()) -setRiskyAllowed(true) -setRules([ PHP8x0Migration:risky true, ]);设置setRiskyAllowed(true)或 CLI 参数--allow-riskyyes是启用 risky 规则集/规则的必要条件。测试保障向后兼容承诺文档末尾的 References 一节特别强调The test class defines officially supported behaviour. Each test case is a part of our backward compatibility promise.测试类定义了官方支持的行为每个测试用例都是向后兼容承诺的一部分。NoAliasFunctionsFixerTest.php 对该规则进行了系统性验证值得关注的测试策略包括逐集合全覆盖provideAllCases()通过反射读取SETS常量为每个别名生成不应被修改与应被修改两类用例L278-L367保证每个集合内的每个映射都有测试默认配置组合provideFixCases()先按默认集合internal、IMAP、pg生成用例再为其余集合单独配置测试L58-L72大小写不敏感time用例使用全大写的MKTIME()、GMMKTIME()验证L205-L221参数个数约束time集合中mktime($a)带参调用保持原样只有无参mktime()才改为time()L165-L189PHP 8.1 兼容testFix81验证在 PHP 8.1 下is_double(...)first-class callable 语法不会被误改L250-L265非法配置testInvalidConfiguration验证传入[sets [foo]]会抛出InvalidFixerConfigurationExceptionL267-L273。此外tests/Fixtures/Integration/php_compat/PHP7.3.test等集成测试在真实规则集组合下验证了no_alias_functions与其他规则协同工作的最终输出。实战建议新旧项目差异化配置新项目建议直接[sets [all]]或引入PHP8x0Migration:risky存量老项目可先只用默认[internal, IMAP, pg]小范围试点再逐步放开到mbreg、time等集合。先用 diff 模式预览由于规则是 risky 的务必先执行php-cs-fixer fix --dry-run --diff检查每个改动点确认没有覆盖内置别名函数的自定义全局函数。警惕time集合mktime()/gmmktime()只有在无参调用时才等价于time()带参调用会被保留这是实现中显式的参数个数保护L286-L293。与native_function_invocation配合若同时启用native_function_invocation如Symfony:risky所依赖全局函数还会被加上\前缀形成统一命名 显式全局引用的双重规范化效果。注意集合空转snmp当前为空集合并计划在 4.0 移除配置中无需显式包含它。参考资料规则文档doc/rules/alias/no_alias_functions.rst修复器实现src/Fixer/Alias/NoAliasFunctionsFixer.php单元测试tests/Fixer/Alias/NoAliasFunctionsFixerTest.php优先级集成测试tests/Fixtures/Integration/priority/no_alias_functions,implode_call.test函数调用识别器src/Tokenizer/Analyzer/FunctionsAnalyzer.php所属规则集示例src/RuleSet/Sets/PHP7x4MigrationRiskySet.php、src/RuleSet/Sets/PHP8x0MigrationRiskySet.php、src/RuleSet/Sets/SymfonyRiskySet.php、src/RuleSet/Sets/PhpCsFixerRiskySet.php赞分享开发工具代码质量静态分析Lint格式化【免费下载链接】PHP-CS-FixerA tool to automatically fix PHP Coding Standards issues项目地址https://gitcode.com/gh_mirrors/ph/PHP-CS-Fixer点击查看免费下载相关推荐PHP-CS-Fixer function_to_constant 规则详解将核心函数调用替换为常量PHP CS Fixer function_to_constant 规则详解将核心函数调用替换为常量 导读 function_to_constant 是 PH开发工具代码质量静态分析Lint格式化PHP CS Fixer 的 use_arrow_functions 规则把单语句匿名函数自动改写为箭头函数PHP CS Fixer 的 use_arrow_functions 规则把单语句匿名函数自动改写为箭头函数 导读 use_arrow_functions 是开发工具代码质量静态分析Lint格式化终极指南PhotoGIMP让Photoshop用户免费切换到GIMP的完整解决方案终极指南PhotoGIMP让Photoshop用户免费切换到GIMP的完整解决方案 你是不是从Photoshop转向GIMP后感觉界面陌生、快捷键不习惯、工开发工具代码质量静态分析Lint格式化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表