m

全干工程师

PHP 8的新特性功能

PHP 8将在2020年11月26日发布。它是一个新的主要版本,这意味着它将引入一些重大更改,以及许多新功能和性能改进。 PHP 8目前正处于非常活跃的开发中,其第一个Alpha版预计将于2020年6月18日发布。

由于所做的重大更改,您很有可能需要对代码进行一些更改才能使其在PHP 8上运行。如果您始终与最新版本保持同步,那么升级也不应该太很难,因为在7. *版本之前,大多数重大更改都已弃用。不用担心,所有这些弃用都列在这篇文章中。

除重大更改外,PHP 8还带来了一组不错的新功能,例如JIT compiler, union types, attributes等。

新特性

从新功能开始,请记住PHP 8仍在积极开发中,因此此列表会随着时间的推移而增长。

Union types rfc

鉴于PHP具有动态类型化的性质,在很多情况下联合类型很有用。联合类型是两种或多种类型的集合,表示可以使用其中一种。

  
public function foo(Foo|Bar $input): int|float;  
  

请注意,void永远不能是联合类型的一部分,因为它表示“根本没有返回值”。此外,可以使用| null或使用现有的 ?来写可为nullable的并集。符号:

  
public function foo(Foo|null $foo): void;  
  
public function bar(?Bar $bar): void;  
  

JIT rfc

JIT(尽管及时)编译器有望显着提高性能,尽管并不总是在Web请求的上下文中。目前还没有完成任何准确的基准测试,但是肯定会到来。

如果您想进一步了解JIT对PHP的作用,您可以阅读我在这里写的另一篇文章

Attributes rfc

属性在其他语言中通常称为注释,它提供了一种向类添加元数据的方法,而无需解析文档块。

快速浏览一下,这是RFC中的属性外观示例:

  
use App\Attributes\ExampleAttribute;  
  
@@ExampleAttribute  
class Foo  
{  
@@ExampleAttribute  
public const FOO = 'foo';  
  
@@ExampleAttribute  
public $x;  
  
@@ExampleAttribute  
public function foo(@@ExampleAttribute $bar) { }  
}  
  
  
@@Attribute  
class ExampleAttribute  
{  
public $value;  
  
public function __construct($value)  
{  
$this->value = $value;  
}  
}  
  

请注意,此基本Attribute以前在原始RFC中称为PhpAttribute,但之后又被另一个RFC更改。如果您想深入了解属性如何工作以及如何建立自己的属性,您可以在此博客上深入了解属性。

Match expression rfc

您可以称之为switch表达式的大哥:match可以返回值,不需要break语句,可以组合条件,使用严格的类型比较,并且不执行任何类型强制。

看起来像这样:

  
$result = match($input) {  
0 => "hello",  
'1', '2', '3' => "world",  
};  
  

您可以在此处详细阅读match表达式。

Constructor property promotion rfc

该RFC添加了语法糖来创建值对象或数据传输对象。现在,PHP可以将它们组合为一个,而不必为它们指定类属性和构造函数

而不是这样做:

  
class Money  
{  
public Currency $currency;  
  
public int $amount;  
  
public function __construct(  
Currency $currency,  
int $amount,  
) {  
$this->currency = $currency;  
$this->amount = $amount;  
}  
}  
  

您现在可以执行以下操作:

  
class Money  
{  
public function __construct(  
public Currency $currency,  
public int $amount,  
) {}  
}  
  

关于Constructor property promotion的更多信息,您可以在这篇专门的文章中阅读。

New static return type rfc

虽然已经可以返回self,但是直到PHP 8为止,static才是有效的返回类型。考虑到PHP具有动态类型的性质,此功能对许多开发人员都非常有用。

  
class Foo  
{  
public function test(): static  
{  
return new static();  
}  
}  
  

New mixed type rfc

有人可能将其称为必要的邪恶:混合类型导致许多人产生混合的感觉。不过,有一个很好的论据:缺少类型可能意味着在PHP中会有很多事情:

  • 函数不返回任何内容或返回null
  • 我们期待着几种类型之一
  • 我们期待一个无法在PHP中提示类型的类型

由于上述原因,增加混合类型是一件好事。混合本身意味着以下类型之一:

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

请注意,mixed也可以用作参数或属性类型,而不仅仅是返回类型。
另请注意,由于mixed已经包含null,因此不允许将其设置为null。以下内容将触发错误:

  
// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.  
function bar(): ?mixed {}  
  

Throw expression rfc

该RFC将抛出从声明变为表达式,这使得在许多新地方抛出异常成为可能:

  
$triggerError = fn () => throw new MyError();  
  
$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');  
  

Inheritance with private methods rfc

以前,PHP曾经对公共,保护和私有方法应用相同的继承检查。换句话说:私有方法应遵循与保护方法和公共方法相同的方法签名规则。这是没有道理的,因为子类将无法访问私有方法。

该RFC更改了该行为,因此不再对私有方法执行这些继承检查。此外,使用最终私有函数也没有意义,因此现在将触发警告:

Warning: Private methods cannot be final as they are never overridden by other classes  

Weak maps rfc

在PHP 7.4中添加的weakrefs RFC的基础上,PHP 8中添加了WeakMap实现。WeakMaps保存对对象的引用,这不会阻止垃圾回收这些对象。

以ORM为例,它们通常实现缓存,该缓存保存对实体类的引用,以提高实体之间关系的性能。只要该高速缓存具有对它们的引用,就不能对其进行垃圾回收,即使该高速缓存是唯一引用它们的对象也是如此。

如果此缓存层使用了弱引用和映射,则PHP将在没有其他引用时垃圾收集这些对象。尤其是在ORM的情况下,它可以管理一个请求中的数百个(如果不是数千个)实体。弱映射可以提供一种更好,更资源友好的方式来处理这些对象。

这是RFC的示例,它是弱映射的样子:

  
class Foo  
{  
private WeakMap $cache;  
  
public function getSomethingWithCaching(object $obj): object  
{  
return $this->cache[$obj]  
??= $this->computeSomethingExpensive($obj);  
}  
}  
  

Allowing ::class on objects rfc

一个小的但有用的新功能:现在可以在对象上使用::class,而不必在对象上使用get_class()。它的工作方式与get_class()相同。

  
$foo = new Foo();  
  
var_dump($foo::class);  
  

Non-capturing catches rfc

每当您想在PHP 8之前捕获异常时,无论是否使用该变量,都必须将其存储在变量中。使用非捕获的捕获,您可以忽略变量,所以代替这个:

  
try {  
// Something goes wrong  
} catch (MySpecialException $exception) {  
Log::error("Something went wrong");  
}  
  

您现在可以执行以下操作:

  
try {  
// Something goes wrong  
} catch (MySpecialException) {  
Log::error("Something went wrong");  
}  
  

请注意,必须始终指定类型,不允许您使用空捕获。如果要捕获所有异常和错误,可以使用Throwable作为捕获类型。

Trailing comma in parameter lists rfc

调用函数时已经有可能,但参数列表中仍然缺少尾部逗号支持。 PHP 8现在允许使用它,这意味着您可以执行以下操作:

  
public function(  
string $parameterA,  
int $parameterB,  
Foo $objectfoo,  
) {  
// …  
}  
  

Create DateTime objects from interface

您已经可以使用DateTime :: createFromImmutable($immutableDateTime)DateTimeImmutable对象创建DateTime对象,但是另一种方法很棘手, 通过添加DateTime :: createFromInterface()DatetimeImmutable :: createFromInterface(),现在有一种通用的方法可以将DateTimeDateTimeImmutable对象彼此转换。

  
DateTime::createFromInterface(DateTimeInterface $other);  
  
DateTimeImmutable::createFromInterface(DateTimeInterface $other);  
  
  

New Stringable interface rfc

Stringable接口可用于键入暗示任何字符串或实现__toString()的提示。此外,每当一个类实现__toString()时,它就会自动实现幕后接口,而无需手动实现。

  
class Foo  
{  
public function __toString(): string  
{  
return 'foo';  
}  
}  
  
function bar(Stringable $stringable) { /* … */ }  
  
bar(new Foo());  
bar('abc');  
  

New str_contains() function

有人可能会说它早该过期了,但是我们终于不必再依赖strpos来知道一个字符串是否包含另一个字符串。

if (strpos('string with lots of words', 'words') !== false) { /* … */ }  

之后:

  
if (str_contains('string with lots of words', 'words')) { /* … */ }  
  

New str_starts_with() and str_ends_with() functions rfc

早该逾期的另外两个,现在已在核心中添加了这两个功能。

  
str_starts_with('haystack', 'hay'); // true  
str_ends_with('haystack', 'stack'); // true  
  

New get_debug_type() function rfc

get_debug_type()返回变量的类型。听起来像gettype()会做什么? get_debug_type()为数组,字符串,匿名类和对象返回更有用的输出。 例如,在类\Foo\Bar上调用gettype()将返回对象。使用get_debug_type()将返回类名称。 可以在RFC中找到get_debug_typegettype之间的差异的完整列表。

Abstract methods in traits improvements rfc

特性可以指定抽象方法,这些方法必须由使用它们的类来实现。需要注意的是:在PHP 8之前,尚未验证这些方法实现的签名。以下是有效的:

  
trait Test {  
abstract public function test(int $input): int;  
}  
  
class UsesTrait  
{  
use Test;  
  
public function test($input)  
{  
return $input;  
}  
}  
  

当使用特征并实现其抽象方法时,PHP 8将执行正确的方法签名验证。这意味着您需要编写以下代码:

  
class UsesTrait  
{  
use Test;  
  
public function test(int $input): int  
{  
return $input;  
}  
}  
  

Object implementation of token_get_all() rfc

token_get_all函数返回一个值数组。该RFC使用PhpToken::getAll()方法添加了PhpToken类。此实现适用于对象而不是普通值。它消耗更少的内存,更易于阅读。

Variable syntax tweaks rfc

在RFC中:“统一变量语法RFC解决了PHP变量语法中的许多不一致之处。该RFC旨在解决一小部分被忽略的情况。”

ext-json always available rfc

以前,可以在未启用JSON扩展的情况下编译PHP,现在再也无法实现。由于JSON的使用如此广泛,因此最好的开发人员始终可以依赖JSON,而不必确保扩展名首先存在。

Breaking changes

如前所述:这是一个重大更新,因此会有重大更改。最好的办法是在UPGRADING文档中查看突破性更改的完整列表。

但是,其中许多重大更改在以前的7. *版本中已被弃用,因此,如果您多年来一直保持最新,那么升级到PHP 8并不难。

Consistent type errors rfc

PHP中的用户定义函数将已经抛出TypeErrors,但是内部函数没有抛出,而是发出警告并返回null。从PHP 8开始,内部函数的行为已变得一致。

Reclassified engine warnings rfc

以前仅触发警告或通知的许多错误已转换为适当的错误。以下警告已更改。

  • Undefined variable: Error exception instead of notice
  • Undefined array index: warning instead of notice
  • Division by zero: DivisionByZeroError exception instead of warning
  • Attempt to increment/decrement property ‘%s’ of non-object: Error exception instead of warning
  • Attempt to modify property ‘%s’ of non-object: Error exception instead of warning
  • Attempt to assign property ‘%s’ of non-object: Error exception instead of warning
  • Creating default object from empty value: Error exception instead of warning
  • Trying to get property ‘%s’ of non-object: warning instead of notice
  • Undefined property: %s::$%s: warning instead of notice
  • Cannot add element to the array as the next element is already occupied: Error exception instead of warning
  • Cannot unset offset in a non-array variable: Error exception instead of warning
  • Cannot use a scalar value as an array: Error exception instead of warning
  • Only arrays and Traversables can be unpacked: TypeError exception instead of warning
  • Invalid argument supplied for foreach(): TypeError exception instead of warning
  • Illegal offset type: TypeError exception instead of warning
  • Illegal offset type in isset or empty: TypeError exception instead of warning
  • Illegal offset type in unset: TypeError exception instead of warning
  • Array to string conversion: warning instead of notice
  • Resource ID#%d used as offset, casting to integer (%d): warning instead of notice
  • String offset cast occurred: warning instead of notice
  • Uninitialized string offset: %d: warning instead of notice
  • Cannot assign an empty string to a string offset: Error exception instead of warning
  • Supplied resource is not a valid stream resource: TypeError exception instead of warning

@运算符不再使致命错误消失

此更改可能会揭示出PHP 8之前再次隐藏的错误。请确保在生产服务器上将display_errors = Off设置为OFF!

默认错误报告级别

现在是E_ALL,而不是E_NOTICE和E_DEPRECATED。这意味着可能会弹出许多错误,虽然以前在PHP 8之前已经存在这些错误,但这些错误以前是被默默忽略的。

Default PDO error mode rfc

从RFC:当前,PDO的默认错误模式为静默。这意味着,当发生SQL错误时,除非开发人员实施自己的显式错误处理,否则不会发出错误或警告,也不会引发异常。

此RFC更改后,默认错误将在PHP 8中更改为PDO::ERRMODE_EXCEPTION

Concatenation precedence rfc

尽管PHP 7.4中已弃用,但此更改现已生效。如果您要编写这样的内容:

echo "sum: " . $a + $b;  

PHP以前会这样解释它:

echo ("sum: " . $a) + $b;  

PHP 8将使其如此解释:

echo "sum: " . ($a + $b);  

Reflection method signature changes

反射类的三个方法签名已更改:

ReflectionClass::newInstance($args);  
ReflectionFunction::invoke($args);  
ReflectionMethod::invoke($object, $args);  

现在:

ReflectionClass::newInstance(...$args);  
ReflectionFunction::invoke(...$args);  
ReflectionMethod::invoke($object, ...$args);  

升级指南指定,如果您扩展这些类,并且仍要同时支持PHP 7和PHP 8,则允许以下签名:

ReflectionClass::newInstance($arg = null, ...$args);  
ReflectionFunction::invoke($arg = null, ...$args);  
ReflectionMethod::invoke($object, $arg = null, ...$args);  

Stable sorting rfc

在PHP 8之前,排序算法是不稳定的。这意味着不能保证相等元素的顺序。 PHP 8将所有排序功能的行为更改为稳定排序。

Fatal error for incompatible method signatures rfc

从RFC:由于不兼容的方法签名而导致的继承错误当前会引发致命错误或警告,具体取决于错误原因和继承层次结构。

Other deprecations and changes

在PHP 7. *开发过程中,添加了几个弃用版本,这些弃用现已在PHP 8中完成。

文章翻译于: https://stitcher.io/blog/new-in-php-8, 感谢原作者。

留言