PHP 8.1 于今日发布了。让我们一一介绍所有新功能、性能改进、更改和弃用。

PHP8.1新特性

与每个版本一样,PHP 8.1 增加了一些不错的新特性。

#枚举(eNum) rfc

枚举在 PHP 8.1 中已经添加!如果您不确定它们的用途,可以在此处阅读有关它们的信息

添加枚举将是 PHP 的重大改进。为了让您快速预览它们的外观,这里有一个代码示例:

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

纤程——又名“绿色线程” ——是一种管理并行性的低级机制。您可能不会直接在您的应用程序中使用它们,但是像 Amphp 和 ReactPHP 这样的框架会大量使用它们。它为PHP添加了异步机制

这是使用纤维的简单示例:

$fiber = new Fiber(function (): void {
    $valueAfterResuming = Fiber::suspend('after suspending');
    
    // … 
});
 
$valueAfterSuspending = $fiber->start();
 
$fiber->resume('after resuming');

如果您想了解更多有关 Fiber 的信息,以及它们能做什么和不能做什么,您可以阅读这篇文章


#性能改进 PR

Dmitry Stogov 对 opcache 进行了一些改进,他称之为“继承缓存”。此功能允许缓存类之间的链接,就像从 PHP 7.4 开始可以预加载链接的类一样。

由于这一变化,Dmitry 报告了 5% 到 8% 的性能提升,这是 PHP 8.1 中需要注意的一个很好的小细节。另对框架的性能测试见 https://github.com/kocsismate/php-version-benchmarks/blob/main/docs/results/2021_11_11_09_20_1_aws_arm64_c6g_4xlarge/result.md,此报告有20-30%性能提升。

笔者注:本要亲自对laravel9框架,进行了php8.1和php8.0的性能对比,实测php8.1比php8.0性能提升达到80%!!,如有兴趣,你可以亲自测试一下PHP8.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

如果您想深入了解有关 readonly 属性的更多信息,可以阅读我的文章


#First-class callable 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
{
    // dump
    
    exit;
}

never不同之处void在于void仍然允许程序继续。这似乎是一个新奇的功能,但它实际上对静态分析器来说是一个非常有用的功能。


#array_is_list函数rfc

您可能偶尔不得不处理这个问题:确定数组的键是否按数字顺序排列,从索引 0 开始。就像json_encode决定数组应该编码为数组还是对象一样。

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
}

#fsync函数rfc

PHP 8.1 添加了fsyncfdatasync函数以强制将文件更改同步到磁盘并确保操作系统写入缓冲区在返回之前已被刷新。

$file = fopen("sample.txt", "w");

fwrite($file, "Some content");

if (fsync($file)) {
    echo "File has been successfully persisted to disk.";
}

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); // Run-time error

Nikita 分析了 Packagist 上排名前 2000 的软件包,仅发现了 23 个会受此更改影响的案例。我们可以得出这样的结论——从技术上讲,这种变化的影响很小,这就是内部人员决定在 PHP 8.1 中添加它的原因。请记住,考虑到它在我们代码中无处不在的积极性能影响,我们中的大多数人都会因这种变化而获胜。


#资源到对象迁移

这些变化是将所有资源转换为专用对象的长期愿景的一部分。您可以在此处阅读更多相关信息。

Fileinfo 函数与finfo对象

类似于finfo_filefinfo_open用于接受和返回资源的函数。从 PHP 8.1 开始,它们可以处理finfo对象。

带有IMAPConnection对象的IMAP 函数

就像文件信息更改一样,IMAP 的功能类似于资源imap_bodyimap_open但不再使用资源


#弃用将 null 传递给内部函数的不可为 null 的参数 rfc

这个变化很简单:内部函数目前接受null不可为空的参数,这个 RFC 不赞成这种行为。例如,这是目前可能的:

str_contains("string", null);

在 PHP 8.1 中,这些类型的错误会抛出弃用警告,在 PHP 9 中它们将被转换为类型错误。


#Autovivification false rfc

来自 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,它有一个默认值[],这样就不会影响到你的代码
  • MurmurHash3和的新支持xxHash

 

更多更改请查看: https://php.watch/versions/8.1

最后更新于 2021年11月27日

PHP8.1发布,新特性一览
标签: