PHP8终于来了!
PHP8.0是PHP的一个重大更新。 它包含许多新的特点和优化其中包括 命名参数, 联合类型, 特性, 构造函数属性提升, match
表达式, Null安全操作符, JIT,并改进了类型系统, 错误处理和 一致性问题。
在本文中,我们将审查所有新的特点和变化,并分享关于每一个变化的一些想法,而且从路线图上看来PHP似乎一直在向前发展。
你可以读取所有有关这些在官方发布的正式公告 。
新的功能
1. 命名参数
在PHP8调用一个函数时,可以省略非必需的参数,传递需要的。
命名参数的例子
function my_awesome_function(string $name, string $value = "", int $expires = 0) {
...
}
现在举个例子,我们要调用这一函数,但只有指定的 $name 和 $expires 属性。
在PHP7:
// calling the function. but we only want to specify $name and $expires
my_awesome_function('test', '', time() + 60 * 60 * 2)
在PHP8:
// calling the function. but we only want to specify $name and $expires
my_awesome_function(name: 'test', expires: time() + 60 * 60 * 2)
关于命名参数的想法
这是一个很好和方便改进。许多语言现在支持这种方法的函数调用。
然而,这一设计可能会因为给函数设置太多参数,导致函数功能复杂化,从而违反了单一职责原则(Single-responsibility principle)。因此,一如既往,谨慎使用;-)
2. 属性
属性是新来的小子。 它本质上是一种直接嵌入进入代码的配置语言。
属性是一个原生的PHP语法,在代码中为 类、方法、函数、参数、类属性等的声明,添加结构化和机器可读的元数据信息。
属性的例子
在PHP7(PHPDocs):
class BookController
{
/**
* @Route("/api/books/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}
在PHP8:
class BookController
{
#[Route("/api/books/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}
3. 构造函数属性提升
基本的想法很简单:把所有类属性和变量的定义赋值,和访问修饰符public, protected或 private直接放在构造函数的参数列表中。PHP处理这一新的语法,是在执行前先将其转化为一般语法的形式。
构造函数属性的例子
在PHP7:
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;
}
}
在PHP8:
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
关于构造函数属性提升的想法
构造函数属性提升减少了需要的代码量,让类更简洁。
如果你不想让类属性出现在构造函数参数列表中,可以用旧的方式声明它们,然后在构造函数内部初始化(或者不处理)。
4. 联合类型
联合类型是一种为属性和变类声明多个类型的方法。因此,如果一个函数的参数可以接受string
或 int
类型的值,现在可以用 string|int
来声明它.
在PHP7,你无法这样做,只有使用PHPDocs(这不是PHP核心库,只存在PHPDocs中).
你能用联合类型代替PHPDocs标记来声明多类型的变量,并且类型在运行时中会进行验证。
联合类型的例子
在PHP7:
class Book {
/** @var int|float */
private $price;
/**
* @param float|int $price
*/
public function __construct($price) {
$this->price = $price;
}
}
new Book('test'); // OK at runtime
在PHP8,因为联合类型是PHP运行时和编译器的一部分,以下代码将会抛出错误:
class Book {
public function __construct(
private int|float $price
) {}
}
new Book('test'); // TypeError
关于联合类型的想法
作为一个SOLID原则的倡导者,我不是特别喜欢这个变化。
我可以理解的,它可以帮助加快速度,并提升了对代码的控制,但是我相信如果你的代码常常需要你用联合类型定义一个属性,那么肯定是有些东西更需要你去思考。
5. Match表达式
在PHP8中,match表达式语法是最好的特性之一,它从多方面改善switch语法。
让我们来比较一下这两种语法。 这是个经典 switch 的例子:
在PHP7(使用 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;
}
在PHP8(使用 match):
$message = match ($statusCode) {
200, 300 => null,
400 => 'not found',
500 => 'server error',
default => 'unknown status code',
};
关于Match表达式特性的思考
match 会做严格类型的检查,而不是宽松的。这就像的使用===
,而不是==
。在我看来,这是一个很好的机会,因为它使代码更严格和更富有表现力。
此外,如果你忘记检查参数值,并且没有指定默认匹配,PHP会抛出一个 UnhandledMatchError
异常。
再次更加严格,但是它将阻止细微的错误不会被忽视。
为了解决这个问题,你应该把 match表达式放在一个单独的方法内,捕捉并处理 UnhandledMatchError
异常,然后返回一个默认值。
6. Null安全操作符
过去用条件判断检测null值,现在能用null安全操作符进行链式调用。
当评价链中的一个要素失败,则执行的整个链条中止,整个链的计算结果为null。
Null安全操作符使用例子
在PHP7:
$country = null;
if ($book !== null) {
$author = $book->author;
if ($author !== null) {
$address = $author->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}
在PHP8:
$country = $book?->author?->getAddress()?->country;
关于null安全操作符的想法
我对这个新特性的感觉是复杂的。
当然,这是一个方便的功能,因为它导致更小的和更可读的编码。
但是,通过更高视角来看,为什么我们甚至需要检查如此之深,并贪婪地?
难道我们的代码不应该有业务规则来抑制这种行为吗? 因此,又是一个方便的新的特征,但谨慎使用…
合理的字符串和数字比较
当用一个数字与一个只含有数字的字符串进行比较时,PHP8采用数字比较。否则,它先将数字转化为字符串,然后在进行字符串比较。
在PHP7:
0 == 'foobar' // true
在PHP8:
0 == 'foobar' // false
这绝对是一个正确的发展方向,但也表明了PHP正试图成为一个更严格的语言。
当我们说相等时,应该意味着真正的相等!
关于PHP8的总体想法
PHP无疑正试图成为一个更加严格,甚至是更加”严肃”的一种语言。
作为一个OOP和SOLID原则的热衷者,我完全乐意和追随这个方向。
此外,正如你将在升级指导中看到的,PHP8通常是向后兼容的,因为它不会破坏很多之前主要版本的功能模块。 酷!
因此,让我们尽可能有意识的尝试用所有PHP的新特性去构建很棒的东西!
如果新版本的PHP能够满足你的需求,可以先看看迁移指南.
你对PHP8有什么想法?