PHP 8.1 正式发布,来看下有哪些新功能

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

nevervoid 的不同之处是, 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

您现在可以使用 0o0O 来表示八进制数。前面用 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/phpunitsymfony/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_filefinfo_open 等函数用于接受和返回资源。从 PH P8.1 开始,它们可以使用所有的 finfo 对象。

具有IMAPConnection 对象的 IMAP 函数

就像 fileinfo 的变更一样,像 imap_bodyimap_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 类型而不是字符串返回
  • htmlspecialcharshtmlentities 这样的函数在默认情况下也会转换为 ' ; 格式不正确的 utf-8也会被 Unicode字符替换,而不是产生一个空字符串
  • hashhash_filehash _ init 将会加入一个额外的参数 $options,它的默认值为[] ,不会影响已有代码
  • 新支持 MurmurHash3xxHash

就是这样了,记住我会在一年中定期更新这篇文章,所以如果你想要随时得到更新的话,一定要 订阅。你对 PHP 8.1 感到兴奋吗?在 Twitter 上告诉我吧!

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://stitcher.io/blog/new-in-php-81

译文地址:https://learnku.com/laravel/t/63073

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7

其实可以去官网的文档进行更新。这样让更多的人看到

2年前 评论

奇奇怪怪的语法越来越多了,有点晕。。。。

2年前 评论
thebestxt

感觉翻译得乱七八糟的。。官网上rfc就是一个tag,结果在这都给翻译进去了。

2年前 评论

一流的可调用语法 这个新特性没咋看懂 :joy:

2年前 评论
thebestxt

重新修改了一遍这篇文章的翻译。主要修改了以下内容:

  1. 标题中的 RFC 本来是一个 tag,在翻译的时候全都被当作原文翻译进去了。

file

  1. 修改了很多 直接复制了机翻结果的内容。有很多地方都是如此,读起来特别不通顺,然后把原文复制到百度翻译,发现文档里的翻译结果和百度给我的结果一字不差

file

file

  1. 修改了一个 把机翻的控制台输出结果直接复制到文档里

file

file

  1. 「Autovivification」这个词被我们翻译成了自动复活

file

查阅了几篇博文和维基百科,发现这个词指的是「如果用数组的方式来操作 false、null等值,会将变量自动变为数组」。本来是 Perl 里自造的词,后来被很多语言都使用了。目前不知道该怎么翻译,暂译为 false自动变为非 false,并且加了注解:

file


看到 Summer 大大在新的视频里用这篇文档了,于是想把这篇文档好好改一改。满篇都把 RFC 翻译进去了实在看不过去。哪位大大看到了麻烦审核一下吧,这个是 修改详情

2年前 评论
Summer (楼主) 2年前
Summer (楼主) 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!