PHP 8.1+ array_merge 不接受 null,需用 ?? [];8.0+ array_key_exists 对数字字符串键严格匹配,建议改用 isset;8.2+ 模型需加 #[\AllowDynamicProperties] 或显式声明属性。

PHP 8.1+ 下 array_merge 接收 null 报 TypeError
ThinkPHP 6.x 早期版本(如 v6.0.13 之前)在构造查询条件、合并配置或处理请求参数时,会直接把可能为 null 的变量传给 array_merge。PHP 8.1 起该函数不再静默转为 [],而是抛出 TypeError: array_merge(): Argument #1 must be of type array, null given。
常见于 where 条件拼接、with 关联预载入、或自定义 scope 方法中未判空就调用 array_merge($a, $b)。
- 修复方式:所有传给
array_merge的参数都加?? []或is_array($x) ? $x : [] - 特别注意
Db::table()->where(...)->select()前的where数组参数,若来自用户输入且未初始化,极易触发 - ThinkPHP 6.0.14+ 已在核心中修补部分位置,但项目里自定义逻辑仍需自查
ThinkPHP 6.1+ 使用 array_key_exists 检查数字字符串键名失效
PHP 8.0+ 对 array_key_exists 的行为做了语义收紧:当数组键是整数(如 0、123),而你用字符串形式检查(如 '0'、'123'),它将返回 false —— 这和 PHP 7.x 的“自动类型宽松匹配”不同。
ThinkPHP 中常见于模型的 append、hidden、visible 属性判断,或手动遍历 $data 时写 if (array_key_exists('id', $data)) 却实际键是整型 id(由 PDO 返回)。
立即学习“PHP免费学习笔记(深入)”;
- 安全写法:统一用
isset($array[$key])替代array_key_exists,它对数字字符串键兼容性更好 - 若必须用
array_key_exists,确保传入的键类型与数组中真实键类型一致(比如先(string)$key或(int)$key) - 不要依赖
array_keys($arr)后用in_array判定,性能差且类型易错
foreach 遍历 ThinkPHP Collection 时修改原数组导致 Invalid argument supplied for foreach()
ThinkPHP 的 Collection 是对数组的封装,但底层仍用 PHP 原生数组。当在 foreach ($collection as $k => $v) 循环体内执行 $collection->push(...)、unset($collection[$k]) 或直接改写 $collection->items,PHP 8+ 会更严格地检测迭代器状态,容易报错或跳过元素。
典型场景:循环处理分页结果时,想动态过滤掉某些项并重排索引;或在事件回调中修改了正在遍历的集合。
- 正确做法:先用
$collection->toArray()转成纯数组再遍历,或用$collection->filter(...)->values()做不可变操作 - 避免在
foreach内部调用$collection->each()或任何会触发__set/offsetSet的方法 - 如果真要边遍历边删,用
for ($i = count($arr) - 1; $i >= 0; $i--)倒序处理原数组
PHP 8.2+ Dynamic property 警告干扰 ThinkPHP 模型属性访问
PHP 8.2 默认开启 dynamic_properties 限制,而 ThinkPHP 模型(尤其 BaseModel)常通过 __get/__set 动态代理字段。若模型类没声明 public $field_name; 或没设置 protected $lazyWrite = true;,PHP 会发出 Deprecated: Creation of dynamic property App\Model\User::$name is deprecated 警告。
这不阻断运行,但日志爆炸、CI/CD 流水线可能因警告失败,且掩盖真正错误。
- 最简方案:在模型类顶部加
#[\AllowDynamicProperties](PHP 8.2+) - 长期建议:显式声明常用字段为
public或protected,或升级到 ThinkPHP 6.3+,它已默认适配该特性 - 别用
ini_set('error_reporting', E_ALL & ~E_DEPRECATED)掩盖,这会让后续升级更难定位问题
类型强制转换不是语法糖,是 PHP 运行时契约的收紧。每个 ?? []、每处 isset 替代、每个 #[\AllowDynamicProperties],都是在对齐新版 PHP 的语义边界——漏掉任意一个,都可能在某个凌晨三点的部署里突然冒出来。



















