简介
PHP 8.3 引入了许多新特性,包括类型化类常量(Typed Class Constants)、#[\Override] 属性、json_validate() 函数、只读属性的深度克隆等。本文将介绍主要变化和如何从 PHP 8.2 迁移到 PHP 8.3。
参考资源
- 在线测试环境:https://onlinephp.io/
- 官方发布说明:https://www.php.net/releases/8.3/en.php
- 迁移指南:https://www.php.net/manual/en/migration83.php
- 更新详情:https://php.watch/versions/8.3
PHP 8.3 新特性
类型化类常量(Typed Class Constants)
PHP 8.3 允许为类常量声明类型,增强了类型安全性:
interface DatabaseInterface {
public const string ENGINE = 'mysql';
}
trait ConfigTrait {
final protected const int MAX_CONNECTIONS = 100;
}
enum Status: string {
public const string DEFAULT = 'pending';
case Pending = 'pending';
case Active = 'active';
}
class Database implements DatabaseInterface {
use ConfigTrait;
// 类型遵循 LSP(里氏替换原则),子类可以收窄类型
public const string ENGINE = 'postgresql';
}
类常量类型遵循里氏替换原则(LSP),子类可以收窄父类常量的类型:
class ParentClass {
public const string|int VALUE = 'MyValue';
}
class ChildClass extends ParentClass {
// ✅ 可以将 string|int 收窄为 string
public const string VALUE = 'MyValue';
}
#[\Override] 属性
新的 #[\Override] 属性确保方法确实覆盖了父类方法,有助于捕获拼写错误和简化重构:
class ParentClass {
protected function setUp(): void {}
}
class ChildClass extends ParentClass {
#[\Override]
protected function setUp(): void {} // ✅ 正确覆盖
#[\Override]
protected function setUpP(): void {} // ❌ Fatal error: 父类没有该方法
}
这在重构时特别有用 - 如果父类方法被重命名或删除,PHP 会立即报错而不是静默失败。
动态类常量和枚举成员访问
PHP 8.3 支持使用变量语法访问类常量,无需使用 constant() 函数:
class Config {
const DATABASE = 'mysql';
const CACHE = 'redis';
}
$key = 'DATABASE';
// PHP 8.3 之前
echo constant(Config::class . "::{$key}");
// PHP 8.3 - 新语法
echo Config::{$key}; // 输出: mysql
同样适用于枚举:
enum Color: string {
case Red = '#FF0000';
case Green = '#00FF00';
}
$color = 'Red';
echo Color::{$color}->value; // 输出: #FF0000
只读属性的深度克隆
PHP 8.3 允许在 __clone() 方法中重新初始化只读属性,解决了深度克隆的问题:
readonly class User {
public function __construct(
public string $name,
public DateTime $createdAt,
) {}
public function __clone(): void {
// PHP 8.3 允许在 __clone() 中修改只读属性
$this->createdAt = clone $this->createdAt;
}
}
$user1 = new User('Alice', new DateTime());
$user2 = clone $user1;
// $user2->createdAt 现在是独立的副本
json_validate() 函数
新的 json_validate() 函数用于验证 JSON 字符串语法,比 json_decode() 更高效:
// 验证 JSON 语法
var_dump(json_validate('{"name": "PHP", "version": 8.3}')); // true
var_dump(json_validate('{invalid json}')); // false
var_dump(json_validate('[1, 2, 3]')); // true
// 相比 json_decode(),不需要解码完整内容,更节省内存
if (json_validate($jsonString)) {
$data = json_decode($jsonString);
}
Random 扩展增强
PHP 8.3 为 Random 扩展增加了新方法:
$randomizer = new Random\Randomizer();
// getFloat() - 生成指定范围的随机浮点数
echo $randomizer->getFloat(0, 10); // 例如: 6.7810757668383
// 使用边界类型
echo $randomizer->getFloat(
0,
10,
Random\IntervalBoundary::ClosedOpen // [0, 10)
);
// nextFloat() - 生成 [0, 1) 范围的随机浮点数
echo $randomizer->nextFloat(); // 例如: 0.21185336351144
// getBytesFromString() - 从指定字符集生成随机字符串
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
echo $randomizer->getBytesFromString($chars, 16); // 例如: a7b3x9k2m5n8p1q4
匿名类可以是只读的
$instance = new readonly class {
public function __construct(
public string $name = 'Anonymous',
) {}
};
echo $instance->name; // Anonymous
INI 环境变量回退值
PHP 8.3 支持在 INI 文件中为环境变量设置回退值:
; 如果 DB_HOST 环境变量未设置,使用 localhost 作为默认值
database.host = ${DB_HOST:-localhost}
PHP 8.3 新增函数
mb_str_pad() - 多字节字符串填充
// 填充多字节字符串到指定长度
echo mb_str_pad('中国', 6, '爱', STR_PAD_RIGHT) . "\n"; // 中国爱爱爱爱
echo mb_str_pad('中国', 6, '爱', STR_PAD_LEFT) . "\n"; // 爱爱爱爱中国
echo mb_str_pad('中国人', 6, '爱', STR_PAD_BOTH) . "\n"; // 爱中国人爱爱
str_increment() 和 str_decrement()
// 字符串递增
echo str_increment('a'); // 'b'
echo str_increment('z'); // 'aa'
echo str_increment('A9'); // 'B0'
echo str_increment('foo'); // 'fop'
// 字符串递减
echo str_decrement('b'); // 'a'
echo str_decrement('aa'); // 'z'
echo str_decrement('B0'); // 'A9'
stream_context_set_options()
// 设置流上下文选项
$context = stream_context_create();
stream_context_set_options($context, [
'http' => [
'method' => 'GET',
'header' => 'Accept: application/json',
],
]);
DateTime 新方法
// 从 DateTime 创建 DateTimeImmutable
$dt = new DateTime('2024-01-01');
$immutable = DateTimeImmutable::createFromMutable($dt);
// IntlCalendar 新方法
$calendar = IntlCalendar::createInstance();
$calendar->setDate(2024, 0, 15); // 设置日期
$calendar->setDateTime(2024, 0, 15, 10, 30, 0); // 设置日期时间
DOM 扩展新方法
$doc = new DOMDocument();
$doc->loadHTML('<div class="box" id="main">Hello</div>');
$element = $doc->getElementById('main');
// 获取所有属性名
$names = $element->getAttributeNames(); // ['class', 'id']
// 切换属性
$element->toggleAttribute('hidden'); // 添加 hidden 属性
$element->toggleAttribute('hidden'); // 移除 hidden 属性
// 检查包含关系
$parent = $doc->documentElement;
var_dump($parent->contains($element)); // true
// 获取根节点
$root = $element->getRootNode();
// 比较节点
var_dump($element->isEqualNode($element)); // true
命令行 Linter 改进
# PHP 8.3 支持同时检查多个文件
php -l file1.php file2.php file3.php
# 输出:
# No syntax errors detected in file1.php
# No syntax errors detected in file2.php
# No syntax errors detected in file3.php
弃用特性
get_class() 和 get_parent_class() 无参数调用
class MyClass {
public function getName(): string {
// ❌ PHP 8.3 已弃用
return get_class();
// ✅ 使用 $this 或 self
return get_class($this);
// 或
return self::class;
}
public function getParentName(): string {
// ❌ PHP 8.3 已弃用
return get_parent_class();
// ✅ 使用 $this 或 self
return get_parent_class($this);
// 或
return parent::class;
}
}
Assert 相关设置
// ❌ 以下 INI 设置已弃用
// assert.active
// assert.bail
// assert.callback
// assert.exception
// assert.warning
// ❌ assert_options() 函数已弃用
assert_options(ASSERT_ACTIVE, true);
// ✅ 使用 ini_set() 替代
ini_set('zend.assertions', 1);
MT_RAND_PHP 变体
// ❌ PHP 8.3 已弃用
mt_srand(1234, MT_RAND_PHP);
// ✅ 使用默认的 Mersenne Twister 实现
mt_srand(1234, MT_RAND_MT19937);
兼容性变化
空数组负索引行为变化
$arr = [];
$arr[-5] = 'a';
$arr[] = 'b';
var_dump($arr);
// PHP 8.3: [-5 => 'a', -4 => 'b']
// PHP < 8.3: [-5 => 'a', 0 => 'b']
range() 函数行为变化
// PHP 8.3 更严格地处理参数类型
range('a', 'z'); // ✅ 正常
range(1, 10); // ✅ 正常
range(1.5, 5.5); // ✅ 正常
range('a', 1); // ⚠️ PHP 8.3 会产生警告
SQLite3 默认错误模式
// PHP 8.3 中 SQLite3 默认使用异常模式
$db = new SQLite3(':memory:');
// 错误现在会抛出异常而不是返回 false
DateTime 异常类型细化
PHP 8.3 为 DateTime 相关操作引入了更具体的异常类型:
try {
new DateTime('invalid date');
} catch (DateMalformedStringException $e) {
// PHP 8.3 新增的具体异常类型
echo $e->getMessage();
}
unserialize() 错误级别提升
// PHP 8.3 将 E_NOTICE 提升为 E_WARNING
// 反序列化时的错误现在产生警告而不是通知
$data = unserialize('invalid');
其他改进
gc_status() 返回更多信息
$status = gc_status();
// PHP 8.3 返回额外的垃圾回收信息
var_dump($status);
// 包含 running, protected, full 等新字段
类别名支持内置类
// PHP 8.3 允许为内置类创建别名
class_alias('stdClass', 'MyStdClass');
$obj = new MyStdClass();
OpenSSL EC 密钥生成
// 使用自定义参数生成 EC 密钥
$config = [
'curve_name' => 'prime256v1',
'private_key_type' => OPENSSL_KEYTYPE_EC,
];
$key = openssl_pkey_new($config);
总结
PHP 8.3 带来了许多实用的新特性和改进:
主要新特性:
- 类型化类常量
#[\Override]属性- 动态类常量访问
- 只读属性的深度克隆
json_validate()函数- Random 扩展增强
mb_str_pad()函数
需要注意的变化:
- 空数组负索引行为变化
get_class()和get_parent_class()无参数调用已弃用- Assert 相关 INI 设置已弃用
- SQLite3 默认使用异常模式
- DateTime 异常类型细化
通过了解这些变化并遵循迁移建议,可以顺利地从 PHP 8.2 迁移到 PHP 8.3。