当前位置:首页 > PHP > 正文内容

PHP内核分析之常见变量基本结构(六)

phpmianshi2年前 (2018-06-07)PHP192

一、类型一览

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:用来存储普通成员的属性值,对象对非静态成员属性的操作就是通过这个数组。

版权声明:本文由PHP面试资料网发布,如需转载请注明出处。
分享给朋友:

相关文章

PHP中如何实现进程间通讯

PHP中如何实现进程间通讯

进程间通讯机制——IPC(Inter-Process-Communication)。为了使得php5可以使用共享内存和信号量,必须在编译php5程序时激活shmop和sysvsem这两个扩展模块。  ...

tp5.0.x 5.1.x 最新getshell漏洞

概况近日thinkphp团队发布了版本更新:https://blog.thinkphp.cn/869075 ,其中修复了一处getshell漏洞。影响范围5.1.x < 5.1.315.0.x&...

PHP内核分析之生命周期五个阶段(四)

一、模块初始化阶段我们先来看一下该阶段的每个函数的作用。1.1、sapi_initialize_request_empty函数// main/SAPI.c SAPI_API vo...

php-fpm backlog参数优化

php-fpm backlog参数优化

一、问题分析       1、分析php-fpm.slow.log发现没有执行慢的地方,然后把目光放到了nginx 与php建立连接的阶段上,使用tcpdump...

通过 PHP OPcache 让你的 Laravel 应用运行速度飞起来

Laravel 优化介绍:https://segmentfault.com/a/11900000115690121.缓存配置信息 php artisan config:cache2.缓存路由信息 ph...

PHP中 array_walk array_map array_filter区别

array_walk:array_walk — 使用用户自定义函数对数组中的每个元素做回调处理1. 用户自定义的函数处理每一个元素2. 直接修改原数组,不会创建新的数组3. 可以传递额外的参数更多信息...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。