PHP8 的新特性和重大变化
PHP8将于 2020 年 11 月 26 日发布,关于此版本中即将推出的功能的大量文章 现已发布!由于这是一个主要版本,将会有新功能以及重大更改,因此了解正在发生的变化非常重要。这将使您更容易思考 PHP8 将如何影响您的应用程序以及您需要采取哪些措施来确保您可以顺利升级。
我想我会经历一些主要的变化,看看下一个 PHP 版本会发生什么。
运行 PHP8
一个好的第一步是查看如何安装 PHP8,以便您可以自己检查。如果您使用的是 Ubuntu,那么安装 PHP8 的最简单方法是使用现有的 ondrej/php PPA 库。这可以使用以下命令安装。
sudo add-apt-repository ppa:ondrej/phpsudo apt-get update
您可以通过搜索 PHP8 来确保安装了正确的库。
sudo apt-cache search php8.0
假设您看到上一条命令的一些输出,您现在可以使用此命令安装 PHP8。
sudo apt-get install php8.0
运行后,您现在可以运行 PHP8。
$ php --versionPHP 8.0.0rc1 (cli) (built: Oct 18 2020 19:43:43) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies
with Zend OPcache v8.0.0rc1, Copyright (c), by Zend Technologies
除了自己安装之外,您现在还可以在以下平台上运行 PHP8。
场地
平台.sh
您还可以在 ExtendsClass 站点上运行 PHP8 代码片段。
如果您也使用这些平台进行托管,这会很方便,因为它可以帮助您在发布前的大量时间内测试您的应用程序。如果您知道任何其他运行支持 PHP8 的平台,请发表评论并告诉我。
新特性
PHP8 中有很多新特性。我在互联网上阅读了大量关于这些新功能的文章,但我想我会详细介绍每一个。
即时编译器 (RFC)
Just-In-Time(或 JIT)编译器是 PHP7 发布之前速度改进的结果。它被引入到 PHP8 中,因为如果不使用 JIT,可能无法进行更多的速度改进。这个想法是它将进一步提高 PHP 性能。
当 PHP 代码被执行时,它被翻译成字节码,这些字节码用于执行程序中的步骤。JIT 意味着 PHP 将分析正在执行的代码,并能够在代码执行时实时做出性能改进决策。这个想法是它将用于 CPU 密集型应用程序,而不一定用于基于 Web 的场景。这意味着服务器端 PHP 应用程序在 PHP 中内置的 JIT 系统中可能更流行。
要使用 JIT,您首先需要激活它。在我的测试系统 (Ubuntu 20.04) 上,我已经安装了 PHP opcache 模块,该模块与主要的 PHP8 包一起安装。这是在位于 /etc/php/8.0/cli/conf.d/10-opcache.ini 的文件中配置的。
要激活 JIT,您需要启用 opcache 并为opcache.jit_buffer_size设置分配一些内存。这就是文件在我的系统上的样子。
; configuration for php opcache module; priority=10
zend_extension=opcache.so
opcache.enable_cli=1
opcache.jit_buffer_size=256M
您可以确保它处于活动状态,您可以使用该opcache_get_status()功能。您可以查看此数组的“jit”部分以获取有关 JIT 当前状态的信息。
var_dump(opcache_get_status()['jit']);
如果 JIT 已正确激活,这应该打印出如下内容。
array(7) {["enabled"]=>
bool(true)
["on"]=>
bool(true)
["kind"]=>
int(5)
["opt_level"]=>
int(4)
["opt_flags"]=>
int(6)
["buffer_size"]=>
int(268435440)
["buffer_free"]=>
int(268432880)
}
那么它更快吗?一句话,是的。我见过一些人使用 mandlebrot 集进行基准测试,因此我决定使用我不久前创建的库,该库在 PHP 中绘制了几种不同类型的分形。我所做的只是生成三个分形以及使用该microtime()函数花费的时间。这是 PHP 7.4.8 的结果。
Burningship84.20269203186
Mandlebrot
21.552599906921
Tricorn
32.685042858124
当我在PHP8跑完全相同的代码,这是显着更快。数字不言自明。
Burningship15.272277116776
Mandlebrot
3.7528541088104
Tricorn
4.4957919120789
这种大规模的速度提升真的很有趣。我在这里使用的代码创建了大小合适的分形,但我记得在创建代码时,我大部分时间都花在等待生成分形上。这个添加对我很感兴趣,因为我已经将 PHP 推到了它的极限几次(除了生成分形)。我可以看到这对 PHP 的未来有真正的好处,并且允许在通常的网站语言之外的情况下选择该语言。
我没有看到像 Drupal 或 WordPress 这样的应用程序的速度提升,但从我读到的内容来看,这些类型的应用程序可能几乎没有区别。将来我将在这些平台上运行基准测试,看看 JIT 在那里产生了什么样的不同。
联合类型 (RFC)
从 PHP7 开始,可以规定参数和返回值必须具有什么样的类型。如果您传递的参数类型与预期类型不同,这将允许 PHP 抛出错误。在像 PHP 这样的松散类型语言中,确保函数接收和生成正确类型的值很重要。
在 PHP8 中,现在可以为参数和返回值规定不同的类型,由管道字符分隔。这是一个可以接受整数或浮点值的函数示例。
function addNumbers(int|float $number1, int|float $number2) : int|float{
return $number1 + $number2;
}
以前,需要在没有任何类型提示的情况下创建这样的函数,因为如果传递的参数不正确,PHP 可以静默地转换类型。这意味着如果我们将参数类型设置为整数,那么 PHP 会将任何浮点值转换为整数,如果您不进行单元测试,这可能会导致一些棘手的错误被捕获。
要使用上述函数,我们只需像其他函数一样调用它即可。
echo addNumbers(1, 1); // prints 2echo addNumbers(1.1, 1.1); // prints 2.2
如果我们尝试像这样将字符串传递给函数。
echo addNumbers('one', 'two');
我们将收到一个 PHP 致命错误,指出我们需要将 int 或 float 传递到函数中。
PHP Fatal error: Uncaught TypeError: addNumbers(): Argument #1 ($number1) mustbe of type int|float, string given, called in union_types.php on line 10 and defined
in union_types.php:3
这是一个简单的示例,但我们可以对此进行扩展并创建一个接受多种不同类型变量的函数。下面是一个设计非常糟糕的函数的例子,它展示了如何扩展这个特性。
function printNumber(int|float|string|array|bool|null $number) : void{
printf("%f\n", $number);
}
这条规则的例外是void类型,可以在上面的函数中看到。void 类型不能用作联合类型,因为它规定函数将不返回任何内容。换句话说,你不能说一个函数会返回一个整数或一个空值,否则你会收到一个 PHP 致命错误。
虽然一个简单的改变我可以看到这个特性被使用了很多,因为它以前只能在注释中规定不同类型的值,这导致文档块注释变得比代码更具描述性。
Nullsafe 运算符 (RFC)
除了空合并运算符之外,现在还可以直接从方法中检测空返回值。如果您不知道,空合并运算符允许我们获得一个值而无需测试该值是否存在,如果第一个值为空,则返回一个不同的值。这意味着我们可以这样做以从 $_GET 超级全局获取一个值,如果该值不存在,则为 0。
$page = $_GET['page'] ?? 0;echo $page;
nullsafe 运算符以类似的方式工作,但允许我们创建一个方便的快捷方式来在尝试使用该值之前测试方法的 null 返回。使用以下几个创建 Horn 和 Car 对象的类。
class Horn {public function beep() {
print 'beep';
}
}
class Car {
protected $horn;
public function setHorn(Horn $horn) {
$this->horn = $horn;
}
public function getHorn() {
return $this->horn;
}
}
在这种情况下,可以创建一个没有 Horn 对象的 Car 对象来驱动喇叭。在这种情况下,我们需要确保该getHorn()方法在尝试使用之前不会返回 null。通常这意味着 if 语句和is_null()检查以确保事物存在,但是使用 nullsafe 运算符我们可以内联执行此操作。下面的示例创建一个 Car 对象,并且beep()仅当该getHorn()方法不返回空值时才调用该方法。
$car = new Car();$car->getHorn()?->beep();
我可以看到这在 Drupal 中非常有用,我倾向于编写大量检查代码以确保对象属性或方法的返回值在使用它们之前确实包含在其中。由于 Drupal 中对象包含的内容具有上下文性质,因此这是必要的,并且此更改肯定会简化某些检查。
命名参数 (RFC)
命名参数允许您调用函数并规定不同的参数顺序。取以下具有两个参数的简单函数并将数组填充到指定长度。
function fillArray(array $arrayToFill, int $number) : array{
for ($i = 0; $i < $number; ++$i) {
$arrayToFill[$i] = 1;
}
return $arrayToFill;
}
我们可以通过按照定义的顺序传递参数来以正常方式调用此方法。
$newArray = fillArray([], 2);
从 PHP8 开始,我们现在可以在将参数传递给函数时命名参数,这也允许我们以我们想要的任何顺序发送参数。
$newArray = fillArray(number: 2, arrayToFill: []);
一个简单的例子,但它也可以使代码更具可读性。
这种技术适用于 PHP 中的任何函数,而不仅仅是用户定义的函数。PHP 是一种语言,其中数组和字符串函数具有不同的参数顺序,这是一个非常受欢迎的补充。
属性 V2 (RFC1 RFC2 RFC3)
属性提供了一种向 PHP 类、方法、函数、类属性、函数参数和常量添加元数据的机制。它们不能通过代码直接访问,需要使用内置反射类的 PHP 拉出。ReflectionClass 类从 PHP5 开始就存在于 PHP 中,但 PHP8 的新getAttribute()方法是该方法。此方法返回一个包含属性信息的 ReflectionAttribute 对象数组。
这个添加经历了一些变化(正如您从上面的多个 RFC 中看到的那样),但当前向您的代码添加属性的语法如下。
#[Automobile]#[MyCustomAttribute(1, 'string')]
class Car {
#[ThingSetup(123)]
public function doThing() {}
}
如果我们实例化类,我们就可以使用 ReflectionClass 打印出包含在类级别上的属性信息。
$car = new Car();
$reflectionClass = new ReflectionClass($car);
foreach ($reflectionClass->getAttributes() as $attribute) {
echo $attribute->getName() . ' ' . PHP_EOL;
$arguments = $attribute->getArguments();
foreach ($arguments as $argument) {
echo '-' . $argument . PHP_EOL;
}
}
这将打印出以下内容。
AutomobileMyCustomAttribute
-1
-string
我们可以使用getMethods()ReflectionClass的方法来查找和报告 Car 类中方法的属性。
foreach ($reflectionClass->getMethods() as $method) {foreach ($method->getAttributes() as $attribute) {
echo $attribute->getName() . ' ' . PHP_EOL;
$arguments = $attribute->getArguments();
foreach ($arguments as $argument) {
echo '-' . $argument . PHP_EOL;
}
}
}
此代码打印出以下内容。
ThingSetup-123
PHP8 中实际上有很多属性,因此我建议您通读 RFC 以熟悉它们是什么以及如何将它们集成到您的代码中。
匹配表达式 (RFC)
新的匹配表达式就像一个简写的 switch 语句。它看起来有点像一个函数声明,它会根据传入的值返回一个值。match 语句的语法如下所示。
$value = 2;
$result = match($value) {
1 => 'One',
2 => 'Two',
3 => 'Three',
};
echo $result; // Prints 'Two'.
需要注意的一件事是 switch 语句使用“==”比较,而 match 语句使用“===”比较。此外,“match”这个词现在是一个保留关键字,因此您不能再有任何称为 match 的类或函数。
重大变化
由于这是 PHP 的新版本,因此会有一些更改会破坏现有代码,并且 PHP 团队一直在努力编制即将发布的版本中的破坏性更改列表。重大更改主要是由于在 PHP7 中标记为已弃用的代码在向 PHP8 的过渡过程中被删除。我在这里挑选了一些您可能想要了解的更有趣的变化。
构造方法删除
当面向对象被添加到 PHP 时,可以创建与类同名的构造函数方法,这与其他语言(如 Java)的工作方式类似。该__construct()方法是在 PHP5 中添加的,从那时起,建议将构造函数移至新格式。在 PHP8 中,不能再使用类名作为构造函数,因此必须使用__construct()方法格式。
删除each()方法
该each()方法从数组中检索下一项和键,并将指针向前移动一个。这是在从数组中获取值的同时将指针移动到数组中的下一项的便捷方法。这现在已被删除,应使用 foreach 或 ArrayIterator 代替。
移除了偏移访问的花括号 (RFC)
不再可能使用花括号访问数组中的元素或字符串中的字符。我不得不承认我最近没有看到有人使用它,但是您可能会发现一些包含这种代码的旧代码。这是取自 PHP 文档的此功能的示例。
$array = [1, 2];echo $array[1]; // prints 2
echo $array{1}; // also prints 2
$string = "foo";
echo $string[0]; // prints "f"
echo $string{0}; // also prints "f"
更严格的算术运算符类型检查 (RFC)
在以前的 PHP 版本中,对数组或对象运行算术或按位运算会返回一个无意义的值。
var_dump([123] % [321]); // Prints "int(0)".
在 PHP8 中运行相同的代码会产生一个 TypeError。
PHP Fatal error: Uncaught TypeError: Unsupported operand types: array % array in /vagrant/union_types.php:3
@ 操作符不再隐藏致命错误
由于可以在 PHP7 中捕获致命错误,因此也可以使用 @ 运算符来抑制它们。在 PHP8 中,情况不再如此,并且可能会发现隐藏在 PHP7 中的致命错误。确保在您的生产服务器上始终将 display_errors 设置为 off,以便在发生这种情况时不会将任何错误泄露给您的用户。
更清晰的字符串数字解析 (RFC)
PHP 在将字符串解析为数值时做了很多聪明的事情。在 PHP8 中,这略有变化,因此值得检查 RFC 以确保您了解更改的来源。基本上,不要总是依赖于正确解释为整数的“42”字符串值。
更清晰的数字字符串比较 (RFC)
对 PHP 比较字符串和数字的方式进行了一些小的更改。检查 RFC 以获取详细信息,但例如,以下将在 PHP7 中返回 true,在 PHP8 中返回 false。
var_dump(42 == "42abc");
结论
PHP8 有一些变化,但看起来大部分被删除的弃用代码都是针对我有一段时间没有使用过的旧功能。我真的很想知道 JIT 引擎在我使用的代码库上有什么,甚至在更广泛的 PHP 社区中。也就是说,我强烈建议扫描您的代码库是否存在 PHP8 不兼容性并运行单元测试,以确保您的 PHP 应用程序在升级之前能够正常运行。
目前,我会通读重大更改文档并密切关注 PHP8 发布日历以获取任何公告。
以上是 PHP8 的新特性和重大变化 的全部内容, 来源链接: utcz.com/z/345771.html