PHP 8.3 正式发布
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
魔术方法中修改一次,以启用只读属性的深度克隆。
更多新特性、变更及特性弃用请查看:
新特性
- 类常量显式类型
- 新增
json_validate
函数 - 动态类常量及枚举成员获取
gc_status()
返回额外的 GC 信息- Random 扩展 - 新增
\Random\Randomizer::getBytesFromString
方法 - Random 扩展 - 新增
\Random\Randomizer::getFloat()
和nextFloat()
方法
PHP INI 环境变量语法支持回退值
- PHP CLI Lint (
php -l
) 支持一次额 linting 多个文件 class_alias()
支持内置 PHP 类创建别名- 新增
stream_context_set_options
函数
语法/功能变更
unserialize()
:E_NOTICE
错误升级为E_WARNING
highlight_file
和highlight_string
输出 HTML 变更- 细粒化
DateTime
异常 - 某些PHP扩展类中的类常量类型声明
- 内置 CLI 服务器
$_SERVER['SERVER_SOFTWARE']
值更新
废弃