跟厂长学PHP7内核(七):常见变量类型的基本结构

上篇文章讲述了变量的存储结构zval,今天我们就来学习一下几个常见变量类型的基本结构。 一、类型一览 zval中的u1.v.type用来存储变量的类型,而zval.value存储的是不同类型对应的值,所以type决定value取值的地方,以下是PHP7所定义的所有类型。 #define IS_UNDEF 0 /* 标记未使用类型 */ #define IS_NULL 1 /* NULL */ #define IS_FALSE 2 /* 布尔类型false */ #define IS_TRUE 3 /* 布尔类型true */ #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 /* 引用 */ /* 常量相关类型 */ #define IS_CONSTANT 11 /* 常量 */ #define IS_CONSTANT_AST 12 /* 常量抽象语法树 */ /* 伪类型 */ #define _IS_BOOL 13 #define IS_CALLABLE 14 /* 内部类型 */ #define IS_INDIRECT 15 /* 间接类型 */ #define IS_PTR 17 /* 指针类型 */ IS_UNDEF:标记未定义,表示数据可以被覆盖或删除。 IS_TRUE/IS_FALSE:本来在PHP5中统一用IS_BOOL来代替,这里分成两个可以避免一次类型的检查。 IS_REFERRENCE:引用类型,用于处理PHP脚本中的符号&。 IS_PTR:用来解析value.ptr,通常用在函数类型上,比如声明一个函数或方法。 IS_INDIRECT:用于解决在全局符号表访问CV变量的问题。 二、不同类型的结构 刚才聊到zval.u1.v.type决定了zval.value,下面来看一下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; /* zval类型 */ void *ptr; /* 指针类型 */ zend_class_entry *ce; /* class类型 */ zend_function *func; /* function类型 */ struct { uint32_t w1; uint32_t w2; } ww; } zend_value; 基本可以看出该结构体的变量和上文定义的类型是一一对应的,我们抽取几个常用的类型讲述一下。 2.1、字符串 字符串str对应的结构体是zend_string,它有四个成员,定义如下。 struct _zend_string { zend_refcounted_h gc; zend_ulong h; /* hash value */ size_t len; char val[1]; }; gc:变量的引用计数信息,用于内存管理。 h:字符串通过Time33算法计算的到的Hash值,避免了在数组操作中hash值的重复计算,据说提高了PHP7百分之5的性能。 len:字符串的长度。 val:字符串的内容,val[1]并不表示只能存储1个字节,在字符串分配时实际上是操作了malloc(sizeof(zend_string)+字符串你长度),也就是会多分配一些内存,而多出来的内存起始位置就是val,这样就可以将字符串直接存储到val,并通过val进行读取,这种采用了柔性数组的方式,读写效率更高。 结构图 2.2、数组 成员变量arr对应的结构体是zend_array,它就是你可能有所耳闻的HashTable,zend_array结构体定义如下。 struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar reserve) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; }; nTableMask:根据key的hash code映射元素存储位置时有用到,它的值是nTableSize的负数,nTableMask=-nTableSize。 arData:数组的每一个元素都保存在这里,默认指向第一个元素。 nNumUsed:当前使用的Bucket数,但不都是有效的,因为有的Bucket虽然被unset了但是没有马上被删除,而是做了IS_UNDEF标记。 nNumOfElements:有效的Bucket数,这个就与上面不同了,这里记录的是真实有效的Bucket数量。 nTableSize:数组的总容量。 nIternalPointer:当前遍历的指针。 nNextFreeElement:下一个索引的值,比如每次给数组新增数据时,该值就会加一,$a[] = 1。 pDestructor:析构函数,在删除或覆盖某个元素时,调用该函数,可以对旧元素进行清理。 u:这里的u主要还是起到辅助作用,比如flags用来设置散列表的一些属性是否持久化、是否已经初始化等。 2.3、对象 struct _zend_object { zend_refcounted_h gc; uint32_t handle; zend_class_entry *ce; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; }; gc:引用计数。 handle:一次请求期间对象的编号,每一个对象都有一个唯一的编号,与创建的先后顺序有关,主要是在垃圾回收的时候使用。 ce:该对象所属的类。 handlers:对象操作的处理函数,比如成员属性的读写、成员方法的获取、对象的销毁克隆等。 properties:普通成员属性的哈希表,初始化对象时该值为NULL。 properties_table:用来存储普通成员的属性值,对象对非静态成员属性的操作就是通过这个数组。 参考文献 《PHP7内核剖析》 《PHP7底层设计与源码实现》 我执著于这样的细节,其实是执著于原味的生活,我固执地守候生活的起点,不管以后走多么远,那个起点其实也是我的终点。欢迎大家关注我的公众号: https://www.cnblogs.com/enochzzg/p/9668374.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信