学完这篇,你能在自己的服务器上把 JIT 打开、写出用命名参数和联合类型的新代码,并用枚举替掉代码里那些 `1`、`2`、`'draft'` 的魔法值。
下面四步按「先看环境 → 再谈性能 → 再谈写法 → 最后谈建模」的顺序来,每步都给你能直接复制的东西。
第一步:先确认版本,别对着 PHP 7.4 学 8.0
php -v
php --ini # 看配置文件在哪
php -m | grep -i opcache # 看 opcache 是否加载
面板用户不方便敲命令,就在站点里放一个 `phpinfo()` 页面临时看一眼,看完删掉。四个特性的最低版本分别是:JIT / 命名参数 / 联合类型 = PHP 8.0,枚举 = PHP 8.1。也就是说 PHP 8.0 上没有 `enum`,写下去就是解析错误。
如果你是在给 Clara BBS 写 `content/plugins` 下的插件,更要先确认目标站的 PHP 版本——它支持 PHP 7.4-8.5 这一整段,插件保存即生效,但一个 8.1 才认的语法丢到 7.4 站点上会直接语法错误白屏。
第二步:JIT —— 配置三行,但别期待奇迹
JIT 是 Opcache 的一部分,不能单独开。编辑 php.ini:
zend_extension=opcache
opcache.enable=1
opcache.jit_buffer_size=64M
opcache.jit=tracing
注意:只管写 `opcache.jit=tracing` 是没用的,真正的开关是 `jit_buffer_size`,它默认为 0(即关闭)。反过来,只设 `jit_buffer_size` 就会启用默认的 tracing 模式。改完必须重启 PHP-FPM。
验证:
var_dump(opcache_get_status()['jit']['on'] ?? false);
var_dump(opcache_get_status()['jit']['buffer_size'] ?? 0);
什么时候有用:纯计算的密集循环、图像/规则引擎这类 CPU 密集代码,可能有几成加速。什么时候没用:以数据库查询和模板渲染为主的 CRUD 站点,瓶颈在 IO 不在 CPU,JIT 基本看不出差别。所以先压测再加,别把 JIT 当成万能药。
第三步:命名参数与联合类型 —— 改写法,不改逻辑
命名参数让你跳过中间的可选参数:
// 老写法:为了传第 4 个参数,硬填中间两个
htmlspecialchars($str, ENT_QUOTES, 'UTF-8', false);
// 命名参数
htmlspecialchars($str, flags: ENT_QUOTES, double_encode: false);
规则:命名参数必须写在位置参数之后,不能再回头写位置参数;后面也不能跟 `...` 展开。参数名会做大小写不敏感匹配。
注意:参数名是 API 的一部分。你给外部用的函数一旦有人按名字调用,重命名参数就是破坏兼容,改之前想清楚。
联合类型把「一个参数接受多种类型」写进签名:
function formatId(int|string $id): string { ... }
class A {
public function __construct(private int|string|null $id = null) {}
}
`?int` 就是 `int|null` 的简写。不能写重复成员(`int|INT` 报错),也不能把 `void` 放进联合。`false` 可以作为联合成员(8.0 起),`true` / `null` 单独作类型要 8.2。
注意:默认的弱类型模式下,联合类型仍会尝试转换,`formatId('12')` 传字符串不会报错。真要严格拦截,在文件第一行加 `declare(strict_types=1);`。
第四步:枚举 —— 把状态值变成类型
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
public function label(): string
{
return match ($this) {
Status::Draft => '草稿',
Status::Published => '已发布',
};
}
}
$s = Status::from('draft'); // 找不到抛 ValueError
$s = Status::tryFrom($row['status']) ?? Status::Draft; // 兜底更安全
echo $s->value; // 'draft',入库用这个
echo $s->name; // 'Draft'
Status::cases(); // 全部成员,适合渲染下拉框
要点:带 `: string` / `: int` 的是「有值枚举」,背后必须有标量值;不写就是纯枚举。枚举可以有常量、方法、实现接口,但不能有属性,也不能被继承、不能被 `new`。
注意:从数据库读回来一定用 `tryFrom()`。历史脏数据(比如某个状态值是 `'del'`)会让 `from()` 直接抛异常,把整个列表页打挂。另外存储时存 `->value` 而不是 `->name`,将来改大小写不影响老数据。
小结
- JIT 由 `opcache.jit_buffer_size` 控制开关,CPU 密集型才有明显收益,CRUD 站点别指望。
- 命名参数适合跳可选参数,但参数名一旦公开就是兼容契约。
- 联合类型写进签名更清晰,弱模式下仍会转换,要严格就上 `strict_types=1`。
- 枚举解决魔法值问题,读库用 `tryFrom()` 兜底,存库用 `->value`。
- 给支持 PHP 7.4 的系统写插件时,8.1+ 的语法要用前先确认运行版本。