PHP7内核深度解析

编译前的php目录

我们从官网下载php文件解压后,讲解几个重点的文件夹

Zend:php核心内核放在zend文件夹

sapi:这是 PHP 内核提供给外部调用其服务的接口,即外部系统可以通过 SAPI 来调用 PHP 提供的编译脚本、执行脚本的服务。PHP 中实现的 SAPI 有很多,Cli、Fpm 是我们比较常见的。

从下图可以较为清晰的理解外部系统是如何通过 SAPI 调用 PHP 服务的

20200424134202

ext:这个大家比较熟悉这是放置扩展的地方。

configure: 这个文件主要负责编译工作

可以通过

安装php完成了。

你可以从make输出的编译过程中找到

20200424135205

开启–enable-debug 等于 -o0? 默认不开启-o2,不开启会对性能有优化。

 

安装完成后的php目录

sbin: 这里面放着php-fpm,因为编译时使用了pfm方式,所以才会有,如果没有开启就不会存在了。

bin:? 有个php文件,就是php的可执行程序

 

PHP7和PHP5的性能对比

来到php7未编译源码的Zend目录下,bench.php里面有很多的方法,都是用来测试性能的测试函数和方法。

我们用php7和php5都来运行这个文件。

/Zend/bench.php

测试php7:0.774秒,php5:2.056秒

结论:相差2倍

还有一个测试文件/Zend/micro_bench.php? (更详细的测试,有类对象的性能)

php7: 3.8秒? ?| php5 : 9秒

 

?php7新特性

太空船操作符? ?<=>?

用于比较两个表达式,例如当$a小于、等于或者大于$b 时,他分别返回 -1、0、1

echo? 1 <=> 1 //0

echo? 2?<=> 1 //1

echo? 1 <=>2 //-1

语法是这样的:$c = $a <=> $b;
这句代码的意思是

  • 如果$a > $b, $c 的值为1
  • 如果$a == $b, $c 的值为0
  • 如果$a < $b, $c 的值为-1

在没有太空船运算符的时候,我们只能这样写代码

$c = $a > $b ? 1 : ( $a==$b ? 0 : -1 );

或者用if else条件语句写得更多,

?类型声明

修改 declare(strict_type=1);? //strict_type=1 表示开启严格模式,也就是强制类型模式

执行结果

如果关闭严格模式,传入的参数会先转换为Int,结果为10

 

null合并操作符 ??

 

常量数组

php7之前无法通过define来定义一个常量数组的,php7支持了这个操作

 

?namespace批量导入

 

throwable接口

这个用来显示异常的

php5 报错,php7就可以捕获了

下面方法也是:

  • 在php7之前,如果代码中有语法错误或者fatal error时,程序会直接报错退出,php7中实现了全局throwable接口,原来的Exception和部分Error实现了该接口
  • 这种Error可以像Exception一样被第一个匹配的try/catch块捕获。如果没有匹配的catch块,则调用异常函数处理。如果尚未注册异常处理函数,则按照传统方式处理(Fatal error)
  • Error类并非继承自Exception,所以不能用catch(Exception?e),catch(Errore)来捕获,可用catch(Errore){} ,或者通过注册异常处理函数(set_exception_handler())来捕获Error

Closure::call()
在php7之前,当动态的给一个对象添加方法时,可以通过Closure来复制一个闭包对象,并绑定到一个$this对象和类作用域

在php7可通过call来暂时绑定一个闭包对象到$this对象并调用它

 

intdiv

等于3 ,取整数

 

list的方括号写法

 

抽象语法树(AST)

在php7里

20200424194608

返回 array(1){[“b”]=>int(1)}

 

 

…是php的一个语法糖

php5.6的功能

输出:

也就是说第一个函数,…作为参数,就是把数组打散传入的方法里。

第二个函数,方法设置的参数使用… 会把零散的数字,组合成数组。

也就是说…ints和…$num就是一个数组,但是会根据情况对号入座

PHP7结构体

php7中的zval结构体对比php5优化了很多,一个zval只占用16字节的空间,php7中的zval的结构体如下

value 8个字节, union u1占用四个字节,union u2占用也是四个字节 ,总共8个字节

20200425075437

zval用来表示php里的任意变量

 

 

 

value的结构体占用了8个字节,这里面存储的就是zval中的value值的地方。

上面的代码在Zend/zend_types.h

虽然我们写的php代码是弱类型,但是在底层实现还是要区分类型的。

那么如何区分我们是什么类型的呢?

zend_uchar? ? type, 就是类型,包含如下类型

20200425081302

zend_uchar? ? type_flags,? 变量类型特有的标记

他表示常量不可变的类型

20200425081806

第一个常量类型,第二个不可变类型(共享内存的数组),第三个引用计数的类型,第四个包含循环引用的类型,第四个可被复制的类型

zend_uchar? ? const_flags,? 常量类型特有的标记

zend_uchar? ? reserved? 保留字段

 

总结:

_zend_value使用哪个类型,是由 _zval_struct中的 zend_uchar type决定的

例如:_zval_struct中的 zend_uchar type 是IS_LONG整形,我们直接取_zend_value里的*lval就得到值了;

_zval_struct中的 zend_uchar type 是IS_STRING整形,我们直接取_zend_value里的*str就得到值了;

 

调试测试一下

准备zval.php

gdb ../../php-7.1.0-debug/output/bin/php? ?//启动gdb调试

b? ZEND_ECHO_SPEC_CV_HANDLER? ? //在函数加断点

r zval.php? ?//运行程序

20200425084538

n是进入下一行的意思,不会进入函数体。

p输出(print)变量”var”的值

你会看到type = 4,获取值lval = 2

 

发表评论