PHP 8.3发布了!


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()