PHP 8.1.2 Released!
已发布!
PHP 8.1 是 PHP 语言的一个主版本更新。
它包含了许多新功能,包括枚举、只读属性、First-class 可调用语法、纤程、交集类型和性能改进等。

枚举 RFC 文档

PHP < 8.1
class Status
{
    const
DRAFT = 'draft';
    const
PUBLISHED = 'published';
    const
ARCHIVED = 'archived';
}
function
acceptStatus(string $status) {...}
PHP 8.1
enum Status
{
    case
Draft;
    case
Published;
    case
Archived;
}
function
acceptStatus(Status $status) {...}
使用枚举而不是一组常量并立即进行验证。

只读属性 RFC 文档

PHP < 8.1
class BlogData
{
    private
Status $status;
  
    public function
__construct(Status $status)
    {
       
$this->status = $status;
    }
   
    public function
getStatus(): Status
   
{
        return
$this->status;   
    }
}
PHP 8.1
class BlogData
{
    public
readonly Status $status;
  
    public function
__construct(Status $status)
    {
       
$this->status = $status;
    }
}

只读属性不能在初始化后更改,即在为它们分配值后。它们可以用于对值对象和数据传输对象建模。

First-class 可调用语法 RFC 文档

PHP < 8.1
$foo = [$this, 'foo'];

$fn = Closure::fromCallable('strlen');
PHP 8.1
$foo = $this->foo(...);

$fn = strlen(...);

现在可以获得对任何函数的引用。这统称为 First-class 可调用语法。

新的初始化器 RFC

PHP < 8.1
class Service
{
    private
Logger $logger;

    public function
__construct(
        ?
Logger $logger = null,
    ) {
       
$this->logger = $logger ?? new NullLogger();
    }
}
PHP 8.1
class Service
{
    private
Logger $logger;
   
    public function
__construct(
       
Logger $logger = new NullLogger(),
    ) {
       
$this->logger = $logger;
    }
}

对象现在可以用作默认参数值、静态变量和全局常量,以及属性参数。

这有效地使使用 嵌套属性 成为可能。

PHP < 8.1
class User
{
   
/**
     * @Assert\All({
     *     @Assert\NotNull,
     *     @Assert\Length(min=5)
     * })
     */
   
public string $name = '';
}
PHP 8.1
class User
{
   
#[\Assert\All(
       
new \Assert\NotNull,
        new \
Assert\Length(min: 6))
    ]
    public
string $name = '';
}

纯交集类型 RFC 文档

PHP < 8.1
function count_and_iterate(Iterator $value) {
    if (!(
$value instanceof Countable)) {
        throw new
TypeError('value must be Countable');
    }

    foreach (
$value as $val) {
        echo
$val;
    }

   
count($value);
}
PHP 8.1
function count_and_iterate(Iterator&Countable $value) {
    foreach (
$value as $val) {
        echo
$val;
    }

   
count($value);
}

当一个值需要同时满足多个类型约束时,使用交集类型。

注意,目前无法将交集和联合类型混合在一起,例如 A&B|C

Never 返回类型 RFC 文档

PHP < 8.1
function redirect(string $uri) {
   
header('Location: ' . $uri);
    exit();
}

function
redirectToLoginPage() {
   
redirect('/login');
    echo
'Hello'; // <- dead code
}
PHP 8.1
function redirect(string $uri): never {
   
header('Location: ' . $uri);
    exit();
}

function
redirectToLoginPage(): never {
   
redirect('/login');
    echo
'Hello'; // <- dead code detected by static analysis
}

使用 never 类型声明的函数或方法表示它不会返回值,并且会抛出异常或通过调用 die()exit()trigger_error() 或类似的东西来结束脚本的执行。

Final 类常量 RFC 文档

PHP < 8.1
class Foo
{
    public const
XX = "foo";
}

class
Bar extends Foo
{
    public const
XX = "bar"; // No error
}
PHP 8.1
class Foo
{
    final public const
XX = "foo";
}

class
Bar extends Foo
{
    public const
XX = "bar"; // Fatal error
}

可以声明 final 类常量,以禁止它们在子类中被重写。

显式八进制数字表示法 RFC 文档

PHP < 8.1
016 === 16; // false because `016` is octal for `14` and it's confusing
016 === 14; // true
PHP 8.1
0o16 === 16; // false — not confusing with explicit notation
0o16 === 14; // true

现在可以使用显式 0o 前缀表示八进制数。

纤程 RFC 文档

PHP < 8.1
$httpClient->request('https://example.com/')
        ->
then(function (Response $response) {
            return
$response->getBody()->buffer();
        })
        ->
then(function (string $responseBody) {
            print
json_decode($responseBody)['code'];
        });
PHP 8.1
$response = $httpClient->request('https://example.com/');
print
json_decode($response->getBody()->buffer())['code'];

Fibers 是用于实现轻量级协作并发的基础类型。它们是一种创建可以像生成器一样暂停和恢复的代码块的方法,但可以从堆栈中的任何位置进行。Fibers 本身并没有提供并发性,仍然需要一个事件循环。但是,它们允许通过阻塞和非阻塞实现共享相同的 API。

Fibers 允许摆脱以前在 Promise::then() 或基于生成器的协程中看到的样板代码。库通常会围绕 Fiber 构建进一步的抽象,因此无需直接与它们交互。

对字符串键控数组的数组解包支持 RFC 文档

PHP < 8.1
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];

$result = array_merge(['a' => 0], $arrayA, $arrayB);

// ['a' => 1, 'b' => 2]
PHP 8.1
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];

$result = ['a' => 0, ...$arrayA, ...$arrayB];

// ['a' => 1, 'b' => 2]

PHP 以前支持通过扩展运算符在数组内部解包,但前提是数组具有整数键。现在也可以使用字符串键解包数组。

性能改进

Symfony Demo App 请求时间
25 次连续运行,250 次请求(秒)
(越少越好)

结果(相对于 PHP 8.0):

  • Symfony Demo 有 23.0% 的提升
  • WordPress 有 3.5% 的提升

PHP 8.1 中与性能相关的特性:

  • ARM64 的 JIT 后端 (AArch64)
  • 继承缓存(避免在每个请求中重新链接类)
  • 快速解析类名(避免小写和哈希查找)
  • timelib 和 ext/date 性能改进
  • SPL 文件系统迭代器改进
  • serialize/unserialize 优化
  • 一些内部函数优化(get_declared_classes()、explode()、strtr()、strnatcmp() 和 dechex())
  • JIT 的改进和修复

新的类、接口和函数

  • #[ReturnTypeWillChange] 属性。
  • fsyncfdatasync 函数。
  • array_is_list 函数。
  • Sodium XChaCha20 函数。

弃用和向后不兼容

  • 向非空值的内部函数参数传递空值的做法已被弃用。
  • PHP 内置类方法中的暂定返回类型
  • Serializable 接口已弃用。
  • html_entity_encode/html_entity_decode 函数默认处理单引号和用 Unicode 替换字符来替换无效字符。
  • $GLOBALS 变量限制。
  • MySQLi:默认错误模式设置为异常。
  • 隐式不兼容的 float 到 int 转换已被弃用。
  • finfo 扩展:file_info 资源迁移到现有的 finfo 对象。
  • IMAP:imap 资源迁移到 IMAP\Connection 类对象。
  • FTP 扩展:连接资源迁移到 FTP\Connection 类对象。
  • GD 扩展:字体标识符迁移到 GdFont 类对象。
  • LDAP:资源类型迁移到 LDAP\ConnectionLDAP\ResultLDAP\ResultEntry 对象。
  • PostgreSQL:资源类型迁移到 PgSql\ConnectionPgSql\ResultPgSql\Lob 对象。
  • Pspell:pspell 和 pspell config 资源类型迁移到 PSpell\DictionaryPSpell\Config 类对象。
To Top