PHP8.0已经在2020年11月26号发布,到现在最新版,已经是PHP8.2.2,本站leyeah.com已经升级至最新版,PHP8的升级势在必行,可能很多网站,依旧还是在使用PHP7,可以考虑找个时间升级一下,毕竟新版发布都2年有余,性能方便提升不少,也增加了一些新功能,阿里云的Alibaba Cloud Linux 3或Centos的用户可以参考下这篇文章。
Alibaba Cloud Linux 3或centos如何升级到PHP8以上
PHP8提供了哪些新特性呢,可以参照下PHP8的官方release note,上面有对功能做了详细的说明。
1. 命名参数
在一个函数参数多达5,6个时,并且需要传最后一个参数时,前面本来可以默认的也必须传过去,有了命名参数后,就会方便很多,举个例子来说明下。
function test($a, $b = 1, $c = 2, $d = 3, $e = 4, $f = 5) | |
{ | |
echo $a + $b + $c + $d + $e + $f; | |
} | |
//PHP7 | |
test(0, 1, 2, 3, 4, 10); //输出 20 | |
//PHP8 | |
test(0, f: 10); //输出 20 |
对比一下,命名参数前面不能带上$符号,这个要注意一下,看起来是不是方便很多,可读性也强一些。
2. 注解的变化,就引用下官方的例子,看起来方式变得简便一些。
//PHP 7 | |
class PostsController | |
{ | |
/** | |
* @Route("/api/posts/{id}", methods={"GET"}) | |
*/ | |
public function get($id) { /* ... */ } | |
} | |
//PHP 8 | |
class PostsController | |
{ | |
"/api/posts/{id}", methods: ["GET"]) | (|
public function get($id) { /* ... */ } | |
} |
3. 构造器属性提升
//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; | |
} | |
} | |
//PHP8 | |
class Point { | |
public function __construct( | |
public float $x = 0.0, | |
public float $y = 0.0, | |
public float $z = 0.0, | |
) {} | |
} |
可以看出,写法上简便很多。
4. 增加了联合类型
//PHP8 | |
function test(int $a, int|string $b): mixed | |
{ | |
if (is_numeric($b)) { | |
return $a + $b; | |
} else { | |
return $a . ' ' . $b; | |
} | |
} | |
echo test(2, 6); //输出 8 | |
echo test(2, 'hello');//输出 2 hello |
从列子中看出,$b参数可以传入多个类型声明,这个对某些情况会很有用,以前只能在注释里标记,现在方法里可以直接声明,方便不少。
mixed类型是以下里面的一种。
array
bool
callable
int
float
null
object
resource
string
需要注意的是,null不能作为单独的类型,如下:
function test (null $a); // 不可以 | |
function test (int|null $a); // 可以 |
还有oject和resource也不能一起用,如下:
function test (object|resource $a); // 不可以 | |
function test (int|resource $a); // 可以 |
5. 增加了match表达式,看下面例子
//PHP 7 or PHP 8 | |
$a = 1; | |
switch ($a) { | |
case 1: | |
echo 'hello 1'; | |
break; | |
case 2: | |
echo 'hello 2'; | |
break; | |
default: | |
echo 'hello'; | |
break; | |
} | |
//上述结果 hello 1 | |
//PHP 8 | |
$a = 1; | |
echo match ($a) { | |
1 => 'hello 1', | |
2 => 'hello 2', | |
default => 'hello', | |
}; | |
//上述结果 hello 1 |
需要注意PHP8中变量$a是区分数据类型的,变成这样,结果就不同,这点需要格外注意。
//PHP7 | |
$a = '1'; | |
switch ($a) { | |
case 1: | |
echo 'hello 1'; | |
break; | |
case 2: | |
echo 'hello 2'; | |
break; | |
default: | |
echo 'hello'; | |
break; | |
} | |
//上述结果 hello 1 | |
//PHP 8 | |
$a = '1'; | |
echo match ($a) { | |
1 => 'hello 1', | |
2 => 'hello 2', | |
default => 'hello', | |
}; | |
//上述结果 hello |
6. 增加了nullsafe操作符,看以下例子:
//PHP8 | |
class TestClass | |
{ | |
public function __construct(object|null $dog) | |
{ | |
######## 写法简单 ####### | |
$dog?->cat?->saying(); | |
} | |
} | |
class Dog | |
{ | |
public $cat; | |
public function __construct() | |
{ | |
$this->cat = new Cat(); | |
} | |
public function saying(): void{ | |
echo 'Wang wang'; | |
} | |
} | |
class Cat | |
{ | |
public function saying(): void{ | |
echo 'Miao miao'; | |
} | |
} | |
new TestClass(new Dog()); //输出 Miao miao | |
new TestClass(null); //输出为空 |
本来多种判断的,现在加几个?就解决了,$dog?->cat?->saying(); 确实方便很多了。
第一个变量还是要有的,如果你直接写$dog?->cat?->saying(); 还是会报错的。
7. 改进了字符串和数字的比较,看下例子:
//PHP7 | |
var_dump(0 == '0'); // 输出 bool(true) | |
var_dump(0 == ''); // 输出 bool(true) | |
var_dump('0' == ''); // 输出 bool(false) 不能理解吧,为何会不相等,上面都连等了。 | |
//PHP8 | |
var_dump(0 == '0'); // 输出 bool(true) | |
var_dump(0 == ''); // 输出 bool(false) | |
var_dump('0' == ''); // 输出 bool(false) |
与其说改进,还不如说修了个bug,这个可能对有些对于空或0的判断要格外注意,一不小心,可能就是一个大bug,类型这种情况。
//PHP7 | |
var_dump(44 == '44aaa'); 输出 bool(true) | |
//PHP8 | |
var_dump(44 == '44aaa'); 输出 bool(false) |
8. 内部函数类型错误的一致性,这个看下就行,错误级别不太一样而已。
//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 | |
//PHP8 | |
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 |
9. 即时编译,这个看看官方的说明就行。
PHP 8 引入了两个即时编译引擎。 Tracing JIT 在两个中更有潜力,它在综合基准测试中显示了三倍的性能, 并在某些长时间运行的程序中显示了 1.5-2 倍的性能改进。 典型的应用性能则和 PHP 7.4 不相上下。
关于 JIT 对 PHP 8 性能的贡献
10. static和self可以作为返回类型,看一下例子:
//PHP8 | |
class Animal | |
{ | |
public static function instance(): self|static | |
{ | |
return new static(); | |
} | |
public function saying(): void | |
{ | |
echo 'No sound'; | |
} | |
} | |
class Dog extends Animal | |
{ | |
public function saying(): void | |
{ | |
echo 'Wang wang'; | |
} | |
} | |
class Cat extends Animal | |
{ | |
public function saying(): void | |
{ | |
echo 'Miao miao'; | |
} | |
} | |
Animal::instance()->saying(); //输出 No sound | |
Dog::instance()->saying(); //输出 Wang wang | |
Cat::instance()->saying(); //输出 Miao miao |
11. throw new \Exception('') 从以前的声明用,现在也可用于表达式结构,看一下例子:
//PHP7 | |
try { | |
$foo = $bar['offset'] ?? throw new \Exception('test'); | |
} catch(\Exception $e) { | |
echo 'get exception'; | |
} | |
//以上结果会出现语法解析错误: | |
#PHP Parse error: syntax error, unexpected 'throw' (T_THROW) in /test/php8.php on line 3 | |
//PHP8 | |
try { | |
$foo = $bar['offset'] ?? throw new \Exception('test'); | |
} catch(\Exception $e) { | |
echo 'get exception'; | |
} | |
//以上结果输出 get exception |
12. 增加了类Weakmap,看下面的例子,看看它有什么用:
//PHP7 or PHP8 | |
// 房子 | |
class House | |
{ | |
public $rooms; | |
public function __construct(array $rooms = []) | |
{ | |
$this->rooms = $rooms; | |
} | |
// 添加人 | |
public function addPerson(Person $person) | |
{ | |
$this->rooms[$person->name] = $person; | |
} | |
} | |
// 人 | |
class Person | |
{ | |
public $name; | |
public function __construct(string $name) | |
{ | |
$this->name = $name; | |
} | |
} | |
$house = new House(); | |
$lucy = new Person('lucy'); | |
$jony = new Person('jony'); | |
// 为房子添加人 | |
$house->addPerson($lucy); | |
$house->addPerson($jony); | |
//计数 | |
echo count($house->rooms); // 输出 2 | |
unset($lucy); // 删除1人 | |
//$luch已经删除,想得到的结果是1人 | |
echo count($house->rooms); // 输出 2 | |
// 这个人还在 | |
$lucyObj = $house->rooms['lucy']; | |
//理论上$luch应该不存在了 | |
echo $lucyObj->name; // 输出 lucy | |
//PHP8 Weakmap写法 | |
class House | |
{ | |
public WeakMap $rooms; | |
public function __construct() | |
{ | |
// 实例化 WeakMap | |
$this->rooms = new WeakMap(); | |
} | |
public function addPerson(Person $person) | |
{ | |
// 注意,这里的键一定是要一个对象 | |
$this->rooms[$person] = $person->name; | |
} | |
} | |
class Person | |
{ | |
public function __construct(public string $name){} | |
} | |
// 房东 | |
$house = new House(); | |
// 租房的人 | |
$lucy = new Person('lucy'); | |
$jony = new Person('jony'); | |
// 租房并给要钥匙 | |
$house->addPerson($lucy); | |
$house->addPerson($jony); | |
// 查看租房人数 | |
echo count($house->rooms); // 输出 2 | |
// lucy不租了(删除引用) | |
unset($lucy); | |
// 再次查看租房人数 | |
echo count($house->rooms); // 输出 1 | |
$lucyObj = $house->rooms['lucy']; | |
// 会报以下错误,lucy这个人已经走了 | |
#PHP Fatal error: Uncaught TypeError: WeakMap key must be an object in /test/php8.php:44 | |
#Stack trace: | |
#0 {main} | |
# thrown in /test/php8.php on line 44 |
13. ::class可以用于实例化后的类,看以下例子
//PHP7 | |
$obj = new \stdClass(); | |
echo $obj::class; | |
//结果会报以下错误: | |
#PHP Fatal error: Dynamic class names are not allowed in compile-time ::class fetch in /test/php8.php on line 4 | |
//PHP8 | |
$obj = new \stdClass(); | |
echo $obj::class; | |
//输出 stdClass |
14. Exception可以不用定义变量,看以下例子:
//PHP7 | |
try { | |
throw new \Exception(); | |
} catch (\Exception) { | |
echo "Something went wrong"; | |
} | |
//结果报以下错误: | |
#PHP Parse error: syntax error, unexpected ')', expecting '|' or variable (T_VARIABLE) in /test/php8.php on line 5 | |
//PHP8 | |
try { | |
throw new \Exception(); | |
} catch (\Exception) { | |
echo "Something went wrong"; | |
} | |
//结果输出 Something went wrong |
15. 方法参数最后的逗号被允许,看以下例子:
//PHP7 | |
function test($a, $b,) | |
{ | |
echo $a + $b; | |
} | |
test(3, 5); | |
//结果会报以下错误: | |
#PHP Parse error: syntax error, unexpected ')', expecting variable (T_VARIABLE) in /test/php8.php on line 2 | |
//PHP8 | |
function test($a, $b,) | |
{ | |
echo $a + $b; | |
} | |
test(3, 5); | |
//输出 8 |
16. 增加了Stringable的接口,看一下例子:
//PHP8 | |
class Foo | |
{ | |
public function __toString(): string | |
{ | |
return 'foo'; | |
} | |
} | |
function bar(string|Stringable $str) | |
{ | |
echo $str; | |
} | |
bar(new Foo()); //输出 foo | |
bar('abc');//输出 abc |
17. 增加了str_contains, str_starts_with, str_ends_with, fdiv, get_debug_type, get_resource_id方法
var_dump(str_contains('string with lots of words', 'words')); //输出 bool(true) | |
var_dump(str_starts_with('haystack', 'hay')); //输出 bool(true) | |
var_dump(str_ends_with('haystack', 'stack')); //输出 bool(true) |
以上是一些PHP8的新特性,当然还有一些其他方面的改动,请自行查阅相关文档即可。