PHP 8.2 新特性

PHP 8.2 将于2022 年 12 月 8 日发布。在本文中,我们将逐一介绍所有新特性、性能改进、更改和弃用。

只读类RFC

PHP 8.1 中引入了只读属性。此 RFC 构建在它们之上,并添加了语法糖以使所有类属性同时变为只读。代替这样写:

class Post
{
    public function __construct(
        public readonly string $title, 
        public readonly Author $author,
        public readonly string $body,
        public readonly DateTime $publishedAt,
    ) {}
}

你现在可以这样写:

readonly class Post
{
    public function __construct(
        public string $title, 
        public Author $author,
        public string $body,
        public DateTime $publishedAt,
    ) {}
}

从功能上讲,将类设置为只读与将所有属性设置为只读完全相同;但它也会阻止在类上添加动态属性:

$post = new Post(/* … */);

$post->unknown = 'wrong';

Uncaught Error: Cannot create dynamic property Post::$unknown

请注意,如果子类也是只读的,则只能从只读类扩展。

PHP 已经发生了很大的变化,只读类是一个受欢迎的补充。


#弃用动态属性RFC

我会说这是一个更好的改变,但它会受到一点伤害。动态属性在 PHP 8.2 中被弃用,在 PHP 9.0 中将抛出一个ErrorException

class Post
{
    public string $title;
}

// …

$post->name = 'Name';

请记住,类实现__get__set仍将按预期工作:

class Post
{
    private array $properties = [];
    
    public function __set(string $name, mixed $value): void
    {
        $this->properties[$name] = $value;
    }
}

// …

$post->name = 'Name';

如果你想了解更多关于为什么弃用有用以及如何处理它们,你可以阅读这篇关于如何处理弃用的后续文章.


#新的随机扩展RFC

PHP 8.2 增加了一个新的随机数生成器,修复了前一个随机数生成器的很多问题:性能更高,更安全,更易于维护,并且不依赖于全局状态;消除了使用 PHP 的随机函数时一系列难以检测的错误。

有一个名为 的新类Randomizer,它接受一个随机发生器引擎。现在您可以根据需要更改该引擎。例如,区分生产环境和测试环境。

$rng = $is_production
    ? new Random\Engine\Secure()
    : new Random\Engine\Mt19937(1234);
 
$randomizer = new Random\Randomizer($rng);
$randomizer->shuffleString('foobar');

#nulltrue, 和false作为独立类型RFC

PHP 8.2 添加了三种新类型——或者类似的东西。在这篇文章中,我们将避免陷入类型安全的困境,但从技术上讲nulltrue, 和false可以被视为它们自己的有效类型。常见的例子是 PHP 的内置函数,其中false用作发生错误时的返回类型。例如在file_get_contents

file_get_contents(/* … */): string|false

在 PHP 8.2 之前,您已经可以false将其他类型作为联合使用;但现在它也可以用作独立类型:

function alwaysFalse(): false
{
    return false;
}

现在同样适用于truenull


#析取范式类型RFC

DNF 类型允许我们结合并集和交集类型,遵循一个严格的规则:当结合并集和交集类型时,交集类型必须用括号分组。实际上,它看起来像这样:

function generateSlug((HasTitle&HasId)|null $post) 
{
    if ($post === null) {
        return '';
    }

    return 
        strtolower($post->getTitle()) 
        . $post->getId();
}

在这种情况下,(HasTitle&HasId)|null是 DNF 类型。

这是一个很好的补充,特别是因为它意味着我们现在可以拥有可为空的交集类型,这可能是此功能最重要的用例。


#traits中的常量RFC

您现在可以在traits中使用常量:

trait Foo 
{
    public const CONSTANT = 1;
 
    public function bar(): int 
    {
        return self::CONSTANT;
    }
}

您将无法通过trait名称访问常量,无论是从trait外部还是从trait内部。

trait Foo 
{
    public const CONSTANT = 1;
 
    public function bar(): int 
    {
        return Foo::CONSTANT;
    }
}

Foo::CONSTANT;

但是,您可以通过使用trait的类访问常量,前提是它是public的:

class MyClass
{
    use Foo;
}

MyClass::CONSTANT; // 1

#编辑回溯中的参数RFC

任何代码库中的常见做法是将生产错误发送到跟踪它们的服务,并在出现问题时通知开发人员。这种做法通常涉及通过网络将堆栈跟踪发送到第三方服务。然而,在某些情况下,这些堆栈跟踪可能包含敏感信息,例如环境变量、密码或用户名。

PHP 8.2 允许您使用属性标记此类“敏感参数”,这样您就不必担心在出现问题时它们会被列在您的堆栈跟踪中。这是来自 RFC 的示例:

function login(
    string $user,
    #[\SensitiveParameter] string $password
) {
    // …
    
    throw new Exception('Error');
}
 
login('root', 'root');
 
Fatal error: Uncaught Exception: Error in login.php:8
Stack trace:
#0 login.php(11): login('root', Object(SensitiveParameterValue))
#1 {main}
  thrown in login.php on line 8

#在 const 表达式中获取枚举的属性RFC

来自 RFC:

此 RFC 建议允许使用->/?->来获取常量表达式中枚举的属性。此更改的主要动机是允许在不允许枚举对象的地方获取名称和值属性,例如数组键

这意味着以下代码现在有效:

enum A: string 
{
    case B = 'B';
    
    const C = [self::B->value => self::B];
}

#DateTime::createFromImmutable()和DateTimeImmutable::createFromMutable()的返回类型更改breaking

以前,这些方法看起来像这样:

DateTime::createFromImmutable(): DateTime
DateTimeImmutable::createFromMutable(): DateTimeImmutable

在 PHP 8.2 中,这些方法签名更改如下:

DateTime::createFromImmutable(): static
DateTimeImmutable::createFromMutable(): static

这种变化更有意义,因为它提高了从DateTimeDateTimeImmutable扩展的类的静态洞察可能性。但是,从技术上讲,这是一项重大更改,可能会影响从这两个类中的任何一个扩展的自定义实现。


#utf8_encode()utf8_decode()弃用RFC

在 PHP 8.2 中,使用utf8_encode()utf8_decode()将触发这些弃用通知:

Deprecated: Function utf8_encode() is deprecated
Deprecated: Function utf8_decode() is deprecated

RFC 认为这些函数的名称不准确,经常会引起混淆:这些函数仅在 和 之间ISO-8859-1进行转换UTF-8,而函数名称暗示了更广泛的用途。在RFC中有关于推理的更详细解释。

替代方案?RFC 建议改为使用mb_convert_encoding()


#语言环境不敏感strtolower()strtoupper() breaking RFC

strtolower()strtoupper()都不再对语言环境敏感。如果您想要本地化的大小写转换,您可以使用mb_strtolower()


#几个 SPL 方法的签名更改breaking

SPL 类的几种方法已更改以正确执行其正确的类型签名:

SplFileInfo::_bad_state_ex()
SplFileObject::getCsvControl()
SplFileObject::fflush()
SplFileObject::ftell()
SplFileObject::fgetc()
SplFileObject::fpassthru()
SplFileObject::hasChildren()
SplFileObject::getChildren()

#PCRE 中的新n修饰符升级

您现在可以在pcre*函数中使用n修饰符 ( NO_AUTO_CAPTURE) 。


#ODBC 用户名和密码转义breaking

升级指南:

ODBC连接字符串和用户名/密码都被传递时,扩展现在转义用户名和密码,并且必须附加字符串。

这同样适用于PDO_ODBC.

 

#弃用${}字符串插值RFC

PHP 有几种在字符串中嵌入变量的方法。此 RFC 弃用了两种这样做的方式,因为它们很少使用,并且经常导致混淆:

"Hello ${world}";
Deprecated: Using ${} in strings is deprecated
 
"Hello ${(world)}";
Deprecated: Using ${} (variable variables) in strings is deprecated

需要明确的是:两种流行的字符串插值方式仍然有效:

"Hello {$world}";
"Hello $world";

#弃用部分支持的可调用对象RFC

另一个变化虽然影响略小,但现在也弃用了部分支持的可调用对象。部分支持的可调用对象是可以使用call_user_func($callable) 调用但不能用$callable()直接调用的可调用对象。顺便说一句,这些类型的可调用对象的列表相当短:

"self::method"
"parent::method"
"static::method"
["self", "method"]
["parent", "method"]
["static", "method"]
["Foo", "Bar::method"]
[new Foo, "Bar::method"]

这样做的原因是什么?这是朝着能够将callable用于类型化属性的正确方向迈出的一步。Nikita 在 RFC 中解释得很好:

所有这些可调用对象都是上下文相关的。“self::method”指代的方法取决于从哪个类执行调用或可调用性检查。实际上,当以 [new Foo, "parent::method"] 的形式使用时,这通常也适用于最后两种情况。

减少可调用对象的上下文依赖性是此 RFC 的次要目标。在此 RFC 之后,唯一仍然存在的范围依赖性是方法可见性:“Foo::bar”可能在一个范围内可见,但在另一个范围内不可见。如果将来可调用对象仅限于公共方法(而私有方法必须使用一流的可调用对象或 Closure::fromCallable() 才能使其范围独立),那么可调用类型将变得明确定义并且可以用作属性类型。但是,不建议将可见性处理更改作为此 RFC 的一部分。


目前就这些了,我将全年更新此列表。如果你想收到不定期的更新,你可以订阅我的时事通讯!

更多请参见:https://php.watch/versions/8.2

 

参考

https://stitcher.io/blog/new-in-php-82

最后更新于 2022年12月3日

PHP 8.2 新特性
标签: