PHP 8.3 是 PHP 语言的重大更新。它包含许多新功能,例如类常量的显式类型、只读属性的深度克隆以及随机功能的添加。与往常一样,它还包括性能改进、错误修复和常规清理。
类型化类常量
尽管 PHP 的类型系统年复一年地投入了巨大的努力,但声明常量类型仍然是不可能的。对于全局常量来说,这不太值得关注,但确实可能成为类常量的错误和混乱的根源:
默认情况下,子类可以覆盖其父类的类常量,因此有时很难假设类常量的实际值和类型是什么,除非它们的定义类或常量本身是final:
以前
interface I { // 之前我们可以天真地假设 PHP 常量总是一个字符串。 const PHP = 'PHP 8.2'; }
class Foo implements I { // 但实现类可以将其定义为数组。 const PHP = ; } class Bar extends Foo { const TEST = null; // 或者实现为空 }
|
类常量(class constants)对声明类、接口、特质和枚举常量类型(为简单起见,从现在起统称为 "类常量")的支持:
enum E { const string TEST = "Test1"; // E::TEST is a string } trait T { const string TEST = E::TEST; // T::TEST is a string too } interface I { const string TEST = E::TEST; // I::TEST is a string as well } class Foo implements I { use T; const string TEST = E::TEST; // Foo::TEST must also be a string } class Bar extends Foo { const string TEST = "Test2"; // Bar::TEST must also be a string, but the value can change }
|
现在:
interface I { const string PHP = 'PHP 8.3'; }
class Foo implements I { const string PHP = ; }
// Fatal error: 无法使用数组作为类常量的值 // Foo::PHP of type string
|
动态类常量获取
PHP 实现了多种查找成员姓名的方法。
一个值得注意的例外是类常量。
之前:
class Foo { const PHP = 'PHP 8.2'; }
$searchableConstant = 'PHP';
var_dump(constant(Foo::class . "::{$searchableConstant}"));
|
现在:
class Foo { const PHP = 'PHP 8.3'; }
$searchableConstant = 'PHP';
var_dump(Foo::{$searchableConstant});
|
新的 [ Override ] 属性
当实现接口或从另一个类继承时,PHP 会执行各种检查以确保实现的方法与接口或父类施加的约束兼容。然而,它无法检查一件事:意图。
对于人类读者来说也是如此。虽然人类读者能够通过仔细查看代码以及可能的 VCS 历史记录来确定意图,但如果原始作者的意图以确保信息保持最新的方式明确表达,那么肯定会更简单。
以前:
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase { protected $logFile;
protected function setUp(): void { $this->logFile = fopen('/tmp/logfile', 'w'); }
protected function taerDown(): void { fclose($this->logFile); unlink('/tmp/logfile'); } }
// 日志文件将永远不会被删除,因为 // 方法名称打错了(taerDown 与 tearDown)。
|
现在:
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase { protected $logFile;
protected function setUp(): void { $this->logFile = fopen('/tmp/logfile', 'w'); }
#[\Override] protected function taerDown(): void { fclose($this->logFile); unlink('/tmp/logfile'); } }
// Fatal error: MyTest::taerDown() 具有 #[\Override] 属性、 // 但不存在匹配的父方法
|
通过向#[\Override]方法添加该属性,PHP 将确保父类或实现的接口中存在同名方法。添加该属性可以清楚地表明重写父方法是有意的,并简化了重构,因为将检测到重写的父方法的删除。
深度克隆
以前:
class PHP { public string $version = '8.2'; }
readonly class Foo { public function __construct( public PHP $php ) {}
public function __clone(): void { $this->php = clone $this->php; } }
$instance = new Foo(new PHP()); $cloned = clone $instance;
// Fatal error: Cannot modify readonly property Foo::$php
|
现在:
class PHP { public string $version = '8.2'; }
readonly class Foo { public function __construct( public PHP $php ) {}
public function __clone(): void { $this->php = clone $this->php; } }
$instance = new Foo(new PHP()); $cloned = clone $instance;
$cloned->php->version = '8.3';
|
其他新功能:
- 新json_validate()功能
- 新方法 Randomizer::getBytesFromString()、Randomizer::getFloat() 和Randomizer::nextFloat()