本章主要内容来源于秦朋先生编著的《PHP7内核剖析》这本书籍,感兴趣的小伙伴可以购买该书,进行系统性学习。

概述


变量是一种常用的数据类型,主要由变量名、变量值、变量类型三个部分构成。PHP中变量名与变量值可以简单的对应为:zval、zend_value。

PHP中变量的内存是通过引用计数进行管理的,PHP7中的引用计数转移到了具体的value结构中,而不再是zval中,这是PHP7与旧版本的一个区别,变量之间的传递、赋值通常也针对zend_value。

普通变量定义的方式包含了两步:变量定义、变量初始化。只定义而不初始化变量也是可以的。

变量类型


PHP基本数据类型共分8种:

  • 标量数据类型:整型、浮点型、字符串、布尔型
  • 复合数据类型:数组、对象
  • 特殊数据类型:资源、NULL

在PHP源代码的zend_types.h文件中,定义了更加细分的数据类型,比如布尔型会在内部实际分为IS_TRUE、IS_FALSE两种。全部数据类型如下:

  1. /* regular data types */
  2. #define IS_UNDEF 0
  3. #define IS_NULL 1
  4. #define IS_FALSE 2
  5. #define IS_TRUE 3
  6. #define IS_LONG 4
  7. #define IS_DOUBLE 5
  8. #define IS_STRING 6
  9. #define IS_ARRAY 7
  10. #define IS_OBJECT 8
  11. #define IS_RESOURCE 9
  12. #define IS_REFERENCE 10
  13. /* constant expressions */
  14. #define IS_CONSTANT_AST 11
  15. /* internal types */
  16. #define IS_INDIRECT 13
  17. #define IS_PTR 14
  18. #define IS_ALIAS_PTR 15
  19. #define _IS_ERROR 15
  20. /* fake types used only for type hinting (Z_TYPE(zv) can not use them) */
  21. #define _IS_BOOL 16
  22. #define IS_CALLABLE 17
  23. #define IS_ITERABLE 18
  24. #define IS_VOID 19
  25. #define _IS_NUMBER 20

变量实现


在PHP源代码的zend_types.h文件中,同样发现代码中定义了_zval_struct结构体,该结构体就是PHP表示变量的基础数据结构,而不同类型的变量值则通过结构体的成员zend_value联合体来表示,zval结构体中还定义了其他联合体,具体结构如下:

  1. struct _zval_struct {
  2. zend_value value; /* value */
  3. union {
  4. struct {
  5. ZEND_ENDIAN_LOHI_3(
  6. zend_uchar type, /* active type */
  7. zend_uchar type_flags,
  8. union {
  9. uint16_t extra; /* not further specified */
  10. } u)
  11. } v;
  12. uint32_t type_info;
  13. } u1;
  14. union {
  15. uint32_t next; /* hash collision chain */
  16. uint32_t cache_slot; /* cache slot (for RECV_INIT) */
  17. uint32_t opline_num; /* opline number (for FAST_CALL) */
  18. uint32_t lineno; /* line number (for ast nodes) */
  19. uint32_t num_args; /* arguments number for EX(This) */
  20. uint32_t fe_pos; /* foreach position */
  21. uint32_t fe_iter_idx; /* foreach iterator index */
  22. uint32_t access_flags; /* class constant access flags */
  23. uint32_t property_guard; /* single property guard */
  24. uint32_t constant_flags; /* constant flags */
  25. uint32_t extra; /* not further specified */
  26. } u2;
  27. };

联合体u1实际上联合了一个结构体v和一个32位无符号整型type_info。
结构体v定义了三个成员:

  • type用于标识value类型,既变量类型小节提到的源代码中定义的数据类型,类似IS_*等。
  • type_flags是类型掩码,用于变量的内存管理。
  • 联合体u是一个扩展字段,可暂时忽略。

type_info实际上是将v结构的3个成员组合到一起,type和type_flags各占一个字节,u占用两个字节,总共四个字节,可通过type_info直接获取v中的值。

联合体u2纯粹用于一些辅助功能,zval结构中zend_value和u1占用的空间分别是8byte和4byte,共12byte,系统会进行字节补齐,为了不浪费4byte的空间,PHP定义了一个u2结构体,最终zval结构的大小就是16byte。这个结构在特殊情况下会使用,比如next在散列表解决hash冲突时会用到、fe_pos在foreach遍历时会用到。

zend_value联合体比较简单,各类型根据自己的类型选择使用不同的成员。其中,整型、浮点型的值直接存储到zend_value中;其他类型则是指针,指向具体类型的结构;布尔型直接拆分为true和false两种,直接通过type类型来区分,不需要具体的value。具体结构如下:

  1. typedef union _zend_value {
  2. zend_long lval; /* long value */
  3. double dval; /* double value */
  4. zend_refcounted *counted;
  5. zend_string *str;
  6. zend_array *arr;
  7. zend_object *obj;
  8. zend_resource *res;
  9. zend_reference *ref;
  10. zend_ast_ref *ast;
  11. zval *zv;
  12. void *ptr;
  13. zend_class_entry *ce;
  14. zend_function *func;
  15. struct {
  16. uint32_t w1;
  17. uint32_t w2;
  18. } ww;
  19. } zend_value;

zend_value中定义的ast、zv、ptr等类型,只给内核自己使用。