是,strict_types=1自PHP 7.0引入起核心行为始终一致:仅文件级作用、仅校验当前文件函数调用的参数/返回值类型是否ZVAL层面精确匹配,不隐式转换,不跨文件生效,不校验数组键或变量赋值。

strict_types=1 在 PHP 7.0–8.3 的行为是否一致
是,核心逻辑从 PHP 7.0 引入起就没变过:declare(strict_types=1) 始终只作用于当前文件的函数调用入口,仅校验参数类型是否与声明完全匹配(ZVAL type 字段精确比对),不转换、不妥协。PHP 8.0+ 新增的 mixed、never、static 等类型不参与 strict 检查(它们本身就不支持严格模式),而 int、string、array、类名等老类型的行为在各版本中保持稳定。
容易踩的坑:
- PHP 8.0+ 中
array_filter()回调若声明了返回类型(如function(): string),且该回调所在文件启用了strict_types=1,那么回调实际返回null或int就会抛TypeError—— 这不是 strict_types 本身变了,而是 PHP 8 对内置函数回调的返回值做了更严格的运行时校验 - PHP 8.1 引入
enum后,enum类型声明默认受 strict_types 影响,但枚举实例传参时仍需注意:传MyEnum::A✅,传"A"❌(即使值相同)
PHP 5.x 项目升级到 7.0+ 后 strict_types 是否自动生效
不会。PHP 5.x 完全不识别 declare(strict_types=1),语法错误直接报错;升级到 7.0+ 后,该声明依然默认关闭(strict_types=0),必须手动添加才能启用。
这意味着:
立即学习“PHP免费学习笔记(深入)”;
- 旧代码里所有
function foo(int $x)在未加declare的文件中,依然接受"123"、123.0、true—— 和 PHP 5.x 时代“没类型声明”时的松散感一样 - 升级后第一件事不是改语法,而是决定策略:全量加声明?还是只在新模块加?漏掉一个文件,就等于留了一个类型契约断点
- 用
find ./src -name "*.php" -exec grep -L "declare(strict_types=1)" {} \;扫出未声明文件,比靠人眼检查靠谱得多
第三方库函数调用时 strict_types 是否起作用
不起作用。strict_types 是文件级开关,只约束「当前文件中定义的函数」被「当前文件中调用」时的行为。你控制器里写了 declare(strict_types=1),调用 Laravel 的 $request->query->get('page', 1),那个 get() 方法在框架源码里没声明 strict,它就照常把字符串 "1" 转成整数 1,不会报错。
所以:
- 不要指望 strict_types 能帮你拦截所有类型问题,它管不了外部代码
- 如果你封装了一个
safeGetInt(string $key): int,并在自己文件里启用了 strict,那这个函数内部怎么处理$key(比如用filter_var(..., FILTER_VALIDATE_INT))才是关键,strict 只保证别人调你时传的是string,不保证你转得对不对 - 静态分析工具(如 PHPStan、Psalm)能跨文件检查类型流,这才是补 strict_types 盲区的正解
strict_types=1 对性能有可测量影响吗
有,但极小,通常可忽略。Zend VM 在函数调用前多做一次 zval_type 比对,耗时在纳秒级。真实瓶颈从来不在这里。
真正值得警惕的是误用带来的维护成本:
- 把
declare(strict_types=1)加在包含大量$_GET/$_POST直接传参的旧路由文件里,会导致一堆"123"→int失败,这不是性能问题,是契约错位 - 在 DTO 构造方法里写
public function __construct(int $id, string $name)并启用 strict,但上游 JSON 解码后$id是string,那就得先intval()再传——strict 不帮你做这事,它只负责拦住你 - 最常被忽略的一点:strict_types 不校验数组键类型。
$arr[1]和$arr["1"]在 PHP 里是同一个元素,strict 不管这个



















