PHP 8.3三大核心变更是类型化类常量、#[Override]属性和动态类常量语法,要求显式声明常量类型、强制覆盖校验、限制变量插值范围,并默认禁用动态属性以提升健壮性。

PHP 8.3 已于 2023 年 11 月 23 日正式发布,不是“最新版”——截至 2026 年 5 月,PHP 8.4 已稳定发布,PHP 8.5 处于开发阶段。但 PHP 8.3 仍是大量生产环境的主力版本,其三大核心变更直接影响代码健壮性与维护成本:类型化类常量、#[\Override] 属性、动态类常量语法。忽略它们,升级后大概率触发致命错误或弃用警告。
类常量必须声明类型,否则报错
PHP 8.3 开始,接口/类/枚举中定义的常量若未显式标注类型(如 const string VERSION = '1.0'),在实现类中重写时若类型不匹配,会直接抛出 Fatal error: Cannot use array as value for class constant。
常见错误现象:
- 旧代码中接口只写
const VERSION = '1.0',子类误写成const VERSION = [],PHP 8.2 及以前静默通过,8.3 立即崩溃 - IDE 无法推断常量类型,导致补全失效或类型提示丢失
实操建议:
- 所有公共契约常量(尤其接口)必须加类型,如
const int MAX_RETRY = 3、const array SUPPORTED_FORMATS = ['json', 'xml'] - 不要依赖“默认是 string”这种隐式假设;
const VERSION这种无类型写法在 8.3 中已不安全 - 迁移时用静态分析工具(如 PHPStan level 8+)批量扫描未类型化常量
#[\Override] 不是可选装饰,而是编译期强制校验
这个属性不是“加了更好”,而是“加了就必须成立”。一旦标记,PHP 在加载类时就检查父类或接口中是否存在同名、签名兼容的方法;不满足则直接 Fatal error,不等到运行时。
立即学习“PHP免费学习笔记(深入)”;
典型踩坑场景:
- 拼写错误:把
tearDown()写成taerDown(),8.2 中测试始终走父类逻辑,8.3 加了#[\Override]就立刻报错 - 父类方法被重构删除,子类忘了同步删掉
#[\Override]方法,部署即失败 - 误用于 trait 方法:该属性只校验继承链(
extends/implements),对use的 trait 无效
实操建议:
- 只在明确覆盖父类/接口方法时添加,禁止“预防性添加”
- PHPUnit 测试类的
setUp()/tearDown()、Laravel 的boot()/register()等钩子方法,是高价值加注点 - CI 流程中启用
opcache.enable_cli=1,确保类加载阶段就能捕获校验失败
ClassName::{$var} 不能替代 constant() 做任意字符串拼接
新语法仅支持变量插值,不支持表达式拼接。试图写 Foo::{"PREFIX_". $suffix} 或 Foo::{$config['key']} 会报 TypeError,因为大括号内必须求值为纯字符串字面量(且不能含非法字符)。
常见错误现象:
- 从
constant(Foo::class . '::' . $name)直接改成Foo::{$name},但$name实际是'APP_NAME'—— 表面能跑,但若$name = 'APP_NAME::extra'就崩 - 用数组键名动态取常量,如
$key = array_keys($map)[0]; Foo::{$key},一旦$key含空格或特殊符号,立刻失败
实操建议:
- 只用于简单变量(如
$constName = 'DEBUG'),避免复杂表达式 - 仍需运行时兜底:对不可信输入,先用
defined()检查常量是否存在,再用新语法访问 - 枚举成员也适用此语法(
MyEnum::{$value}),但注意枚举成员名必须合法,不能是数字字符串如"0"
动态属性默认禁用,#[\AllowDynamicProperties] 不是补丁而是设计决策
PHP 8.3 起,未标记的类中给未声明属性赋值(如 $user->email = 'a@b.c')会触发 Deprecated: Creation of dynamic property 警告。这不是临时兼容层,而是语言层面切断“随意扩对象”的惯性。
容易被忽略的关键点:
- 警告级别是
E_DEPRECATED,默认不中断执行,但生产环境开启error_reporting = E_ALL & ~E_DEPRECATED会完全屏蔽——等于主动放弃检测 -
__get()/__set()魔术方法不受影响,但二者无法替代类型安全和 IDE 支持 - 第三方库若内部依赖动态属性(如某些老旧 ORM),升级后需确认是否已适配 8.3
实操建议:
- 新类一律不加
#[\AllowDynamicProperties],改用public array $attributes = []显式承载动态字段 - 遗留代码迁移时,逐个类加注并配合单元测试验证行为不变,而非全局开启
- 配置
zend.assertions = 1+assert.exception = 1,把弃用警告转为异常,强制暴露问题
#[\Override] 锁死契约。



















