本章主要内容来源于秦朋先生编著的《PHP7内核剖析》这本书籍,感兴趣的小伙伴可以购买该书,进行系统性学习。
概述
变量是一种常用的数据类型,主要由变量名、变量值、变量类型三个部分构成。PHP中变量名与变量值可以简单的对应为:zval、zend_value。
PHP中变量的内存是通过引用计数进行管理的,PHP7中的引用计数转移到了具体的value结构中,而不再是zval中,这是PHP7与旧版本的一个区别,变量之间的传递、赋值通常也针对zend_value。
普通变量定义的方式包含了两步:变量定义、变量初始化。只定义而不初始化变量也是可以的。
变量类型
PHP基本数据类型共分8种:
- 标量数据类型:整型、浮点型、字符串、布尔型
- 复合数据类型:数组、对象
- 特殊数据类型:资源、NULL
在PHP源代码的zend_types.h文件中,定义了更加细分的数据类型,比如布尔型会在内部实际分为IS_TRUE、IS_FALSE两种。全部数据类型如下:
/* 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
变量实现
在PHP源代码的zend_types.h文件中,同样发现代码中定义了_zval_struct结构体,该结构体就是PHP表示变量的基础数据结构,而不同类型的变量值则通过结构体的成员zend_value联合体来表示,zval结构体中还定义了其他联合体,具体结构如下:
struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_3(
zend_uchar type, /* active type */
zend_uchar type_flags,
union {
uint16_t extra; /* not further specified */
} u)
} v;
uint32_t type_info;
} u1;
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* cache slot (for RECV_INIT) */
uint32_t opline_num; /* opline number (for FAST_CALL) */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t access_flags; /* class constant access flags */
uint32_t property_guard; /* single property guard */
uint32_t constant_flags; /* constant flags */
uint32_t extra; /* not further specified */
} u2;
};
联合体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。具体结构如下:
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
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;
zend_value中定义的ast、zv、ptr等类型,只给内核自己使用。