随着2020年的PHP开发者峰会结束,8.X将在2020年11月26发布。
PHP 8.0 是 PHP 语言的主要更新。它包含了许多新特性和优化,包括命名参数、联合类型、属性、构造函数属性提升、匹配表达式、nullsafe 操作符、JIT,以及对类型系统、错误处理和一致性的改进。
有别于PHP7,万众瞩目的Just In Time Compilation(即时编译)功能成为了大家期待的重点。
命名参数 Named arguments
PHP 7
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
PHP 8
htmlspecialchars($string, double_encode: false);
- 只指定必需参数,跳过可选参数。
- 参数是独立于顺序并且是自描述的。
php8支持命名参数,这样靠前的有默认的参数,就不用必须明确写出,如下php8跳过第二和第三个默认参数,直接指定第四个参数。开发者可以按自己的意愿更改参数顺序,这样的好处是,摆脱了php函数有些不经常用的参数靠前,代码中又必须明确写出才能给后面的参数赋值的困扰。
属性 Attributes
属性,在很多语言中亦称作注解。
PHP 7
class PostsController
{
/**
* @Route("/api/posts/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}
PHP 8
class PostsController
{
#[Route("/api/posts/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}
不再使用 PHPDoc 注释,现在可以使用 PHP 原生语法的结构化元数据。
构造函数属性提升 Constructor property promotion
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,
) {}
}
- 定义和初始化属性可使用更少的样板代码。
联合类型 Union types
PHP 7
class Number {
/** @var int|float */
private $number;
/**
* @param float|int $number
*/
public function __construct($number) {
$this->number = $number;
}
}
new Number('NaN'); // Ok
PHP 8
class Number {
public function __construct(
private int|float $number
) {}
}
new Number('NaN'); // TypeError
可使用联合类型声明在运行时验证类型来代替使用 PHPDoc 注释说明。
Match 表达式Match expression
PHP 7
switch (8.0) {
case '8.0':
$result = "Oh no!";
break;
case 8.0:
$result = "This is what I expected";
break;
}
echo $result;
//> Oh no!
PHP 8
echo match (8.0) {
'8.0' => "Oh no!",
8.0 => "This is what I expected",
};
//> This is what I expected
match 类似于 switch,具有如下特性:
- match 为表达式,意味着其结果可以存储在变量中或用于返回。
- match 分支仅支持单行表达式,同时不需要 break; 语句。
- match 使用严格比较。
实例说明:
PHP7
$result = NULL;
switch ($input) {
case "true":
$result = 1;
break;
case "false":
$result = 0;
break;
case "null":
$result = NULL;
break;
}
PHP8
$result = match($input) {
"true" => 1,
"false" => 0,
"null" => NULL,
};
相比switch, match会直接返回值,可以直接赋值给$result了。
并且,类似switch的多个case一个block一样,match的多个条件也可以写在一起,比如:
$result = match($input) {
"true", "on" => 1,
"false", "off" => 0,
"null", "empty", "NaN" => NULL,
};
需要注意的和switch不太一样的是,以前我们用switch可能会经常遇到这种诡异的问题:
$input = "2 person";
switch ($input) {
case 2:
echo "bad";
break;
}
你会发现,bad竟然被输出了,这是因为switch使用了宽松比较(==)。match就不会有这个问题了, 它使用的是严格比较(===),就是值和类型都要完全相等。
还有就是,当input并不能被match中的所有条件满足的时候,match会抛出一个UnhandledMatchError exception:
$input = "false";
$result = match($input) {
"true" => 1,
};
会得到:Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type string
这样就不用担心万一match条件没写全导致了不可预知的错误。
另外还是要说明,match是关键字,也就是从PHP8开始它不能出现在namespace或者类名中,如果你的项目中有用match作为类名的: class Match {}
在PHP8开始将会得到语法错误了, 当然,方法名中还是可以用的。
Nullsafe 操作符Nullsafe operator
PHP 7
// PHP 7
$country = null;
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}
PHP 8
// PHP 8
$country = $session?->user?->getAddress()?->country;
可使用 nullsafe 操作符完成链式调用来代替 null 的验证。当对链中的一个元素求值失败时,整个链的执行将中止,整个链的求值为 null。
更合理的字符串与数值的比较 Saner string to number comparisons
PHP 7
// PHP 7
0 == 'foobar' // true
PHP 8
// PHP 8
0 == 'foobar' // false
当数值与数值字符串比较时,PHP 8 使用数值比较。否则将数值转换为字符串并采用字符串比较。
内置函数的一致错误类型 Consistent type errors for internal functions
PHP 7
// PHP 7
strlen([]); // Warning: strlen() expects parameter 1 to be string, array given
array_chunk([], -1); // Warning: array_chunk(): Size parameter expected to be greater than 0
PHP 8
// PHP 8
strlen([]); // TypeError: strlen(): Argument #1 ($str) must be of type string, array given
array_chunk([], -1); // ValueError: array_chunk(): Argument #2 ($length) must be greater than 0
大部分内置函数在参数验证失败后会抛出 Error 异常。
php8如果参数验证失败,大多数内部函数将引发Error异常。
这个改进对开发者提出了更高的要求,原来的warning错误,会直接升级为error错误。
JIT 编译 Just-In-Time compilation
PHP 8 引入了两个 JIT 编译引擎。Tracing JIT 是其中最有前景的,它在合成基准测试上的性能提高了3倍,在一些特定的长时间运行的应用程序上的性能提高了1.5-2倍。
典型的应用程序性能与 PHP 7.4 相当。
![](https://img.haomeiwen.com/i6943526/0daf4de0c885335b.png)
JIT的新特性,则是将PHP代码转化为传统的机器码,而并非通过zend虚拟机来运行,这大大增加了运行速度。
但是缺点是向下不兼容。
# JIT 可以通过php.ini中设置
opcache.jit = on // on 开; off 关
![](https://img.haomeiwen.com/i6943526/04884ebcd5044535.png)
左图是PHP8之前的Opcache流程示意图, 右图是PHP8中的Opcache示意图, 可以看出几个关键点:
- Opcache会做opcode层面的优化,比如图中的俩条opcode合并为一条
- PHP8的JIT目前是在Opcache之中提供的
- JIT在Opcache优化之后的基础上,结合Runtime的信息再次优化,直接生成机器码
- JIT不是原来Opcache优化的替代,是增强
- 目前PHP8只支持x86架构的CPU
事实上JIT共用了很多原来Opcache做优化的基础数据结构,比如data flow graph, call graph, SSA等
类型系统和错误处理的改进 Type system and error handling improvements
- 算术/位操作中更严格的类型检查 RFC
- 抽象 trait 方法验证 RFC
- 魔术方法的正确签名 RFC
- 重分类的引擎警告 RFC
- 不兼容方法签名的致命错误 RFC
-
@
操作符不在静默致命错误。 - 私有方法的继承 RFC
- 混合类型 RFC
- 静态返回类型 RFC
- 内部函数
email_thread
的类型 - 用于代替 Curl, Gd, Sockets, OpenSSL, XMLWriter 和 XML 扩展资源的封装对象
其他的语法微调和改进 Other syntax tweaks and improvements
- 允许在参数列表 RFC 和闭包 use 列表 RFC 后尾随逗号
- 未使用的捕获 RFC
- 变量语法微调 RFC
- 命名空间名称视为一个 token RFC
- Throw 调整为表达式 RFC
- 允许对象上使用 ::class 语法 RFC
新类、接口、和函数
New Classes, Interfaces, and Functions·
- Weak Map 类
- Stringable 接口
- str_contains(), str_starts_with(), str_ends_with()
- fdiv()
- get_debug_type()
- get_resource_id()
- token_get_all() 对象实现
- New DOM Traversal and Manipulation APIs
注解语法
通过重复使用现有标记T_SL和T_SR,注解是用“ <<”和“ >>”括起来的特殊格式的文本。
注解可以用该语言应用于许多事物:
- 函数(包括闭包和短闭包)
- 类(包括匿名类),接口,特征
- 类常量
- 类属性
- 类方法
- 功能/方法参数
namespace My\Attributes {
<<PhpAttribute>>
class SingleArgument {
public $argumentValue;
public function __construct($argumentValue) {
$this->argumentValue = $argumentValue;
}
}
}
namespace {
<<SingleArgument("Hello World")>>
class Foo {
}
$reflectionClass = new \ReflectionClass(Foo::class);
$attributes = $reflectionClass->getAttributes();
var_dump($attributes[0]->getName());
var_dump($attributes[0]->getArguments());
var_dump($attributes[0]->newInstance());
}
/**
string(28) "My\Attributes\SingleArgument"
array(1) {
[0]=>
string(11) "Hello World"
}
object(My\Attributes\SingleArgument)#1 (1) {
["argumentValue"]=>
string(11) "Hello World"
}
**/
PHP8之前PHP实现注解可以通过php-parser来实现,但现在可以直接通过Reflection 来获取。
/**
* @param Foo $argument
* @see https:/xxxxxxxx/xxxx/xxx.html
*/
function dummy($Foo) {}
# 现在获取这段注解则可以使用
$ref = new ReflectionFunction("dummy");
var_dump($ref->getAttributes("See")[0]->getName());
var_dump($ref->getAttributes("See")[0]->getArguments());
![](https://img.haomeiwen.com/i6943526/12326de043e5ff39.gif)
网友评论