- 开发工具
- 代码质量
- 静态分析
【免费下载链接】phpstan
PHP Static Analysis Tool - discover bugs in your code without running it!
本篇文章以 PHPStan 官方错误文档 website/errors/mod.leftNonNumeric.md 为核心,结合 phpstan-strict-rules 扩展包在仓库中的规则映射与端到端测试配置,深入讲解mod.leftNonNumeric这条 strict 级别错误规则的触发条件、底层原理与标准修复姿势。读完本文,你将能够在自己的项目中快速识别%取模运算的左操作数类型问题,并掌握从「改类型声明」到「显式强转」的完整修复思路。
错误标识符是什么
mod.leftNonNumeric是 PHPStan 在启用phpstan-strict-rules扩展后可能报告的一条错误标识符。其官方 shortDescription 定义为:
Left side of the modulo operator is not a numeric type.
即「取模运算符(%)的左侧操作数不是数值类型」。该标识符用于机器可读的错误定位:在 CI 输出、编辑器诊断或ignoreErrors配置中,都可以直接以mod.leftNonNumeric作为键名引用这条规则。
在仓库的 website/src/errorsIdentifiers.json 中,该标识符被映射到 strict-rules 扩展包内的规则类:
"mod.leftNonNumeric": { "PHPStan\\Rules\\Operators\\OperandsInArithmeticModuloRule": { "phpstan/phpstan-strict-rules": [ "https://github.com/phpstan/phpstan-strict-rules/blob/2.1.x/src/Rules/Operators/OperandsInArithmeticModuloRule.php#L51" ] } }从这份映射可以看出两点关键信息:
- 该规则由
OperandsInArithmeticModuloRule类负责实现,源码位置指向 strict-rules 2.1.x 的src/Rules/Operators/OperandsInArithmeticModuloRule.php第 51 行(左操作数检查分支); - 同一规则类还负责右侧操作数的检查,即映射表紧邻的
mod.rightNonNumeric(对应同文件第 59 行),因此%运算两侧都会被严格校验。
这些以*NonNumeric结尾的标识符并非孤立存在,+、-、*、/、**等算术运算符都有对应的左/右操作数检查标识符,例如div.leftNonNumeric、mul.leftNonNumeric、plus.leftNonNumeric、pow.leftNonNumeric等,仓库的 website/errors 目录中均有对应文档,mod系列是其中针对取模运算的一对。
触发示例
原文档给出了一段最小化触发代码:
<?php declare(strict_types = 1); function remainder(bool $flag, int $b): int { return $flag % $b; }这里$flag被声明为bool类型,却直接参与了%取模运算。PHPStan(在 strict 规则加持下)会在此处报告mod.leftNonNumeric:取模运算的左侧是bool,不是合法的数值类型。
值得留意的是declare(strict_types = 1);这一行:在严格类型模式下,PHP 运行时对「弱类型隐式转换」的容忍度大幅收窄,非数值类型直接参与算术运算更可能在运行时抛出TypeError。strict 规则正是为了在运行之前就拦住这类隐患。
为什么会被报告
从 PHP 语言语义出发,%(取模/求余)是算术运算符,它的语义前提是两个操作数都必须是数值(int或float)。当左侧操作数是bool时,PHP 在运行时虽然会尝试将非数值强制转换为数字(例如true→1、false→0),但这种隐式转换会带来两类实际风险:
- 结果与直觉不符:
true % 3在运行时被转换为1 % 3,得到1。这类「碰巧能跑」的代码通常掩盖了真实的逻辑错误——开发者大概率是把某个本应承载数值的变量传错了位置。 - 严格模式下抛异常:在
strict_types = 1的文件中,非数值参与算术运算的行为更加敏感,可能在运行时直接抛出TypeError,导致线上崩溃。
因此,PHPStan 规定:算术运算中只应使用int和float类型。bool、array、object、null、string(非数字字符串)等出现在%左侧,都意味着代码存在逻辑错误。
这条规则由phpstan/phpstan-strict-rules扩展包提供,并不包含在 PHPStan 核心默认规则中——核心 PHPStan 更倾向于「只报告必然出错或必然无效的代码」,而 strict-rules 则进一步收紧边界,报告「几乎肯定是 bug」的模式。在仓库的 e2e/phpstan.neon 端到端测试配置中可以看到,strict 规则的启用方式正是通过 include 该扩展包的规则文件:
includes: - vendor/phpstan/phpstan-strict-rules/rules.neon如何修复
方案一:修正操作数的真实类型(推荐)
如果取模运算的左侧本来就应当是一个数值,那么直接修正参数的类型声明,让类型反映真实意图:
-function remainder(bool $flag, int $b): int +function remainder(int $a, int $b): int { - return $flag % $b; + return $a % $b; }这是最彻底的修复方式——类型声明与使用方式一致,mod.leftNonNumeric自然消失,且任何调用方传入非数值都会被 PHPStan 或运行时拦下。
方案二:显式类型转换
如果调用方传参无法改变(例如确实需要把bool转换为数字参与运算),则应使用显式转换表达「这里是有意转换」:
function remainder(bool $flag, int $b): int { - return $flag % $b; + return (int) $flag % $b; }显式(int)强转将意图明示给读者与静态分析器:你清楚地知道$flag不是数值,但你有意将其转为整数。需要说明的是,bool转int的语义是true→1、false→0,请确认这确实是业务想要的语义。
修复优先级建议
根据仓库 website/errors/CLAUDE.md 中对错误文档编写规范的约定,修复方式的推荐顺序为:先修复真实 bug(修正类型声明),其次用原生类型声明收窄类型,再考虑显式强转等更局部的处理。这也正对应本文两种方案的排列顺序。
与其他算术规则的关联
mod系列是 strict-rules 中「算术操作数必须为数值」家族的一员。仓库 website/errors 目录下与之同构的文档包括:
div.leftNonNumeric/div.rightNonNumeric:除法/左右操作数非数值;mul.leftNonNumeric/mul.rightNonNumeric:乘法*左右操作数非数值;plus.leftNonNumeric/plus.rightNonNumeric:加法+左右操作数非数值;minus.leftNonNumeric/minus.rightNonNumeric:减法-左右操作数非数值;pow.leftNonNumeric/pow.rightNonNumeric:幂运算**左右操作数非数值。
它们的触发原理、修复方式与mod.leftNonNumeric完全同构,只是运算符不同。若你的项目中出现多个此类标识符,说明存在系统性的「非数值参与算术运算」问题,值得在调用链源头统一治理类型声明。
在项目中的启用与落地
要在你的项目中获得mod.leftNonNumeric这条检查,需要以下两步:
- 安装扩展包:通过 Composer 引入
phpstan/phpstan-strict-rules(仓库的 e2e/composer.json 展示了在端到端测试环境中对它的依赖声明方式); - 启用规则:在
phpstan.neon配置中加入includes: - vendor/phpstan/phpstan-strict-rules/rules.neon(参考 e2e/phpstan.neon)。
启用后,PHPStan 会将其作为 strict 级别规则纳入分析。mod.leftNonNumeric的ignorable属性为true(见原文档 frontmatter),意味着你可以通过ignoreErrors配置或@phpstan-ignore-next-line注释对其进行豁免,但正如本文所述,这类错误通常暗示真实的逻辑缺陷,豁免前请先确认代码语义。
小结
mod.leftNonNumeric是 phpstan-strict-rules 提供的算术操作数类型校验规则:当取模运算符%的左侧不是int或float数值类型时触发。其底层由OperandsInArithmeticModuloRule实现(左操作数分支位于 strict-rules 2.1.x 的src/Rules/Operators/OperandsInArithmeticModuloRule.php第 51 行),与mod.rightNonNumeric及div、mul、plus、minus、pow等系列标识符共同构成完整的算术类型防线。修复时优先修正操作数类型声明,必要时辅以显式强转,即可在运行之前消除隐式转换带来的不确定性。
- 开发工具
- 代码质量
- 静态分析
【免费下载链接】phpstan
PHP Static Analysis Tool - discover bugs in your code without running it!
相关推荐
PHPStan 错误标识符 div.leftNonNumeric 深度解析:除法左操作数非数值类型
PHPStan 错误标识符 div.leftNonNumeric 深度解析:除法左操作数非数值类型 div.leftNonNumeric 是 PHPStan 在
开发工具代码质量静态分析PHPStan 错误标识符 mul.rightNonNumeric 详解:乘法运算符右操作数非数值类型
PHPStan 错误标识符 mul.rightNonNumeric 详解:乘法运算符右操作数非数值类型 mul.rightNonNumeric 是 PHPSta
开发工具代码质量静态分析PHPStan 错误标识符 `mul.leftNonNumeric` 详解:乘法运算左操作数必须为数值类型
PHPStan 错误标识符 mul.leftNonNumeric 详解:乘法运算左操作数必须为数值类型 mul.leftNonNumeric 是 PHPStan
开发工具代码质量静态分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考