PHP内核 - 数据类型 - 变量

PHP   PHP内核  

一、前言

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

  PHP中变量的内存是通过引用计数进行管理的,而且PHP7中引用计数转移到了具体的value结构中而不再是zval中,这也是PHP7与之前版本不同的一个地方。变量之间的传递、赋值通常也针对zend_value。
  
  PHP中通过 $ 符号定义一个变量,在定义的同时可以进行初始化,在变量使用前不需要提前声明。事实上,普通变量定义的方式包含了两步:变量定义、变量初始化,只定义而不初始化也是可以的,比如

  1. $a;
  2. $b = 1;

这段代码在执行时会分配两个zval,也就是定义了两个变量,只不过 $a 没有值而已,相当于unset() 了。

  

二、变量类型

PHP中的变量类型,也就是数据类型。宏观角度看可以分为以下8种:

(1)标量类型:字符串、整型、浮点型、布尔型

(2)符合类型:数组、对象

(3)特殊类型:资源、NULL

具体到内部实现,实际上会细分出更多的类型,比如布尔型在内部实际分为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_ERROR 15
  19. /* fake types used only for type hinting (Z_TYPE(zv) can not use them) */
  20. #define _IS_BOOL 16
  21. #define IS_CALLABLE 17
  22. #define IS_ITERABLE 18
  23. #define IS_VOID 19
  24. #define _IS_NUMBER 20

(该数据更新于2019-08-08 PHP源码 /Zend/zend_types.h)

  

三、内部实现

PHP中,通过zval这个结构体来表示一个变量,而不同类型的变量值则通过zval嵌入的一个联合体表示,即zval。

通过zval、zend_value 以及不同类型的结构实现了PHP基础的数据类型,另外,zval并不只是 PHP 变量会使用,它也是内核中的一个通用结构,用于同一函数的参数,很多类型是供内核自己使用的,这里简单了解下。

  1. struct _zval_struct {
  2. // 变量值
  3. zend_value value;
  4. union {
  5. struct {
  6. // 下面这个宏是为了兼容大小字节序,小字节序就是下面的顺序,大字节序则是下面4个顺序翻转,忽略即可
  7. ZEND_ENDIAN_LOHI_3 (
  8. // 变量类型
  9. zend_uchar type, /* active type */
  10. // 类型掩码,各类型会有不同的几种属性,内存管理会用到
  11. zend_uchar type_flags,
  12. union {
  13. uint16_t extra; /* not further specified */
  14. } u)
  15. } v;
  16. uint32_t type_info;
  17. } u1;
  18. // 一些辅助值
  19. union {
  20. uint32_t next; /*hash collision chain */
  21. uint32_t cache_slot; /*cache slot (for RECV_INIT) */
  22. uint32_t opline_num; /*opline number (for FAST_CALL)*/
  23. uint32_t lineno; /*line number (for ast nodes) */
  24. uint32_t num_args; /*arguments number for EX(This) */
  25. uint32_t fe_pos; /* foreach position */
  26. uint32_t fe_iter_idx; /* foreach iterator index */
  27. uint32_t access_flags; /*class constant access flags*/
  28. uint32_t property_guard; /* single property guard */
  29. uint32_t constant_flags; /* constant flags */
  30. uint32_t extra; /* not further specified */
  31. } u2;
  32. };
  33. // 文件位于 /Zend/zend_types.h

zval除了嵌入了一个zend_value用来保存具体的变量值,还有两个特殊的union。

u1: 这个结构看起来比较复杂,实际上它只是联合了一个结构体 v 和一个32位无符号整型type_info。

你可以完全不用注意这个结构的定义中的ZEND_ENDIAN_LOHI_3 这个宏,它仅仅只是用于表示拥有不同的字节序的机器中的可预测的内存布局情况。

v 中定义了2个成员:type 用于标识 value 类型;type_flags 是类型掩码,用于变量的内存管理。type_info 实际上是将 v 结构的 2 个成员组合到了一起,v 中的成员各占一个字节,总共 2 个字节,type_info 也是 2 个字节,每个字节对应 v 的一个成员,可以直接通过 type_info 位移获得 v 成员的值。

注:PHP7中,将 u1 中的成员去掉了两个, PHP7.3之前的版本是 4 个成员。

(该数据更新于2019-08-08 PHP源码 /Zend/zend_types.h)

u2: 这个结构纯粹用于一些辅助功能。

zend_value 的结构比较简单,它是一个联合体,各类型根据自己的类型选择使用不同的成员,其中整型、浮点型的值直接存储在zend_value 中,其他类型为指针,指向具体类型的结构。

另外,zend_value 中并没有布尔型,这是因为PHP7中将布尔型具体拆分为了true、false两种类型,它们直接通过 type 类型区分,因此不需要具体的value,而在 PHP旧版本中,布尔型也是通过整型进行区分的。

  1. typedef union _zend_value {
  2. zend_long lval; // 整型
  3. double dval; // 浮点型
  4. zend_refcounted *counted; // 获取不同类型结构的gc头部
  5. zend_string *str; // 字符串类型
  6. zend_array *arr; // 数组类型
  7. zend_object *obj; // 对象类型
  8. zend_resource *res; // 资源类型
  9. zend_reference *ref; // 引用类型,通过&$var_name定义的
  10. zend_ast_ref *ast; // 下面几个都是内核自己使用的 value
  11. zval *zv; // 指向另一个 zval
  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、ptr、zv、ce 等。

 

 



Top