zoukankan      html  css  js  c++  java
  • 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,传入的参数是对象时,会获取对象的属性,所以遍历对象就是在变量对象的属性。

    三、参考文献

  • 相关阅读:
    c语言结构体数组引用
    c语言结构体数组定义的三种方式
    如何为SAP WebIDE开发扩展(Extension),并部署到SAP云平台上
    SAP SRM ABAP Webdynpro和CFCA usb key集成的一个原型开发
    使用SAP API portal进行SAP SuccessFactors的API测试
    SAP UI5应用里的页面路由处理
    在SAP WebIDE Database Explorer里操作hdi实例
    如何使用SAP事务码SAT进行UI应用的性能分析
    使用SAP WebIDE进行SAP Cloud Platform Business Application开发
    SAP CRM WebClient UI ON_NEW_FOCUS的用途
  • 原文地址:https://www.cnblogs.com/pingyeaa/p/9626946.html
Copyright © 2011-2022 走看看