在php7中变量主要由zval保存,只占用16个字节
zval结构如下
struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* define数据类型 */ /* active type */
zend_uchar type_flags, /* 变量类型特有的标记 */
zend_uchar const_flags, /* 常量类型的标记 */
zend_uchar reserved) /* 保留字段 */ /* call info for EX(This) */
} v;
uint32_t type_info;
} u1;
union {
uint32_t next; /* 解决哈希冲突 */ /* hash collision chain */
uint32_t cache_slot; /* 运行时缓存 */ /* literal cache slot */
uint32_t lineno; /* 标记运行在哪一行 */ /* line number (for ast nodes) */
uint32_t num_args; /* 函数调用个数 */ /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach的位置 */ /* foreach position */
uint32_t fe_iter_idx; /* foreach游标索引位置 */ /* foreach iterator index */
uint32_t access_flags; /* 用在类中 */ /* class constant access flags */
uint32_t property_guard; /* set get魔术方法中会用到 */ /* single property guard */
uint32_t extra; /* not further specified */
} u2;
};
zval主要由value u1 和u2保存 value占8字节,u1和u2各占4个字节
_zend_value的结构如下:
typedef union _zend_value {
zend_long lval; /* 整形 */
double dval; /* 浮点型 */
zend_refcounted *counted;
zend_string *str; /* 字符型 */
zend_array *arr; /* 数组 */
zend_object *obj; /* 对象 */
zend_resource *res; /* 资源 */
zend_reference *ref; /* 引用 */
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce; /* 类 */
zend_function *func; /* 函数 */
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
u1中的type用来区分数据类型,从而映射到_zend_value中的不同类型,type的类型关系如下
/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#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
纸上得来终觉浅 ,我们直接来用gdb实战一下看看内部的运行过程
我们编写这样一段程序zval.php,用echo来打断点,来查看php的变量相关的保存
$a = 100;
echo $a;
$b=2.3;
echo $b;
$c = null;
echo $c;
$d = true;
echo $d;
$e = false;
echo $e;
$f = "string";
echo $f;
$g = [1, 2, 3];
echo $g;
$h = new stdclass();
echo $h;
用gdb命令执行 在echo打断点
[root@VM_0_4_centos zval]# gdb ../php-7.1.9/bin/php
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/php/php-7.1.9/bin/php...done.
(gdb) b ZEND_ECHO_SPEC_CV_HANDLER
Breakpoint 1 at 0x8f76a7: file /download/php-7.1.9/Zend/zend_vm_execute.h, line 34699.
(gdb) r zval.php
Starting program: /home/php/zval/../php-7.1.9/bin/php zval.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".
Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /download/php-7.1.9/Zend/zend_vm_execute.h:34699
34699 SAVE_OPLINE();
Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64 libxml2-2.9.1-6.el7_2.3.x86_64 nss-softokn-freebl-3.34.0-2.el7.x86_64 xz-libs-5.2.2-1.el7.x86_64 zlib-1.2.7-17.el7.x86_64
(gdb) n
34700 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) n
34702 if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p *z
$1 = {value = {lval = 100, dval = 4.9406564584124654e-322, counted = 0x64, str = 0x64, arr = 0x64, obj = 0x64, res = 0x64, ref = 0x64, ast = 0x64, zv = 0x64, ptr = 0x64,
ce = 0x64, func = 0x64, ww = {w1 = 100, 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, extra = 0}}
(gdb)
最后的p *z中我们可以看到 u1的type是4,对应的是IS_LONG整型,所以直接取value中的lval 值为100
(gdb) c
Continuing.
100
Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /download/php-7.1.9/Zend/zend_vm_execute.h:34699
34699 SAVE_OPLINE();
(gdb) n
34700 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) n
34702 if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p *z
$1 = {value = {lval = 4612361558371493478, dval = 2.2999999999999998, counted = 0x4002666666666666, str = 0x4002666666666666, arr = 0x4002666666666666, obj = 0x4002666666666666,
res = 0x4002666666666666, ref = 0x4002666666666666, ast = 0x4002666666666666, zv = 0x4002666666666666, ptr = 0x4002666666666666, ce = 0x4002666666666666,
func = 0x4002666666666666, ww = {w1 = 1717986918, w2 = 1073899110}}, u1 = {v = {type = 5 '\005', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'},
type_info = 5}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
(gdb) p *z
$2 = {value = {lval = 17788064, dval = 8.7884713284254274e-317, counted = 0x10f6ca0, str = 0x10f6ca0, arr = 0x10f6ca0, obj = 0x10f6ca0, res = 0x10f6ca0, ref = 0x10f6ca0,
ast = 0x10f6ca0, zv = 0x10f6ca0, ptr = 0x10f6ca0, ce = 0x10f6ca0, func = 0x10f6ca0, ww = {w1 = 17788064, w2 = 0}}, u1 = {v = {type = 1 '\001', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 1}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0,
property_guard = 0, extra = 0}}
(gdb) c
Continuing.
Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /download/php-7.1.9/Zend/zend_vm_execute.h:34699
34699 SAVE_OPLINE();
(gdb) n
34700 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) n
34702 if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p *z
$3 = {value = {lval = 17788064, dval = 8.7884713284254274e-317, counted = 0x10f6ca0, str = 0x10f6ca0, arr = 0x10f6ca0, obj = 0x10f6ca0, res = 0x10f6ca0, ref = 0x10f6ca0,
ast = 0x10f6ca0, zv = 0x10f6ca0, ptr = 0x10f6ca0, ce = 0x10f6ca0, func = 0x10f6ca0, ww = {w1 = 17788064, w2 = 0}}, u1 = {v = {type = 3 '\003', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 3}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0,
property_guard = 0, extra = 0}}
(gdb) c
Continuing.
1
Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /download/php-7.1.9/Zend/zend_vm_execute.h:34699
34699 SAVE_OPLINE();
(gdb) n
34700 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) n
34702 if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p *z
$4 = {value = {lval = 17788064, dval = 8.7884713284254274e-317, counted = 0x10f6ca0, str = 0x10f6ca0, arr = 0x10f6ca0, obj = 0x10f6ca0, res = 0x10f6ca0, ref = 0x10f6ca0,
ast = 0x10f6ca0, zv = 0x10f6ca0, ptr = 0x10f6ca0, ce = 0x10f6ca0, func = 0x10f6ca0, ww = {w1 = 17788064, w2 = 0}}, u1 = {v = {type = 2 '\002', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 2}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0,
property_guard = 0, extra = 0}}
这三次执行u1的type对应的是1(IS_NULL) 3(IS_TRUE) 2(IS_FALSE) 不需要去value中取值
(gdb) n
34700 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) n
34702 if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p *z
$5 = {value = {lval = 17733632, dval = 8.7615783471909966e-317, counted = 0x10e9800, str = 0x10e9800, arr = 0x10e9800, obj = 0x10e9800, res = 0x10e9800, ref = 0x10e9800,
ast = 0x10e9800, zv = 0x10e9800, ptr = 0x10e9800, ce = 0x10e9800, func = 0x10e9800, ww = {w1 = 17733632, w2 = 0}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0,
property_guard = 0, extra = 0}}
(gdb) p *$5.value.str
$6 = {gc = {refcount = 1, u = {v = {type = 6 '\006', flags = 7 '\a', gc_info = 0}, type_info = 1798}}, h = 9223378990886268924, len = 6, val = "s"}
(gdb) p *$5.value.str.val@6
$7 = "string"
u1对应的type是6 字符串型,可以通过 p * 5.value.str
(gdb) p *z
$2 = {value = {lval = 140737314653024, dval = 6.9533472258009033e-310, counted = 0x7ffff5a58360, str = 0x7ffff5a58360, arr = 0x7ffff5a58360, obj = 0x7ffff5a58360,
res = 0x7ffff5a58360, ref = 0x7ffff5a58360, ast = 0x7ffff5a58360, zv = 0x7ffff5a58360, ptr = 0x7ffff5a58360, ce = 0x7ffff5a58360, func = 0x7ffff5a58360, ww = {w1 = 4121264992,
w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 28 '\034', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 7175}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
(gdb) c
Continuing.
Array
Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /download/php-7.1.9/Zend/zend_vm_execute.h:34699
34699 SAVE_OPLINE();
(gdb) n
34700 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) n
34702 if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p *z
$3 = {value = {lval = 140737314680496, dval = 6.9533472271582005e-310, counted = 0x7ffff5a5eeb0, str = 0x7ffff5a5eeb0, arr = 0x7ffff5a5eeb0, obj = 0x7ffff5a5eeb0,
res = 0x7ffff5a5eeb0, ref = 0x7ffff5a5eeb0, ast = 0x7ffff5a5eeb0, zv = 0x7ffff5a5eeb0, ptr = 0x7ffff5a5eeb0, ce = 0x7ffff5a5eeb0, func = 0x7ffff5a5eeb0, ww = {w1 = 4121292464,
w2 = 32767}}, u1 = {v = {type = 8 '\b', type_flags = 12 '\f', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 3080}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
后面两次根据u1的type来看分别是数组和对象类型