PHP 8.2 新特性

PHP 8.2 将于2022 年 11 月 24 日发布。在这篇文章中,我们将一一介绍所有功能、性能改进、更改和弃用。

#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

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

#弃用动态属性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';

stdClass的对象也是如此,它们将继续支持动态属性。

我可以想象有些开发人员依赖动态属性,他们对这种变化不满意。我认为这些开发人员将从更深入地研究静态分析中受益。如果您想了解更多信息,可以查看我的 PHP 之路:静态分析系列!

如果您升级到 PHP 8.2 时不想看到这些警告,您可以做一些事情。

您可以在仍应允许这些属性的类上使用#[AllowDynamicProperties]属性:

#[AllowDynamicProperties]
class Post
{
    public string $title;
}

// …

$post->name = 'Name'; // All fine

或者您可以简单地禁用弃用警告。我不建议这样做,因为你会遇到 PHP 9.0 的麻烦,但这里是你如何在 PHP 中禁用弃用警告的方法:

error_reporting(E_ALL ^ E_DEPRECATED);

 

 

 


#析取范式类型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的类访问该常量,因为它是公共的:

class MyClass
{
    use Foo;
}

MyClass::CONSTANT; // 1

#编辑回溯中的参数rfc

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

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

function test(
    $foo,
    #[\SensitiveParameter] $bar,
    $baz
) {
    throw new Exception('Error');
}
 
test('foo', 'bar', 'baz');
 
Fatal error: Uncaught Exception: Error in test.php:8
Stack trace:
#0 test.php(11): test('foo', Object(SensitiveParameterValue), 'baz')
#1 {main}
  thrown in test.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

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


#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-1UTF-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) 。


#随机扩展的改进rfc

PHP 的 Random 扩展有许多在首次添加时被忽略的怪癖。这个 RFC 修复了许多这些怪癖。


#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)调用的callables,但不能通过直接调用$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

PHP 8.2 新特性
标签: