美文网首页PHP经验分享
php7的zval相关介绍

php7的zval相关介绍

作者: 程序员小饭 | 来源:发表于2020-01-28 20:52 被阅读0次

在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的方式获得string的对象,通过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来看分别是数组和对象类型

相关文章

  • php7的zval相关介绍

    在php7中变量主要由zval保存,只占用16个字节zval结构如下 zval主要由value u1 和u2保...

  • php7中的zval结构体

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

  • php7中的引用类型

    上一篇文章中,我们看zval类型中并没有记录引用计数的相关信息,那么php7中在进行&定义的时候是怎么处理的呢?我...

  • 【PHP7源码学习】系列之zval实现

    上一篇讲解了【PHP7源码学习】源码整体结构分析,从本篇开始讲解PHP7中第一个非常重要的结构 zval。 基础知...

  • GDB调试一些小记录

    起因 最近在看PHP7的源码,想要看一下一个zval一个执行流程,此时需要用到gdb调试,然后按照相关的步骤进行每...

  • php7 zval及变量存储方式

    Zval是PHP中最重要的数据结构之一,它包含了PHP中变量的值和类型相关信息。 1. zval 1.1 zval...

  • php7变量实现_zval

    php5的变量实现 php通过一个zval结构体来实现变量,对于全局变量,php维护一个全局的hashtable,...

  • php7引用计数

    1. 什么是引用计数 在《php7 zval及变量存储方式》的2.3节中我们说到,对于复杂类型的变量(string...

  • PHP的面试总结

    PHP的垃圾回收机制是什么? php5和php7的垃圾回收机制都是利用引用计数。 在zval结构体中定义了ref_...

  • PHP引用计数

    1. PHP官方手册引用计数介绍 引用计数每个php变量存在一个叫"zval"的变量容器中。一个zval变量容器,...

网友评论

    本文标题:php7的zval相关介绍

    本文链接:https://www.haomeiwen.com/subject/tghmthtx.html