PHP 8 来了!朝着正确方向迈出了一步?
介绍
新功能
pexels发布的照片
介绍
PHP 8 终于来了!
PHP 8.0 是 PHP 语言的一次重大更新,
包含许多新特性和优化,包括命名参数、联合类型、属性、构造函数属性提升、匹配表达式、空安全运算符、JIT,以及类型系统、错误处理和一致性方面的改进。
在本文中,我们将回顾所有新功能和变化,并分享对每个变化的一些想法,以及 PHP 似乎正在绘制的路线图。
您也可以在官方发布公告中阅读有关这些内容的全部内容。
新功能
命名参数
在 PHP 8 中,调用函数时,可以省略非必需参数,只传递所需的参数。
命名参数示例
function my_awesome_function(string $name, string $value = "", int $expires = 0) {
...
}
假设现在我们要调用这个函数,但只指定$name
和$expires
属性。
在 PHP 7 中:
// calling the function. but we only want to specify $name and $expires
my_awesome_function('test', '', time() + 60 * 60 * 2)
在 PHP 8 中:
// calling the function. but we only want to specify $name and $expires
my_awesome_function(name: 'test', expires: time() + 60 * 60 * 2)
我对命名参数的看法
这是一个巧妙且便捷的设计。如今许多语言都支持这种方法调用。
然而,这种设计可能会导致函数违反“单一职责原则”,因为这些方法会因为参数过多而“做太多事情”。所以,一如既往,请谨慎使用 ;-)
属性
属性是这个模块中的新生事物。它本质上是一种直接嵌入到代码中的配置语言。
属性是一种原生的 PHP 语法,它能够在代码中的声明中添加结构化的、机器可读的元数据信息:类、方法、函数、参数、属性等等。
属性示例
在 PHP 7 中(使用 PHPDocs):
class BookController
{
/**
* @Route("/api/books/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}
在 PHP 8 中:
class BookController
{
#[Route("/api/books/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}
构造函数属性提升
基本思想很简单:抛弃所有类属性和变量赋值,并在构造函数参数前加上 、 或 前缀public
。PHPprotected
将private
采用这种新语法,并在实际执行代码之前将其转换为正常的语法。
构造函数属性示例
在 PHP 7 中:
class Point {
public float $x;
public float $y;
public float $z;
public function __construct(
float $x = 0.0,
float $y = 0.0,
float $z = 0.0
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
在 PHP 8 中:
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
我对构造函数属性提升的看法
构造函数属性提升减少了所需的代码量,从而生成更小、更简洁的类。
如果您仍然希望拥有不属于构造函数参数的类属性,您可以按照旧的方式声明它们,然后在构造函数内部实例化(或不实例化)。
联合类型
联合类型是一种为属性/变量声明多种类型的方法。
因此,如果函数参数可以接受string
或int
值,您现在可以将其声明为string|int
。
这在 PHP 7 中无法实现,只能使用 PHPDocs(因此它不属于 PHP 核心库,但存在于 PHPDocs 中)。您可以使用在运行时
验证的原生联合类型声明,而无需使用 PHPDoc 注释来声明多种类型的组合。
联合类型示例
在 PHP 7 中:
class Book {
/** @var int|float */
private $price;
/**
* @param float|int $price
*/
public function __construct($price) {
$this->price = $price;
}
}
new Book('test'); // OK at runtime
在 PHP 8 中,由于 Union 类型是 PHP 运行时库和编译器的一部分,因此这将引发错误:
class Book {
public function __construct(
private int|float $price
) {}
}
new Book('test'); // TypeError
我对 Union 类型的看法
作为SOLID 原则的拥护者,我并不十分支持这项更改。
我可以理解,它确实可以提高速度,并增强我对代码的控制力,但我认为,如果你的代码经常需要使用联合类型作为属性,那么你肯定需要好好考虑一下。
匹配表达式
匹配表达式语法是 PHP 8 中最优秀的功能之一,它switch
以多种方式改进了语法。
我们先来比较一下两者。这里有一个经典的switch
例子(来自本文的例子):
在 PHP 7 中(使用switch
):
switch ($statusCode) {
case 200:
case 300:
$message = null;
break;
case 400:
$message = 'not found';
break;
case 500:
$message = 'server error';
break;
default:
$message = 'unknown status code';
break;
}
在 PHP 8 中(使用match
):
$message = match ($statusCode) {
200, 300 => null,
400 => 'not found',
500 => 'server error',
default => 'unknown status code',
};
我对匹配表情功能的看法
match
将执行严格的类型检查,而不是宽松的检查。这就像使用===
而不是==
。
在我看来,这是一个很好的机会,因为它使代码更严格,更具表现力。
此外,如果您忘记检查某个值,并且没有指定默认分支,PHP 将抛出UnhandledMatchError
异常。
这再次提高了严格性,但它可以防止一些细微的错误被忽视。
要解决这个问题,您应该将表达式包装在一个单独的方法中,并通过返回默认值match
来处理异常。UnhandledMatchError
空安全运算符
现在,您可以使用新的 nullsafe 运算符来使用链式调用,而无需使用空值检查条件。
当链中某个元素的求值失败时,整个链的执行将中止,并且整个链的求值结果都将为空。
空安全运算符示例
在 PHP 7 中:
$country = null;
if ($book !== null) {
$author = $book->author;
if ($author !== null) {
$address = $author->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}
在 PHP 8 中:
$country = $book?->author?->getAddress()?->country;
我对 nullsafe 运算符的看法
我对此百感交集。
当然,这确实很方便,因为它可以减少代码量,而且无疑更易读。
但从全局来看,我们的代码为什么需要如此深入和贪婪地进行检查?
我们的代码是否应该有业务规则来约束这种行为?
所以,这又是一个方便的新功能,但请谨慎使用……
更合理的字符串与数字比较
当与数字字符串进行比较时,PHP 8 使用数字比较。否则,它会将数字转换为字符串并使用字符串比较。
在 PHP 7 中:
0 == 'foobar' // true
在 PHP 8 中:
0 == 'foobar' // false
这无疑是朝着正确方向迈出的一步,但前提是 PHP 正在努力成为一种更严格的语言。
当我们说“equals”时,我们的意思应该是“equals”!
我对 PHP 8 的总体看法
PHP 无疑正在努力成为一种更严格、更“严肃”的语言。作为面向对象编程 (OOP)和SOLID 原则
的忠实拥护者,我非常乐意并支持这个方向。 此外,正如您在升级指南中看到的,PHP 8 总体上是向后兼容的,因为它不会破坏之前主要版本中的许多功能。太棒了!
因此,让我们尽可能有意识地尝试使用 PHP 的所有新功能,并构建出令人惊叹的东西!
如果您对新版 PHP 感到满意,请查看迁移指南。
您对 PHP 8 有何看法?