• PHP7内核(六):变量之zval


    记得网上流传甚广的段子“PHP是世界上最好的语言”,暂且不去讨论是否言过其实,但至少PHP确实有独特优势的,比如它的弱类型,即只需要$符号即可声明变量,使得PHP入手门槛极低,成为大家所青睐的Web服务端语言。那么它的变量是如何实现的呢?我们今天就来学习一下PHP的基本变量。

    一、引言

    PHP的变量存储在zval结构体中,在执行阶段中编译为op_array时就能看到zval的身影。结构体定义在Zend/zend_types.h中,定义内容如下所示:

    struct _zval_struct {
    	zend_value        value;			/* value */
    	union {
    		struct {
    			ZEND_ENDIAN_LOHI_4(
    				zend_uchar    type,			/* active type */
    				zend_uchar    type_flags,
    				zend_uchar    const_flags,
    				zend_uchar    reserved)	    /* 保留字段 */
    		} v;
    		uint32_t type_info;
    	} u1;
    	union {
    		uint32_t     var_flags;
    		uint32_t     next;                 /* hash collision chain */
    		uint32_t     cache_slot;           /* literal cache slot */
    		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 */
    	} u2;
    };
    

    二、结构体剖析

    2.1、zend_value

    结构体的第一个变量是zend_value,顾名思义,它其实也是一个结构体,用于存放变量的值,比如整型、浮点型、引用计数、字符串、数组、对象、资源等。zend_value定义了众多类型的指针,但这些类型并不都是变量的类型,有些是给内核自己使用的,比如指针ast、zv、ptr。

    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.2、u1

    u1是一个联合体,它联合了结构体v和整型type_info。下面我们先来看一下结构体v的构成。

    union {
    	struct {
    		ZEND_ENDIAN_LOHI_4(
    			zend_uchar    type,			/* active type */
    			zend_uchar    type_flags,
    			zend_uchar    const_flags,
    			zend_uchar    reserved)	    /* call info for EX(This) */
    	} v;
    	uint32_t type_info;
    } u1;
    

    2.2.1、type

    type是指变量的类型,刚在2.1中讲到了zend_value是用来存储变量的值,所以也应该有地方存储变量的类型,而这就是type的职责。以下是PHP定义的所有变量类型,有我们熟知的布尔、NULL、浮点、数组、字符串等类型。也有陌生的undef、indirect、ptr类型,变量类型在下一章中详解,这里不再赘述。

    /* 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					11
    #define IS_CONSTANT_AST				12
    
    /* fake types */
    #define _IS_BOOL					13
    #define IS_CALLABLE					14
    
    /* internal types */
    #define IS_INDIRECT             	15
    #define IS_PTR						17
    

    2.2.2、type_flags

    可以把它理解为子类型,上面提到了变量的类型,这个是针对不同类型的子类型或标记,type_flags一共有以下6种。

    /* zval.u1.v.type_flags */
    #define IS_TYPE_CONSTANT			(1<<0)  /* 常量 */
    #define IS_TYPE_IMMUTABLE			(1<<1)  /* 不可变的类型 */
    #define IS_TYPE_REFCOUNTED			(1<<2)  /* 需要引用计数的类型 */
    #define IS_TYPE_COLLECTABLE			(1<<3)  /* 可能包含循环引用的类型 */
    #define IS_TYPE_COPYABLE			(1<<4)  /* 可被复制的类型 */
    #define IS_TYPE_SYMBOLTABLE			(1<<5)  /* 符号表类型 */
    

    2.2.3、const_flags

    常量类型的标记,对应的属性为:

    /* zval.u1.v.const_flags */
    #define IS_CONSTANT_UNQUALIFIED		0x010
    #define IS_LEXICAL_VAR				0x020
    #define IS_LEXICAL_REF				0x040
    #define IS_CONSTANT_CLASS           0x080  /* __CLASS__ in trait */
    #define IS_CONSTANT_IN_NAMESPACE	0x100  /* used only in opline->extended_value */
    

    2.2.4、type_info

    type_info与结构体v共用内存,修改type_info等同于修改结构体v的值,所以type_info是v中四个char的组合。

    2.3、u2

    本来使用u1和zend_value就可以表示变量的,没有必要定义u2,但是我们来看一下,如果没有u2,在内存对齐的情况下zval内存大小为16个字节,当联合了u2后依然是占用16个字节。既然有或没有占用内存大小相同,不如用它来记录一些附属信息。下面我们来看下u2都存储了哪些内容。

    2.3.1、next

    用来解决哈希冲突问题,记录冲突的下一个元素位置。

    2.3.2、cache_slot

    运行时缓存,在执行函数时回去缓存中查找,若缓存中没有则到全局function表中查找。

    2.3.3、lineno

    文件执行的行号,应用在AST节点上。Zend引擎在词法和语法解析时会把当前执行的文件行号记录下来,记录在zend_ast中的lineno中。

    2.3.4、num_args

    函数调用时传入函数的参数个数。

    2.3.5、fe_pos

    用于遍历数组时记录当前遍历的位置,比如每次执行foreach时fe_pos都会加一,当再次调用foreach进行遍历时,fe_post会进行重置。

    2.3.6、fe_iter_idx

    这个与fe_pos类似,只不过它是针对对象的。对象的属性也是HashTable,传入的参数是对象时,会获取对象的属性,所以遍历对象就是在变量对象的属性。

    三、参考文献

  • 相关阅读:
    设计模式学习心得5
    HTTP协议 (六) 状态码详解
    HTTP协议 (五) 代理
    HTTP协议 (四) 缓存
    HTTP协议 (三) 压缩
    HTTP协议 (二) 基本认证
    HTTP协议 (一) HTTP协议详解
    java中abstract和interface的區別(轉)
    基于TCP的字符串传输程序
    文件比较
  • 原文地址:https://www.cnblogs.com/pingyeaa/p/9626946.html
Copyright © 2020-2023  润新知