PHP 团队昨天 宣布 发布了 PHP 8.1:
? PHP 8.1 发布了!
? 阅读所有相关信息:t.co/ToeHc3YGau
♻️ 迁移指南:t.co/BVVZ7lSXd1
? 非常感谢贡献者所做的一切,感谢 RM 发布这个惊人的新版本!
❓ 您最喜欢的新功能是什么?
— php.net (@official_php) 2021 年 11 月 25 日
PHP 8.1 已经在 2021 年 11 月 25 日 发布,在本文中,我们将逐一介绍所有功能、性能改进、更改和弃用。
新特性
与每个版本一样,PHP 8.1 添加了一些不错的新特性。新特性会在每年的更新中越来越多。
枚举 RFC
枚举将在 PHP 8.1 中开始支持!如果你不确定枚举可以用在哪里,你可以阅读 这篇文档。
枚举的加入将会是 PHP 的一个大进步,因此我十分期待 PHP 8.1 中的枚举。你可以查看下面的代码示例,以对枚举有一个快速的预览:
enum Status {
case Pending;
case Active;
case Archived;
}
以下是它们的使用方式:
class Post
{
public function __construct(
private Status $status = Status::Pending;
) {}
public function setStatus(Status $status): void
{
// …
}
}
$post->setStatus(Status::Active);
你可以在 这篇文章 中找到如何使用枚举的深入分析。
Fibers RFC
Fibers —— 又叫 “绿色线程” — 是管理并行性的低级机制。您可能不会直接在您的应用程序中使用它,但像 Amphp 和 ReactPHP 等框架将大量使用它们。
这里有一个使用 fibers 的简单例子:
$fiber = new Fiber(function (): void {
$valueAfterResuming = Fiber::suspend('after suspending');
// …
});
$valueAfterSuspending = $fiber->start();
$fiber->resume('after resuming');
如果您想读取更多关于 fibers 的信息,它们能做什么,不能做什么,你可以阅读 这篇文章。
性能提升 PR
Dmitry Stogov 对 opcache 进行了一些改进,他称之为 「继承缓存(inheritance cache)」。这个新特性允许在缓存中连接两个类,就像 PHP 7.4 中两个类的 预加载 一样。
得益于这个新特性,Dmitry 说会提升 5% 到 8% 的性能,这是 PHP 8.1 中一个很好的小细节。
关联数组解包 RFC
数组的解包操作已经在 PHP 7.4 中支持了,但是只在数值数组中好用。之前不支持关联数组解包的原因是因为在合并重复的数组键的问题上未能达成统一。RFC 使用类似于 array_merge
的规则解决了这个问题。
$array1 = ["a" => 1];
$array2 = ["b" => 2];
$array = ["a" => 0, ...$array1, ...$array2];
var_dump($array); // ["a" => 1, "b" => 2]
使用 new
进行初始化设定 RFC
此 RFC 允许您在函数定义中使用关键字 new
作为默认参数,也可以在属性参数等地方使用。
class MyController {
public function __construct(
private Logger $logger = new NullLogger(),
) {}
}
您可以在 这篇专题文章 中阅读关于此功能的所有内容。
只读属性 RFC
类属性可以标记为只读,这意味着它们只能写入一次。
class PostData {
public function __construct(
public readonly string $title,
public readonly DateTimeImmutable $date,
) {}
}
初始化只读属性后,尝试更改该属性将导致错误:
$post = new Post('Title', /* … */);
$post->title = 'Other';
Error: Cannot modify readonly property Post::$title
如果您想深入了解有关只读属性的更多信息,可以阅读 我的后续文章。
你想了解更多关于 PHP 8.1 吗?可以看一下 PHP 8.1 之路。在接下来的十天里,你每天会收到一封邮件,邮件中包含 PHP 8.1 的新功能和现有功能;在那之后会为你自动退订,因此不用担心垃圾邮件的问题。现在就订阅!
最好的可调用语法 RFC
你现在可以通过调用一个方法,并向它传递 ...
,从而创建一个闭包。
function foo(int $a, int $b) { /* … */ }
$foo = foo(...);
$foo(a: 1, b: 2);
RFC 中的纯交叉类型
您已经了解了 PHP 8.0 中的联合类型,交叉类型是一个类似的功能。如果联合类型的要求是输入的类型在给定类型中,那么交叉类型的要求就是输入的类型为所有指定类型。交叉类型在处理大量接口时特别有用:
function generateSlug(HasTitle&HasId $post) {
return strtolower($post->getTitle()) . $post->getId();
}
如果你喜欢这种代码风格,你需要创建一个新的接口 Sluggable
,并在 $post
中实现它,交叉类型解决了这种额外的开销。
新类型 never
RFC
never
类型可以用来表示整个程序在一个函数中终止了。可以通过抛出异常、调用 exit
、或是其他类似的函数来实现。
function dd(mixed $input): never
{
// 输出内容
exit;
}
never
与 void
的不同之处是, void
表示程序还在继续运行。这似乎是一个新奇的功能,但实际上对于静态分析来说却是一个非常有用的功能。
新函数 array_is_list
RFC
你可能偶尔需要处理类似这样的问题:确定数组的键是否从索引 0 开始按数字顺序排列。例如 json_encode
需要决定将 json 转换为数组还是对象。
PHP 8.1 添加了一个内置函数来确定数组是否是具有这些语义的列表:
$list = ["a", "b", "c"];
array_is_list($list); // true
$notAList = [1 => "a", 2 => "b", 3 => "c"];
array_is_list($notAList); // false
$alsoNotAList = ["a" => "a", "b" => "b", "c" => "c"];
array_is_list($alsoNotAList); // false
最终类常量 RFC
PHP 中的类常量可以在继承过程中被覆盖:
class Foo
{
public const X = "foo";
}
class Bar extends Foo
{
public const X = "bar";
}
从 PHP 8.1 开始,您可以将这样的常量标记为 final
,以防止出现这种情况:
class Foo
{
final public const X = "foo";
}
class Bar extends Foo
{
public const X = "bar";
Fatal error: Bar::X cannot override final constant Foo::X
}
新的 同步
函数 RFC
PHP 8.1 增加了 fsync
函数和 fdatync
函数,强制将文件同步更改到磁盘,并确保操作系统写缓冲区在返回前已刷新。
$file = fopen("sample.txt", "w");
fwrite($file, "一些内容");
if (fsync($file)) {
echo "文件已成功保存到磁盘";
}
fclose($file);
因为磁盘同步是一个文件系统的操作,所以 fsync
函数将只对普通文件流起作用。尝试同步非文件流将发出警告。
显式八进制整数文字表示法 RFC
您现在可以使用 0o
和 0O
来表示八进制数。前面用 0
当做前缀的表示法仍然有效。
016 === 0o16; // true
016 === 0O16; // true
突破性变化
虽然 PHP 8.1 是一个小版本,但在技术上可能会有一些突破性的变化,当然也有一些反对意见。我们来逐一讨论。
内部方法返回类型 RFC
在升级到 PHP 8.1 时,你可能会见到此弃用通知:
Return type should either be compatible with IteratorAggregate::getIterator(): Traversable,
or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
你可能注意到了,这个错误信息会在使用 phpunit/phpunit
、symfony/finder
和其他流行的开源包中出现。实际是因为内部函数开始使用正确的返回类型。如果想要从标准库(如 IteratorAggregate
)中继承一个类,这时还需要添加返回类型。
修复方法很简单:如果第三方软件包中出现错误,请更新软件包的代码(其中大多数已在最新版本中修复)。如果代码中出现错误,您可以添加 ReturnTypeWillChange
属性,在 PHP 9.0 之前抑制这些错误。下面是一个类扩展 DateTime
的示例:
class MyDateTime extends DateTime
{
/**
* @return DateTime|false
*/
#[ReturnTypeWillChange]
public function modify(string $modifier)
{
return false;
}
}
或者你可以添加返回类型:
class MyDateTime extends DateTime
{
public function modify(string $modifier): DateTime|false
{
return false;
}
}
限制 $GLOBALS
的使用 RFC
一个对于 $GLOBALS
使用方式的小更改将对所有阵列操作的性能产生重大影响。Nikita 在 RFC 中很好地解释了问题和解决方案。这个变动意味着 $GLOBALS
不会在一些边缘情况下做出什么事情了。$GLOBALS 不再支持整体写入。下面的做法都将报错。
$GLOBALS = [];
$GLOBALS += [];
$GLOBALS =& $x;
$x =& $GLOBALS;
unset($GLOBALS);
引用调用 $GLOBALS
会导致一个运行时错误
by_ref($GLOBALS);
Nikita 分析了 Packagist 上排名前 2000 的包,只发现了 23 个会受此变化影响的案例。我们可以得出结论,这种技术突破性变化的影响将是很小的,这也是为什么内部决定将它们添加到 PHP 8.1。请记住,大多数开发者都将从中获益,对我们代码产生的积极影响将无处不在。
资源到对象的迁移
这些变化是将所有资源转换为专用对象的长期愿景的一部分。你可以在 这里 了解更多。
带有 finfo
对象的 Fileinfo 函数
finfo_file
、finfo_open
等函数用于接受和返回资源。从 PH P8.1 开始,它们可以使用所有的 finfo
对象。
具有 IMAPConnection
对象的 IMAP 函数
就像 fileinfo 的变更一样,像 imap_body
和 imap_open
的 IMAP 函数不再使用资源的方式。
不推荐在内部函数中将 null 传递给不可为 null 的参数 RFC
这个更改很简单:内部函数当前可以为非 null 参数接受 null
,这个 RFC 反对这种行为。例如,现在这样是可以的:
str_contains("string", null);
在 PHP 8.1 中,这样的错误将抛出一个弃用警告,在 PHP 9 中,它们将变为类型错误。
从 false
自动变为非 false (Autovivification) RFC
Autovivification 是 perl 中自造的词,但是在很多语言中都有应用。参考 Perl 的 autovivification 特性、维基百科 Autovivification(译者注)
来自 RFC:
PHP 天生支持 autovivification(自动从假的值中创建数组)。这个特性十分有用,特别是当值没有定义的时候。并且已经用在了很多 PHP 项目中。然而,有一点很奇怪,它居然允许从一个 false 和 null 创建一个数组。
您可以在 RFC 页面上阅读详细信息。总之,不推荐使用这种行为:
$array = false;
$array[] = 2;
Automatic conversion of false to array is deprecated
其他小变化
对于每一个发布版本,语言上都会有一些非常小的变化。所有这些都被列在 GitHub 的 升级指南和 弃用的 RFC 中,如果你想知道每一个小细节,一定要去看看。
以下是一些最重要的变化:
MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH
最大长度不再有效果MYSQLI_STORE_RESULT_COPY_DATA
不再有效- PDO::ATTR_STRINGIFY_FETCHES 现在也可以用布尔值了
- 当使用模拟准备语句时,PDO MySQL 和 Sqlite 结果集中的整数和浮点数将使用本地 PHP 类型而不是字符串返回
- 像
htmlspecialchars
和htmlentities
这样的函数在默认情况下也会转换为'
; 格式不正确的 utf-8 也会被 Unicode 字符替换,而不是产生一个空字符串 hash
、hash_file
和hash _ init
将会加入一个额外的参数$options
,它的默认值为[]
,不会影响已有代码- 新支持
MurmurHash3
和xxHash
就是这样了,记住我会在一年中定期更新这篇文章,所以如果你想要随时得到更新的话,一定要 订阅。你对 PHP 8.1 感到兴奋吗?在 Twitter 上告诉我吧!