我们在php中用到的变量,在底层的C语言代码里是一个结构体,由四个成员组成
typedef struct _zval_struct {
zvalue_value value; /* 变量的值,也是一个结构体 */
zend_uint refcount__gc; /* 变量的引用计数 typedef unsigned int zend_uint */
zend_uchar type; /* 变量的类型 typedef unsigned char zend_uchar */
zend_uchar is_ref__gc; /* 是否引用 typedef unsigned char zend_uchar*/
} zval;
typedef union _zvalue_value {
long lval; /* 长整型,存储整数,bool,资源类型 */
double dval; /* 浮点型,存储小数 */
struct {
char *val;
int len; /* */
} str; /* 字符串,val是字符串指针,len是字符串长度 */
HashTable *ht; /* hashtable 即PHP数组 */
zend_object_value obj; /* php对象 */
} zvalue_value;

php变量的类型,即zval的type成员,一共有8种

 
类型   zvalue_value中存储的成员 说明
IS_NULL 不存储值 NULL
IS_LONG lval 整型
IS_DOUBLE dval 浮点型
IS_BOOL lval 布尔
IS_RESOURCE lval 资源
IS_STRING str 字符串
IS_ARRAY ht 数组
IS_OBJECT obj 对象

这些类型都是宏定义,在Zend/zend.h中可以查到

 #define IS_NULL     0
#define IS_LONG 1
#define IS_DOUBLE 2
#define IS_BOOL 3
#define IS_ARRAY 4
#define IS_OBJECT 5
#define IS_STRING 6
#define IS_RESOURCE 7

通常我们不会直接使用php变量的成员,例如zval->type或zvalue_value->lval,为了代码的兼容性,zend给我们提供了很多的API方便我们操作变量

宏定义原型 获取变量   描述
zend_uchar Z_TYPE(zval zv) type 返回变量类型
long Z_LVAL(zval zv) value.lval  返回zvalue_value的lval成员
zend_bool Z_BVAL(zval zv) value.lval  返回zvalue_value的lval成员,并且转换成zend_bool类型
double Z_DVAL(zval zv) value.dval  
long Z_RESVAL(zval zv) value.lval 返回zvalue_value的lval成员,此时的type是IS_RESOURCE
char* Z_STRVAL(zval zv) value.str.val 返回字符串的值
int Z_STRLEN(zval zv) value.str.len 返回字符串的长度
HashTable* Z_ARRVAL(zval zv) value.ht 返回hashtable即数组
zend_object_value Z_OBJVAL(zval zv) value.obj returns object value
uint Z_OBJ_HANDLE(zval zv) value.obj.handle returns the object handle for object value
zend_object_handlers* Z_OBJ_HT_P(zval zv) value.obj.handlers returns the handler table for object value
zend_class_entry* Z_OBJCE(zval zv) value.obj returns the class entry for object value
HashTable* Z_OBJPROP(zval zv) value.obj returns the properties of object value
HashTable* Z_OBJPROP(zval zv) value.obj returns the properties of object value
HashTable* Z_OBJDEBUG(zval zv) value.obj if an object has the get_debug_info handler set, it is called, else Z_OBJPROP is called

以上这些API其实就是宏定义,上面列出的每个宏同时都有另外两种类似的定义,以Z_TYPE为例

#define Z_TYPE(zval)        (zval).type         //参数是zval
#define Z_TYPE_P(zval_p)    Z_TYPE(*zval_p)     //参数是zval的指针
#define Z_TYPE_PP(zval_pp)  Z_TYPE(**zval_pp)   //参数是zval的指针的指针
以上这些宏定义,在Zend/zend_operators.h中可以查到
 #define Z_LVAL(zval)            (zval).value.lval
#define Z_BVAL(zval) ((zend_bool)(zval).value.lval)
#define Z_DVAL(zval) (zval).value.dval
#define Z_STRVAL(zval) (zval).value.str.val
#define Z_STRLEN(zval) (zval).value.str.len
#define Z_ARRVAL(zval) (zval).value.ht
#define Z_AST(zval) (zval).value.ast
#define Z_OBJVAL(zval) (zval).value.obj
#define Z_OBJ_HANDLE(zval) Z_OBJVAL(zval).handle
#define Z_OBJ_HT(zval) Z_OBJVAL(zval).handlers
#define Z_OBJCE(zval) zend_get_class_entry(&(zval) TSRMLS_CC)
#define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(&(zval) TSRMLS_CC)
#define Z_OBJ_HANDLER(zval, hf) Z_OBJ_HT((zval))->hf
#define Z_RESVAL(zval) (zval).value.lval
#define Z_OBJDEBUG(zval,is_tmp) (Z_OBJ_HANDLER((zval),get_debug_info)?Z_OBJ_HANDLER((zval),get_debug_info)(&(zval),&is_tmp TSRMLS_CC):(is_tmp=0,Z_OBJ_HANDLER((zval),get_properties)?Z_OBJPROP(zval):NULL)) #define Z_LVAL_P(zval_p) Z_LVAL(*zval_p)
#define Z_BVAL_P(zval_p) Z_BVAL(*zval_p)
#define Z_DVAL_P(zval_p) Z_DVAL(*zval_p)
#define Z_STRVAL_P(zval_p) Z_STRVAL(*zval_p)
#define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)
#define Z_ARRVAL_P(zval_p) Z_ARRVAL(*zval_p)
#define Z_AST_P(zval_p) Z_AST(*zval_p)
#define Z_OBJPROP_P(zval_p) Z_OBJPROP(*zval_p)
#define Z_OBJCE_P(zval_p) Z_OBJCE(*zval_p)
#define Z_RESVAL_P(zval_p) Z_RESVAL(*zval_p)
#define Z_OBJVAL_P(zval_p) Z_OBJVAL(*zval_p)
#define Z_OBJ_HANDLE_P(zval_p) Z_OBJ_HANDLE(*zval_p)
#define Z_OBJ_HT_P(zval_p) Z_OBJ_HT(*zval_p)
#define Z_OBJ_HANDLER_P(zval_p, h) Z_OBJ_HANDLER(*zval_p, h)
#define Z_OBJDEBUG_P(zval_p,is_tmp) Z_OBJDEBUG(*zval_p,is_tmp) #define Z_LVAL_PP(zval_pp) Z_LVAL(**zval_pp)
#define Z_BVAL_PP(zval_pp) Z_BVAL(**zval_pp)
#define Z_DVAL_PP(zval_pp) Z_DVAL(**zval_pp)
#define Z_STRVAL_PP(zval_pp) Z_STRVAL(**zval_pp)
#define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)
#define Z_ARRVAL_PP(zval_pp) Z_ARRVAL(**zval_pp)
#define Z_AST_PP(zval_p) Z_AST(**zval_p)
#define Z_OBJPROP_PP(zval_pp) Z_OBJPROP(**zval_pp)
#define Z_OBJCE_PP(zval_pp) Z_OBJCE(**zval_pp)
#define Z_RESVAL_PP(zval_pp) Z_RESVAL(**zval_pp)
#define Z_OBJVAL_PP(zval_pp) Z_OBJVAL(**zval_pp)
#define Z_OBJ_HANDLE_PP(zval_p) Z_OBJ_HANDLE(**zval_p)
#define Z_OBJ_HT_PP(zval_p) Z_OBJ_HT(**zval_p)
#define Z_OBJ_HANDLER_PP(zval_p, h) Z_OBJ_HANDLER(**zval_p, h)
#define Z_OBJDEBUG_PP(zval_pp,is_tmp) Z_OBJDEBUG(**zval_pp,is_tmp)

如果你是初始变量赋值,或者需要同时改变变量的类型,你可以直接使用以下这些宏定义函数

#define ZVAL_RESOURCE(z, l) do {    \//资源类型,值为l
zval *__z = (z); \
Z_LVAL_P(__z) = l; \
Z_TYPE_P(__z) = IS_RESOURCE;\
} while () #define ZVAL_BOOL(z, b) do { \//布尔类型
zval *__z = (z); \
Z_LVAL_P(__z) = ((b) != ); \
Z_TYPE_P(__z) = IS_BOOL; \
} while () #define ZVAL_NULL(z) { \//NULL
Z_TYPE_P(z) = IS_NULL; \
} #define ZVAL_LONG(z, l) { \//整型
zval *__z = (z); \
Z_LVAL_P(__z) = l; \
Z_TYPE_P(__z) = IS_LONG; \
} #define ZVAL_DOUBLE(z, d) { \//浮点型
zval *__z = (z); \
Z_DVAL_P(__z) = d; \
Z_TYPE_P(__z) = IS_DOUBLE; \
} #define ZVAL_STRING(z, s, duplicate) do { \//字符串,duplicate表示是否赋值一份字符串再赋值
const char *__s=(s); \
zval *__z = (z); \
Z_STRLEN_P(__z) = strlen(__s); \
if (UNEXPECTED(Z_STRLEN_P(__z) < )) { \
zend_error(E_ERROR, "String size overflow"); \
} \
Z_STRVAL_P(__z) = (duplicate?estrndup(__s, Z_STRLEN_P(__z)):(char*)__s);\
Z_TYPE_P(__z) = IS_STRING; \
} while () #define ZVAL_STRINGL(z, s, l, duplicate) do { \//字符串,l表示字符串的长度
const char *__s=(s); int __l=l; \
zval *__z = (z); \
Z_STRLEN_P(__z) = __l; \
Z_STRVAL_P(__z) = (duplicate?estrndup(__s, __l):(char*)__s);\
Z_TYPE_P(__z) = IS_STRING; \
} while () #define ZVAL_EMPTY_STRING(z) do { \//设置空字符串
zval *__z = (z); \
Z_STRLEN_P(__z) = ; \
Z_STRVAL_P(__z) = STR_EMPTY_ALLOC();\
Z_TYPE_P(__z) = IS_STRING; \
} while () #define ZVAL_ZVAL(z, zv, copy, dtor) do { \//赋值一个zval变量,copy表示是否硬拷贝zval中的数据,dtor表示是否对zv尝试unset
zval *__z = (z); \
zval *__zv = (zv); \
ZVAL_COPY_VALUE(__z, __zv); \
if (copy) { \
zval_copy_ctor(__z); \
} \
if (dtor) { \
if (!copy) { \
ZVAL_NULL(__zv); \
} \
zval_ptr_dtor(&__zv); \
} \
} while () #define ZVAL_FALSE(z) ZVAL_BOOL(z, 0)//布尔值TRUE
#define ZVAL_TRUE(z) ZVAL_BOOL(z, 1)//布尔值FALSE

接着我们来看一下变量的创建,复制和销毁的相关内容和操作的API

宏定义 说明
ALLOC_ZVAL(zval* pzval) 为变量分配空间
ALLOC_INIT_ZVAL(zval* pzval) 为变量分配空间,并且初始化变量为NULL类型
MAKE_STD_ZVAL(zval* pzval) 为变量分配空间,并且设置refcount__gc = 1,is_ref__gc = 0
ZVAL_COPY_VALUE(zval* dst, zval* src) 复制变量,dst的value和type分别等于src的value和type
INIT_PZVAL_COPY(zval* dst, zval*dst) 先执行ZVAL_COPY_VALUE,然后设置dst的refcount__gc = 1,is_ref__gc = 0
SEPARATE_ZVAL(zval** ppzval) 如果*ppzval的refcount__gc > 1, 则创建一个新的变量*zvalnew,用INIT_PZVAL_COPY函数来复制原变量,最后把ppzval指向新的变量
SEPARATE_ZVAL_IF_NOT_REF(zval** ppzval) 如果*ppzval不是一个引用(is_ref_gc = 0), 则执行SEPARATE_ZVAL(zval** ppzval)
SEPARATE_ZVAL_TO_MAKE_IS_REF(zval** ppzval) 如果*ppzval不是一个引用(is_ref_gc = 0), 则执行SEPARATE_ZVAL(zval** ppzval)接着执行Z_SET_ISREF_PP(zval** ppzval)设置is_ref__gc = 0
COPY_PZVAL_TO_ZVAL(zval dst, zval** src) results in dst being a copy of src without affecting the refcount of src
MAKE_COPY_ZVAL(zval** src, zval* dst) performs INIT_PZVAL_COPY, then zval_copy_ctor on the new zval
void zval_copy_ctor(zval* pzval) 把pzval的数据复制一份出来。
void zval_ptr_dtor(zval* pzval) 把pzval->refcount__gc减1,如果refcount__gc此时等于0,则销毁pzval指向的变量回收空间。
FREE_ZVAL(zval* pzval) free(pzval)回收空间
//zend.h
#define INIT_PZVAL(z) \
(z)->refcount__gc = ; \
(z)->is_ref__gc = ; #define INIT_ZVAL(z) z = zval_used_for_init; #define ALLOC_INIT_ZVAL(zp) \
ALLOC_ZVAL(zp); \
INIT_ZVAL(*zp); #define MAKE_STD_ZVAL(zv) \
ALLOC_ZVAL(zv); \
INIT_PZVAL(zv); #define PZVAL_IS_REF(z) Z_ISREF_P(z) #define ZVAL_COPY_VALUE(z, v) \
do { \
(z)->value = (v)->value; \
Z_TYPE_P(z) = Z_TYPE_P(v); \
} while () #define INIT_PZVAL_COPY(z, v) \
do { \
ZVAL_COPY_VALUE(z, v); \
Z_SET_REFCOUNT_P(z, ); \
Z_UNSET_ISREF_P(z); \
} while () #define SEPARATE_ZVAL(ppzv) \
do { \
if (Z_REFCOUNT_PP((ppzv)) > ) { \
zval *new_zv; \
Z_DELREF_PP(ppzv); \
ALLOC_ZVAL(new_zv); \
INIT_PZVAL_COPY(new_zv, *(ppzv)); \
*(ppzv) = new_zv; \
zval_copy_ctor(new_zv); \
} \
} while () #define SEPARATE_ZVAL_IF_NOT_REF(ppzv) \
if (!PZVAL_IS_REF(*ppzv)) { \
SEPARATE_ZVAL(ppzv); \
} #define SEPARATE_ZVAL_TO_MAKE_IS_REF(ppzv) \
if (!PZVAL_IS_REF(*ppzv)) { \
SEPARATE_ZVAL(ppzv); \
Z_SET_ISREF_PP((ppzv)); \
} #define COPY_PZVAL_TO_ZVAL(zv, pzv) \
(zv) = *(pzv); \
if (Z_REFCOUNT_P(pzv)>) { \
zval_copy_ctor(&(zv)); \
Z_DELREF_P((pzv)); \
} else { \
FREE_ZVAL(pzv); \
} \
INIT_PZVAL(&(zv)); #define MAKE_COPY_ZVAL(ppzv, pzv) \
INIT_PZVAL_COPY(pzv, *(ppzv)); \
zval_copy_ctor((pzv)); #define REPLACE_ZVAL_VALUE(ppzv_dest, pzv_src, copy) { \
int is_ref, refcount; \
\
SEPARATE_ZVAL_IF_NOT_REF(ppzv_dest); \
is_ref = Z_ISREF_PP(ppzv_dest); \
refcount = Z_REFCOUNT_PP(ppzv_dest); \
zval_dtor(*ppzv_dest); \
ZVAL_COPY_VALUE(*ppzv_dest, pzv_src); \
if (copy) { \
zval_copy_ctor(*ppzv_dest); \
} \
Z_SET_ISREF_TO_PP(ppzv_dest, is_ref); \
Z_SET_REFCOUNT_PP(ppzv_dest, refcount); \
} #define SEPARATE_ARG_IF_REF(varptr) \
if (PZVAL_IS_REF(varptr)) { \
zval *original_var = varptr; \
ALLOC_ZVAL(varptr); \
INIT_PZVAL_COPY(varptr, original_var); \
zval_copy_ctor(varptr); \
} else { \
Z_ADDREF_P(varptr); \
}

除了获取类型和值之外,还有一些操作跟l变量的refcount__gc和is_ref__gc相关

宏定义原型   描述
zend_uint Z_REFCOUNT(zval zv) 返回refcount__gc的值
zend_uint Z_SET_REFCOUNT(zval zv) 设置zval变量的refcount__gc并返回
zend_uint Z_ADDREF(zval zv) ++zval->refcount__gc并返回
zend_uint Z_DELREF(zval zv) --zval->refcount__gc并返回
zend_bool Z_ISREF(zval zv) 返回zval->is_ref__gc
void Z_UNSET_ISREF(zval zv) set is_ref__gc to 0
void Z_SET_ISREF(zval zv) set is_ref__gc to 1
void Z_SET_ISREF_TO(zval zv, zend_uchar to) set is_ref__gc to to

这些宏定义也同样有*_P或*_PP的版本,可以在Zend/zend.h中查看

 #define Z_REFCOUNT_PP(ppz)      Z_REFCOUNT_P(*(ppz))
#define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc)
#define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz))
#define Z_DELREF_PP(ppz) Z_DELREF_P(*(ppz))
#define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz))
#define Z_SET_ISREF_PP(ppz) Z_SET_ISREF_P(*(ppz))
#define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz))
#define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref) #define Z_REFCOUNT_P(pz) zval_refcount_p(pz)
#define Z_SET_REFCOUNT_P(pz, rc) zval_set_refcount_p(pz, rc)
#define Z_ADDREF_P(pz) zval_addref_p(pz)
#define Z_DELREF_P(pz) zval_delref_p(pz)
#define Z_ISREF_P(pz) zval_isref_p(pz)
#define Z_SET_ISREF_P(pz) zval_set_isref_p(pz)
#define Z_UNSET_ISREF_P(pz) zval_unset_isref_p(pz)
#define Z_SET_ISREF_TO_P(pz, isref) zval_set_isref_to_p(pz, isref) #define Z_REFCOUNT(z) Z_REFCOUNT_P(&(z))
#define Z_SET_REFCOUNT(z, rc) Z_SET_REFCOUNT_P(&(z), rc)
#define Z_ADDREF(z) Z_ADDREF_P(&(z))
#define Z_DELREF(z) Z_DELREF_P(&(z))
#define Z_ISREF(z) Z_ISREF_P(&(z))
#define Z_SET_ISREF(z) Z_SET_ISREF_P(&(z))
#define Z_UNSET_ISREF(z) Z_UNSET_ISREF_P(&(z))
#define Z_SET_ISREF_TO(z, isref) Z_SET_ISREF_TO_P(&(z), isref)

我们会在扩展的开发过程中,频繁用到这些zend提供的API操作,你不需要把它记下来,随着开发的进行,你将会慢慢习惯这些API的使用。我们在开发的过程中,经常还需要转换变量的类型,可以使用下面这些函数。

//把zval *pzval的变量转换其它类型
void convert_to_long(zval* pzval)//整型
void convert_to_double(zval* pzval)//浮点
void convert_to_long_base(zval* pzval, int base)//整型,base表示进制,2,8,10,16等
void convert_to_null(zval* pzval)//NULL
void convert_to_boolean(zval* pzval)//布尔
void convert_to_array(zval* pzval)//数组
void convert_to_object(zval* pzval)//对象
void convert_object_to_type(zval* pzval, convert_func_t converter)//根据回调函数转换成对象
convert_func_t functions should have the prototype (void) (zval* pzval)//接上,回调函数的原型

php扩展开发-变量的更多相关文章

  1. Chrome扩展开发之二——Chrome扩展中脚本的运行机制和通信方式

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  2. PHP扩展开发相关总结

    1.线程安全宏定义 在TSRM/TSRM.h文件中有如下定义 #define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, ...

  3. Firefox扩展开发

    Firefox扩展开发 (插件开发) Extension开发 入门教程 5步走 五步走   首先需要知道什么是"Firefox插件".这里说的"插件"只是一个通 ...

  4. Chrome扩展开发之三——Chrome扩展中的数据本地存储和下载

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  5. PHP扩展开发(1):入门

    有关PHP扩展开发的文章.博客已经很多了,比较经典的有: TIPI项目(http://www.php-internals.com/,强烈推荐) <Extending and Embedding ...

  6. PHP扩展开发-简单类扩展

    今天来学习简单类扩展开发 实现目标为如下php的类 <?php class classext(){ private $username; CONST URL="http://www.g ...

  7. 【干货】Chrome插件(扩展)开发全攻略(不点进来看看你肯定后悔)

    写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...

  8. 【干货】Chrome插件(扩展)开发全攻略

    写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...

  9. PHP扩展开发教程(总结)

    PHP是一种解释型的语言,对于用户而言,我们精心的控制内存意味着easier prototyping和更少的崩溃!当我们深入到内核之后,所有的安全防线都已经被越过,最终还是要依赖于真正有责任心的软件工 ...

随机推荐

  1. Unity Gizmos可视化辅助工具

    所有gizmo绘制需要在脚本的OnDrawGizmos或OnDrawGizmosSelected里函数完成. OnDrawGizmos在每帧调用.所有在OnDrawGizmos中渲染的gizmos都是 ...

  2. 【JavaEE】怎么设置tomcat管理员的用户名和密码

    如果我们输入错误的Tomcat管理员密码,那么就有提示如下: 2 从它的提示信息中,我们就能找到解决方法,请留意上图中标出的位置!   我们首先打开Tomcat的配置文件,具体如下:   我们进入To ...

  3. Vue provide/inject 部分源码分析 实现响应式数据更新

    provide/inject 数据响应式更新的坑及源码解析 下面是我自己曾经遇到 一个问题,直接以自己QA的形式来写吧 自问自答了,需要的同学也可以直接访问segmentfault地址 官网给出实例, ...

  4. js为页面元素添加水印

    近期有需求为页面部分区域添加上水印,通过在网上找到了js为页面添加水印的方法,后来经过自己的改进,可以实现为页面部分元素添加水印,最终效果如下图: 代码如下: function watermark(s ...

  5. Tomcat中配置JAVA_HOME

    一般来说我们在使用Tomcat时配置JAVA_HOME都是采用的系统环境变量直接配置,下面我提供一种新的配置思路. 这里我们使用的是免安装版的apache-tomcat-7.0.35,首先我们安装好j ...

  6. MySQL使用一张表的一列更新另一张表的一列

    使用MySQL中,在一张表etl_table_field_info上新增了一个字段tgt_table_en_name,该字段的值想从表etl_table_property_info的tgt_table ...

  7. jenkins代码自动部署

    jenkins是一个广泛用于持续构建的可视化web工具,持续构建说得更直白点,就是各种项目的"自动化"编译.打包.分发部署.jenkins可以很好的支持各种语言(比如:java, ...

  8. Html+css实现带图标的控件

    </pre><pre name="code" class="html"><!DOCTYPE html> <html l ...

  9. 使用SAP云平台的destination消费Internet上的OData service

    通过SAP云平台上的destination我们可以消费Internet上的OData service或者其他通过HTTP方式暴露出来的服务. 创建一个新的destination: 维护如下属性: 点击 ...

  10. Android(java)学习笔记93:为什么局部内部类只能访问外部类中的 final型的常量

    为什么匿名内部类参数必须为final类型: 1)  从程序设计语言的理论上:局部内部类(即:定义在方法中的内部类),由于本身就是在方法内部(可出现在形式参数定义处或者方法体处),因而访问方法中的局部变 ...