事情总在不断变化演进当中,依稀记得php7彼时才刚刚发布,恋恋不舍的从5.4系统升级到7.x,于是开始试着写类型声明、开始使用三元判断??等等... 想着什么时候写7也能像5系的那样得心应手,于是写着写着,php8发布了 。登录php官网即可看到醒目的标题:php8发布了!
下面就让咱们一起看看php8都带来了哪些改变和解决了什么痛点吧,话不多说,先安装走起吧安装php8 (macos 11.1)
从php官网下载源码:
wget https://www.php.net/distributions/php-8.0.0.tar.gz
...
下载完毕!
解压:
#解压
tar zxvf php-8.0.0.tar.gz
# 生成 configure 文件
./buildconf --force
# 配置构建流程
./configure --prefix=/usr/local/php80 \--with-config-file-path=/usr/local/php80 \--enable-cli \--enable-debug \--enable-fpm \--with-config-file-scandir=/usr/local/php80/etc/php.d/ \--without-iconv
执行无误的话大概是这样式滴
构建安装
make && sudo make install
漫长的安装完毕后配置文件
sudo cp php.ini-development /usr/local/php80/php.ini
安装成功,✿✿ヽ(°▽°)ノ✿
我本子里之前的版本是7.3.x的版本,这也是我现在工作的主力版本,所以我得处理下双版本问题,就在查看旧版本的时候,看到了扎心的一幕macos未来的版本将不再自带php
不过也无所谓,貌似macos之前自带的php我一次也没用过,咱们继续。。。8带来了哪些改变?
至于改变,官网给列的很清楚明白
什么?不喜英文?其实我也是。。。chrome翻译如下JIT (just in time)
其中最大也是最重要的当然也是喊了有几年的jit即时编译了,简单来讲就是一些频繁被执行的函数方法等会被jit机制转换成CPU级别的机器码直接执行,而不像之前一样通过zend vm执行,更亲和cpu,效率肯定会提升较大。至于技术细节 有兴趣的看官可以看这篇文章,大神写的很详细rhett:PHP JIT 技术详解zhuanlan.zhihu.com
php8的JIT是在opcache的层面上提供的,它是在opcache优化的基础上,结合runtime信息再次优化,直接生成机器码。
opcache扩展可以自行安装下,安装好之后在原有的配置之外,添加两条jit的配置信息,贴上我的配置信息供参考
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=4
opcache.max_accelerated_files=8000
opcache.max_wasted_percentage=5
opcache.use_cwd=1
opcache.validate_timestamps=1
opcache.revalidate_freq=0
opcache.revalidate_path=0
opcache.save_comments=0
opcache.load_comments=0
opcache.jit=1205
opcache.jit_buffer_size=64M
最下面两个就是jit的配置啦,opcache.jit是有四个独立数字组成,从左到右分别是是否在生成机器码点时候使用AVX指令, 需要CPU支持:
0: 不使用
1: 使用寄存器分配策略:
0: 不使用寄存器分配
1: 局部(block)域分配
2: 全局(function)域分配JIT触发策略:
0: PHP脚本载入的时候就JIT
1: 当函数第一次被执行时JIT
2: 在一次运行后,JIT调用次数最多的百分之(opcache.prof_threshold * 100)的函数
3: 当函数/方法执行超过N(N和opcache.jit_hot_func相关)次以后JIT
4: 当函数方法的注释中含有@jit的时候对它进行JIT
5: 当一个Trace执行超过N次(和opcache.jit_hot_loop, jit_hot_return等有关)以后JITJIT优化策略,数值越大优化力度越大:
0: 不JIT
1: 做opline之间的跳转部分的JIT
2: 内敛opcode handler调用
3: 基于类型推断做函数级别的JIT
4: 基于类型推断,过程调用图做函数级别JIT
5: 基于类型推断,过程调用图做脚本级别的JIT
简单测试:
jit果真有传说中的那么强吗,下面测试一下。我在本地分别创建了两个虚拟环境,root指向同一个目录。php版本分别为
c.com:8.0.0
b.com:7.3.25
测试脚本为每次面试都要复习的冒泡排序,代码如下:
$stime=microtime(true); //获取程序开始执行的时间
function mao_pao($arr) {
$count = count($arr);
for ($i=0; $i < $count; $i++) {
for ($j=0; $j < $count; $j++) {
if (isset($arr[$j+1])) {
if ($arr[$j] > $arr[$j+1]) {
$temp = $arr[$j];
$arr[$j] = $arr[$j+1];
$arr[$j+1] = $temp;
}
}
}
}
return $arr;
}
for ($i=0; $i<=10000; $i++) {
$array[] = mt_rand(0, 10000);
}
mao_pao($array);
$etime=microtime(true);//获取程序执行结束的时间$total=$etime-$stime; //计算差值echo "$total";
为了减少误差 我随机生成一个固定数组
php8 vs php7
三次求平均值:8.0.0 with jit on7.3.25
1.22349905967713.6357760429382
由此可见性能提升还是明显的。(pis: 但是8如果关掉jit 测试性能还不如7 这块我也很疑惑,正在找原因。。。todo)
jit为啥能让执行性能提升?可以看下上面给的鸟哥的博客,在此就不赘述了。
Attributes (注解)
最近一直在用spring boot开发项目,给我印象最深的就是可以使用各种注释定义类,方法以及变量等,这样在代码书写比较优雅简洁的同时,提高了开发效率。现在php8的注解(Attributes)功能也来了
Attributes其实是依靠反射函数去实现价值的,以往也会用到php的反射函数,不过大多数情况下是用来自动生成API文档的时候才使用的,感觉没有太大的发挥空间。
/**
* @name 测试方法
* @author yangyanlei
*/
function test($params){}
$ref = new ReflectionFunction("test");
$doc = $ref->getDocComment();
print_r($doc);
执行获取信息如下:
/** * @name 测试方法 * @author yangyanlei */
这样简单粗暴的把注释里面的信息获取,然后再用字符串函数进行处理,不够规范也容易出错。
新的写法:
#[name("测试方法")]
#[author("yangyanlei")]
function test($params) {}
$ref = new ReflectionFunction("test");
print_r($ref->getAttributes("name")[0]->getArguments());
输出内容:
Array ( [0] => 测试方法 )
以后也可以这样实现注解及配置
#[Attribute]
class MyAttribute {
public function __construct($name, $value) {
print_r($name);
print_r($value);
}
}
#[MyAttribute("testname", "testvalue")]
function test($argument) {
}
$ref = new ReflectionFunction("test");
$ref->getAttributes("MyAttribute")[0]->newInstance();
程序输出:
testnametestvalue
虽然现在觉得好像用处不大,但是谁又能保证之后php不会出一个类似于spring boot注解的AOP开源框架呢,期待。。联合类型
自7开始,php已经支持类型声明,但是只支持单个类型声明,而联合声明是接收不同类型的值,而不是单个类型,语法指定T1|T2|...。之前不确定类型只能用?代替 ♂️
public function foo(Foo|Bar $input): int|float;
这个特性没什么可说的 不过有几个点需要注意可空联合类型:null类型可以作为联合类型的一部分 T1|T2|null因此可用于创建可为空的联合。
void是一个返回类型,指示函数不返回值。因此,它不能成为联合类型声明的一部分
match表达式
php8新增match表达式的支持,感觉就是switch的语法糖
function switchDemo($input) {
$res = 0;
switch ($input) {
case 1:
$res = 1;
break;
case 2:
$res = 2;
break;
default:
$res = 3;
}
echo $res . "\n";
}
echo switchDemo(1);
echo switchDemo(2);
echo switchDemo(3);
?>
输出:
1 2 3
match写法:
function matchdemo($input) {
echo match ($input) {
1 => 1,
2 => 2,
3 => 3
};
}
echo matchdemo(1);
echo matchdemo(2);
echo matchdemo(3);
?>
输出:
123
并且,类似switch的多个case一个block一样,match的多个条件也可以写在一起,比如:
$result = match($input) {
"true", "on" => 1,
"false", "off" => 0,
"null", "empty", "NaN" => NULL,
};
结语
以上,本文会随时更新补充,希望phper越来越好,希望php越来越好。