php底层
https://www.cnblogs.com/taek/p/3979252.html
https://blog.csdn.net/xingshangyy/article/details/104176076
https://www.cnblogs.com/wanglijun/p/8830932.html
http://niliu.me/articles/1795.html
https://segmentfault.com/a/1190000022416584
https://www.cnblogs.com/wanglijun/p/8830932.html
php底层运行机制与原理
php动态语言执行过程:拿到一段代码后,经过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令(opcodes),然后ZEND虚拟机顺次执行这些指令完成操作
php7代码执行过程
PHP代码=> token => 抽象语法树 => opcodes => 执行
- 源代码通过词法分析得到 Token Token 是 PHP 代码被切割成的有意义的标识。PHP7 一共有 137 种 Token,在 zend_language_parser.h 文件中做了定义。
- 基于语法分析器将 Token 转换成抽象语法树(AST) Token 就是一个个的词块,但是单独的词块不能表达完整的语义,还需要借助一定的规则进行组织串联。所以就需要语法分析器根据语法匹配 Token,将 Token 进行串联。语法分析器串联完 Token 后的产物就是抽象语法树(AST,Abstract Syntax Tree)。 AST 是 PHP7 版本的新特性,之前版本的 PHP 代码的执行过程中是没有生成 AST 这一步的。它的作用主要是实现了 PHP 编译器和解释器的解耦,提升了可维护性。
- 将语法树转换成 Opcode 需要将语法树转换成 Opcode,才能被引擎直接执行。
- 执行 Opcodes opcodes 是 opcode 的集合形式,是 PHP 执行过程中的中间代码。PHP 工程优化措施中有一个比较常见的 “开启 opcache”,指的技术这里将 opcodes 进行缓存。通过省去从源码到 opcode 的阶段,引擎直接执行缓存好的 opacode,以提升性能。
php语法分析
通过一定的规则,把输入的代码 区分出哪些是 是$开头的变量, 哪些是 以两个单引号括起来的字符串,哪些是以两个双引号括起来的字符串 等等, 这些区分出来的东西 称为token ,token 之间的联系 是由语法分析来完成的, 比如 赋值,加减乘除;
token是通过yylex()函数返回的,每执行yylex()函数,会返回一个token,每个token都会有类型和相对应的值。
$name = ‘string’;
php7 内核架构

- zend 引擎 词法 / 语法分析、AST 编译和 opcodes 的执行均在 Zend 引擎中实现。此外,PHP 的变量设计、内存管理、进程管理等也在引擎层实现。
- PHP 层 zend 引擎为 PHP 提供基础能力,而来自外部的交互则需要通过 PHP 层来处理。
- SAPI server API 的缩写,其中包含了场景的 cli SAPI 和 fpm SAPI。只要遵守定义好的 SAPI 协议,外部模块便可与 PHP 完成交互。
- 扩展部分 依据 zend 引擎提供的核心能力和接口规范,可以进行开发扩展。
php7与php5区别
php7比php5快,主要是php7对zend引擎进行了深度优化。
增加抽象语法数
php5代码在语法解析阶段直接生成opline指令,执行器直接执行opline指令
php7代码在解析生成抽象语法树,然后将抽象语法树编译成opline指令。解耦编译器和执行器。
zval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
struct _zval_struct{
zend_value value;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, // 标记value的类型,如type是is_string,就会取zend_value str的指针
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved
)
} v;
uint32_t type_info; //表示变量类型,is_null,is_false,istrue,is_long,is_array,is_object,is_resource等11个与php相关的
}u1;
union {
uint32_t next; // 数组中解决hash冲突的
uint32_t cache_slot;// 做运行词缓存
uint32_t lineno; // 标记php的哪一行
uint32_t num_args; // 函数调用传递参数个数
uint32_t fe_pos; // foreach位置,每foreach一次 这个值就+1
uint32_t fe_iter_idx; // 游标索引位置
uint32_t access_flags; // 进行类性标记,如public,private,protected
uint32_t property_guard;
} u2;
}
|

可以表示php中任意变量
zend_value是一个联合体,取值是根据u1的type来取值的ZEN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
typedef union _zend_value{
// define IS_UNDEF 0
// define IS_NULL 1
// define IS_FALSE 2
// define is_TRUE 3
zend_long lval; // define IS_LONG 4
double dval; // define IS_DOUBLE 5;
zend_refcounted *counted;
zend_string *str; // define IS_STRING 6;
zend_array *arr; // define IS_ARRAY 7;
zend_object *obj; // define IS_OBJECT 8
zend_resource *res; // define IS_RESOURCE 9
zend_reference *ref; // 引用类型 define IS_REFERENCE 10
zend_function *func;
zend_class_entry *ce; // 类
zend_ast_ref *ast; // 抽象语法树
void *ptr; // IS_VOID 18
} zend_value;
|
使用gdb查看类型
1
2
3
4
5
6
7
8
9
|
gbc php
# echo handle 打断点
b ZEND_ECHO_SPEC_CV_HANDLER
#执行文件
r zval.php
p *z
# 取查看u1 type 对应的数字 4 is_long 5 is_double 6 is_string 7 is_array
# 然后对照_zend_value每个类型对应取什么,如4 取的是lval就算对应该变量存储的值
|
string
1
2
3
4
5
6
7
8
9
10
|
struct _zend_string{
zend_refcounted_h gc; // 主要用在垃圾回收
zend_ulong h; // 该字符串对应的hash值
size_t len;
char val[1];
}
// 字符串在复制的时候都是指定的同一个zend_string
// $a = 'string'; $b = $a;
// 使用 zend_refcounted_h 里的refcount进行+1标记,表示被两个zend_string 引用了
// 当$b 修改时,会复制一份zend_string(地址发生改变了),原来的zend_string的refcount会-1
|
引用类型
1
2
3
4
5
6
7
8
9
10
11
|
// zend_reference
struct _zend_reference{
zend_refcounted_h gc;
zval val;
}
// $a = 'ad';
// $b = &$a;
// 这时候a,b的type都是10,并且都指向相同的zend_reference,zend_reference里的zval指向相同的zend_string
// unset($b);
// 这时,$b的zend_reference 不会变,只是会复制一份zend_string(地址发生改变了),原来的zend_string的u1里的type会改成0(IS_UNDEF)
|
array
1
2
3
4
5
6
7
8
9
10
|
// _zend_array HashTable
struct _zend_array{
zend_refcounted_h gc;
unit32_t nTableMask; // 计算索引值
Bucket *arData;// 存储key-value对
unit32_t nNumUsed; // 已经用的空间
unit32_t nNumOfElements; // 真正的元素个数
unit32_t nTableSize; // arData大小 初始值时8,当不够用的时候会扩容(成倍扩容)
unit32_t nNextFreeElement; // [1,2,3,4] 这类数组使用
}
|
php 生命周期
cli模式的生命周期