编程

PHP 8.3 正式发布

1219 2023-11-21 16:05:00

PHP 8.3 即将于 2023 年 11 月 23 日正式发布。其中新增和更新的内容包括类常量显式类型、json_validate() 函数、动态获取类常量、#[Override] 属性等等。

类常量显式类型

在 PHP 8.2 中,仍然无法声明常量(const)类型,这可能会导致对所使用的类型的混淆: 

interface I {
    const TEST = "Test";  // We may naively assume that the TEST constant is always a string
}
 
class Foo implements I {
    const TEST = [];      // But it may be an array...
}
 
class Bar extends Foo {
    const TEST = null;    // Or null
}

下例是类型和常量在 PHP 8.3 中的示例:

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
}
 
// Error example
 
// Fatal error: Cannot use array as value for class constant
// Foo::PHP of type string
class Buzz implements I {
    const string PHP = [];
}

json_validate() 函数

要在 PHP 中验证 JSON,你需要配置 JSON_THROW_ON_ERROR 标志, 使用 json_last_error,甚至只需确认json_decode() 调用是否为 null

json_decode(json: '{"foo": "bar}', flags: JSON_THROW_ON_ERROR);
 
// JsonException  Control character error, possibly incorrectly encoded.

从 PHP 8.3 开始,你也可以使用 json_validate 函数验证 JSON:

// Valid
json_validate('{"framework": "Laravel"}'); // true
 
// Invalid
json_validate('{"framework": "Laravel}'); // false
 
json_last_error_msg(); // Control character error, possibly incorrectly encoded
json_last_error(); // 3

动态类常量获取

在 PHP >= 8.2 中,只有使用 `constant()` 函数才能动态获取类常数值。以下情况将导致语法错误:

class Framework {
    const NAME = 'Laravel';
}
 
$name = 'NAME';
 
// You could achieve this with the constant() function
constant(Framework::class . '::' . $name); // Laravel
 
// This following is a syntax error in >=v8.2.0
echo Framework::{$name};
// ParseError  syntax error, unexpected token ";", expecting "(".

从 PHP 8.3 开始,您现在可以从类中动态访问常量,

class Framework {
    const NAME = 'Laravel';
}
 
$name = 'NAME';
 
// Syntax error in <= v8.2.0
echo Framework::{$name}; // Laravel

INI 文件中环境变量的回退值

我最喜欢在 PHP 8.3中 添加的内容之一是在使用环境变量定义 INI 设置时提供默认值。这将简化 Docker 默认值,而不必在 Dockerfile 中将默认值指定为 ENV 块。我相信有很多用例会因此而简化。

例如,假设您希望将 www FPM 池 配置为 DRUPAL_FPM_PORT ENV 值:

error_log = syslog
daemonize = false
 
[www]
listen = localhost:${DRUPAL_FPM_PORT}

必须定义 DRUPAL_FPM_PORT 并且不可能有默认值!现在,您可以执行以下操作,这对于 bash/shell 脚本来说应该很熟悉:

[www]
listen = localhost:${DRUPAL_FPM_PORT:-9000}

想象一下,将 xdebug.ini 文件作为 Dockerfile 的一部分进行开发,并使用合理的默认值,同时允许开发人员覆盖他们认为合适的值。

Randomizer 附加

PHP 8.3 包含 Randomizer 附加, 包括新增了一个 getBytesFromString() 方法,该方法用于从源字符串获取随机字节:

$randomizer = new \Random\Randomizer();
 
printf(
    "%s.example.com",
    $randomizer->getBytesFromString('abcdefghijklmnopqrstuvwxyz0123456789', 16)
);
 
// 3zsw04eiubcf82jd.example.com
 
 
// Generate a random code for multi-factor authentication
$randomizer = new \Random\Randomizer(new \Random\Engine\Secure());
 
echo implode('-', str_split($randomizer->getBytesFromString('0123456789', 20), 5));
 
// 11551-80418-27047-42075

新增 #[\Override] 属性

PHP < 8.3

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 vs tearDown).

PHP 8.3

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');
    }
}

// 致命错误: MyTest::taerDown() 有用 #[\Override] 属性,但是父类中没有相匹配的方法

通过将 #[\Override] 属性添加到方法中,PHP 将确保在父类或实现的接口中存在具有相同名称的方法。添加该属性可以清楚地表明重写父方法是有意的,并简化了重构,因为将检测到对重写的父方法的删除。

PHP < 8.3

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

PHP 8.3

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';

readonly 属性现在可以在 __clone 魔术方法中修改一次,以启用只读属性的深度克隆。

 

更多新特性、变更及特性弃用请查看:

新特性

PHP INI 环境变量语法支持回退值

语法/功能变更

废弃