PHPPHP经验分享

PHP7源码学习笔记(一) zval类型

2020-02-26  本文已影响0人  公式般欢笑

源码版本:php-7.1.0
电脑环境:Deepin15.11
GDB版本:8.3.1
GCC版本:6.3.0

PHP的zval数据结构

PHP中的所有变量,都会保存在一个zval结构体中。


typedef struct _zval_struct zval; 

具体的结构体内容为:

struct _zval_struct {
    zend_value        value;        
    union {
        struct {
            ZEND_ENDIAN_LOHI_4( //这里是用来区分大小端的
                zend_uchar    type,      /* type是用于标注变量的类型 */
                ...
        } v;
        uint32_t type_info;
    } u1;
    union {
          ...
    } u2;
};

_zval_struct结构体包含三个内容,value u1和u2。
value 变量包含的具体数据,u1用来区分变量的类型,u2是一个辅助工具。
其中,u1的type一共有10个值,对应PHP中的几种数据类型。

#define IS_UNDEF                    0  //undefined
#define IS_NULL                     1  //null
#define IS_FALSE                    2  //false
#define IS_TRUE                     3  //true
#define IS_LONG                     4  //整型
#define IS_DOUBLE                   5  //浮点型
#define IS_STRING                   6  //字符串
#define IS_ARRAY                    7  //数组
#define IS_OBJECT                   8  //对象
#define IS_RESOURCE                 9  //资源
#define IS_REFERENCE                10  //引用

zval中的value内容是这样的一个结构体,除long类型和double类型之外,都是通过指针的方式来实现的。

typedef union _zend_value {
    zend_long         lval;             /* long value */
    double            dval;             /* double value */
    zend_refcounted  *counted;
    zend_string      *str;
    zend_array       *arr;
    zend_object      *obj;
    ...
    zend_function    *func;
    struct {
        uint32_t w1;
        uint32_t w2;
    } ww;
} zend_value;

指针类型的zend_string、zend_array、zend_object均有结构体的定义,以zend_string为例:

struct _zend_string {
    zend_refcounted_h gc;               // 引用计数
    zend_ulong        h;                /* hash value */
    size_t            len;
    char              val[1];           //可变长度的字符串内容
};

通过GDB深入理解PHP的数据结构

当一个变量被分配的时候,首先会初始化一个空的zval,u1的type为IS_UNDEF。然后在进行语法解析的时候,根据值来判断变量的类型,更改zval.u1.type为对应类型。同时将变量值赋值给value中的对应字段。
我们可以通过一段PHP代码来操作一下PHP的zval类型。

<?php
   $a=1;
   echo $a;

通过gdb来启动PHP,然后在echo处打下断点

(gdb) b ZEND_ECHO_SPEC_CV_HANDLER 
Breakpoint 1 at 0x5bdf2f: file /program/php-7.1.0/Zend/zend_vm_execute.h, line 34640.

运行 php文件

(gdb) r echo.php 
Starting program: /program/bin/php/php7/bin/php echo.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /program/php-7.1.0/Zend/zend_vm_execute.h:34640
34640       SAVE_OPLINE();

继续执行下一步,取出当前的变量

(gdb) n
34641       z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) 
34643       if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p z
$1 = (zval *) 0x7ffff3814080
(gdb) p *z
$2 = {value = {lval = 1, dval = 4.9406564584124654e-324, counted = 0x1, str = 0x1, arr = 0x1, obj = 0x1, res = 0x1, ref = 0x1, ast = 0x1, zv = 0x1, ptr = 0x1, ce = 0x1, func = 0x1, ww = {w1 = 1, w2 = 0}}, u1 = {
    v = {type = 4 '\004', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 4}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, 
    access_flags = 0, property_guard = 0}}

可以看到当前的变量的u1.type为4,并且其value.lval为1。

(gdb) n
34650           zend_string *str = _zval_get_string_func(z); //这里是将内容强转为字符串
(gdb) 
34652           if (ZSTR_LEN(str) != 0) {
(gdb) p str
$3 = (zend_string *) 0x7ffff3802d40
(gdb) p *str
$4 = {gc = {refcount = 1, u = {v = {type = 6 '\006', flags = 0 '\000', gc_info = 0}, type_info = 6}}, h = 0, len = 1, val = "1"}

最终 $4的结果,对应的正是zend_string的结构体。

上一篇下一篇

猜你喜欢

热点阅读