加入收藏 | 设为首页 | 会员中心 | 我要投稿 我爱故事小小网_铜陵站长网 (http://www.0562zz.com/)- 视频终端、云渲染、应用安全、数据安全、安全管理!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

PHP是如何存储变量的?zval结构体你知道吗?

发布时间:2022-07-09 12:42:47 所属栏目:PHP教程 来源:互联网
导读:PHP 源码中的 zval 在 PHP 中定义一个变量是不需要声明类型的,一开始给变量 $a 赋予一个整型值,后面又可以轻而易举地将其改变为其他类型。那在 PHP 的源码中是如何来存储这个变量 $a 的呢?带着这个疑问我们一起去看一看 PHP 的源码。 PHP 的源码是由 C 编
  PHP 源码中的 zval
  在 PHP 中定义一个变量是不需要声明类型的,一开始给变量 $a 赋予一个整型值,后面又可以轻而易举地将其改变为其他类型。那在 PHP 的源码中是如何来存储这个变量 $a 的呢?带着这个疑问我们一起去看一看 PHP 的源码。
 
  PHP 的源码是由 C 编写的,在 PHP 的源码中使用了一个 zval 的结构体来存储在 PHP 代码中创建的变量。我们把 zval 结构体的定义拿出来简单分析一下。
 
  这是 PHP 在 Github 上的官方仓库:github.com/php/php-src,本文使用的分支是 PHP-7.4.29。
 
  zval 结构体
 
  在 PHP 的源码中找到这个文件:php-src/Zend/zend_types.h,可以看到其中 zval 结构体的定义如下,左侧是源码。源码中使用了 PHP 自己定义的类型 zend_uchar 、uint16_t 、uint32_t 等,这些类型会针对不同平台和编译器会转为该平台下的 char short int 等。为了便于理解,我将其翻译为普通类型并展示在了源码的右侧。同时还把其中的宏函数 ZEND_ENDIAN_LOHI_3() 也展开了。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  typedef struct _zval_struct zval;
 
  ...
 
         《源代码》                                               《翻译后》
 
  -------------------------------------------------------------------------------------------
 
  struct _zval_struct {                               | struct _zval_struct {
 
      zend_value value;                               |     zend_value value;
 
      union {                                         |     union {
 
          struct {                                    |         struct {
 
              ZEND_ENDIAN_LOHI_3(                     |             unsigned char type;
 
                  zend_uchar type,                    |             unsigned char type_flags;
 
                  zend_uchar type_flags,              |             union {
 
                  union {                             |                 unsigned short extra;
 
                      uint16_t extra;                 |             } u;
 
                  } u                                 |         } v;
 
              )                                       |         unsigned int type_info;
 
          } v;                                        |     } u1;
 
          uint32_t type_info;                         |     union {
 
      } u1;                                           |         unsigned int next;
 
      union {                                         |         unsigned int cache_slot;
 
          uint32_t next;                              |         unsigned int opline_num;
 
          uint32_t cache_slot;                        |         unsigned int lineno;
 
          uint32_t opline_num;                        |         unsigned int num_args;
 
          uint32_t lineno;                            |         unsigned int fe_pos;
 
          uint32_t num_args;                          |         unsigned int fe_iter_idx;
 
          uint32_t fe_pos;                            |         unsigned int access_flags;
 
          uint32_t fe_iter_idx;                       |         unsigned int property_guard;
 
          uint32_t access_flags;                      |         unsigned int constant_flags;
 
          uint32_t property_guard;                    |         unsigned int extra;
 
          uint32_t constant_flags;                    |     } u2;
 
          uint32_t extra;                             | };
 
      } u2;                                           |
 
  };                                                  |
 
  在 zval 结构体中,变量的值就存储在 zend_value 类型的 value 属性中。并通过 u1.v.type 来记录这个值是什么类型的,比如 IS_LONG 对应整型,IS_STRING 对应字符串类型。
 
  zend_value 联合体
 
  zend_value 类型也是在 php-src/Zend/zend_types.h 中定义的,是一个联合体,下面是 zend_value 联合体的定义,左侧是源码。同样在右侧我也做了简单的翻译,把 zend_long uint32_t 翻译为普通类型便于查看。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
              《源代码》                                              《翻译后》
 
  ------------------------------------------------------------------------------------
 
  typedef union _zend_value {                         | typedef union _zend_value {
 
      zend_long         lval; /* long value */        |     long              lval;
 
      double            dval; /* double value */      |     double            dval;
 
      zend_refcounted  *counted;                      |     zend_refcounted  *counted;
 
      zend_string      *str;                          |     zend_string      *str;
 
      zend_array       *arr;                          |     zend_array       *arr;
 
      zend_object      *obj;                          |     zend_object      *obj;
 
      zend_resource    *res;                          |     zend_resource    *res;
 
      zend_reference   *ref;                          |     zend_reference   *ref;
 
      zend_ast_ref     *ast;                          |     zend_ast_ref     *ast;
 
      zval             *zv;                           |     zval             *zv;
 
      void             *ptr;                          |     void             *ptr;
 
      zend_class_entry *ce;                           |     zend_class_entry *ce;
 
      zend_function    *func;                         |     zend_function    *func;
 
      struct {                                        |     struct {
 
          uint32_t w1;                                |         unsigned int w1;
 
          uint32_t w2;                                |         unsigned int w2;
 
      } ww;                                           |     } ww;
 
  } zend_value;                                       | } zend_value;
 
  联合体的一个特点是其占用的内存是其属性中最大类型对应的长度。其中的 zend_long 就是 long 类型,可以看到 long 类型的 lval 和 double 类型的 dval 占用的长度都是 8 个字节。里面其他指针类型,也均为 8 个字节。最后面的结构体属性 ww 是由两个 int 型构成,长度相加也是 8 个字节。因此此联合体的长度为 8 个字节。
 
  在我们写的 PHP 代码中,整型和浮点型数据的值会直接存放到 lval 和 dval 中。如果是字符串、数组以及其他类型时会开辟一段空间存储数据,并将其地址存放在 zend_value 中,也就是 zval.value 属性,如:zval.value.zend_long = 9527、zval.value.zend_string = 字符串地址 、zval.value.zend_array = 数组地址。然后在 zval.u1.v.type 上标记这个 zval.value 是整型、或浮点型、或字符串、或其他类型。
 
  zval.u1.v.type 类型定义也是在 php-src/Zend/zend_types.h 文件中,全部的定义如下:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  /* 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
 
  /* constant expressions */
 
  #define IS_CONSTANT_AST 11
 
  /* internal types */
 
  #define IS_INDIRECT     13
 
  #define IS_PTR          14
 
  #define IS_ALIAS_PTR    15
 
  #define _IS_ERROR       15
 
  /* fake types used only for type hinting (Z_TYPE(zv) can not use them) */
 
  #define _IS_BOOL        16
 
  #define IS_CALLABLE     17
 
  #define IS_ITERABLE     18
 
  #define IS_VOID         19
 
  #define _IS_NUMBER      20
 
  zval 结构体内存占用
 
  接下来我们分析一下 zval 所需占用的内存。
 
  value:zend_value 类型 8 个字节。
 
  u1:
 
  u1.v.type:unsigned char 1 个字节,u1.v.type_flags:unsigned char 1 个字节,u1.v.u:联合体中只有一个 unsigned short 的 extra 属性 2 个字节,因此 u1.v 的结构体总共是 4 个字节。
 
  u1.type_info:unsigned int 4 个字节。
 
  因此 u1 这个联合体的长度取最长的属性的长度:4 个字节。
 
  u2:也是一个联合体,里面都是 int 型的属性,因此长度是 4 个字节。
 
  所以 zval 总共占用的内存是 8 + 4 + 4 = 16 个字节。
 
  也就是说当我们在写 PHP 代码时,如果创建了一个整型的变量,那么实际上它在运行中会占用 16 个字节的内存,内存开销至少是 C 语言的两倍。当然这两倍的开销也带来了 PHP 处理变量的灵活性。

(编辑:我爱故事小小网_铜陵站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读