生成各类型zval
PHP7将变量的引用计数转移到了具体的value上,所以zval更多的是作为统一的传输格式,很多情况下只是临时性使用,比如函数调用时的传参,最终需要的数据是zval携带的zend_value,函数从zval取得zend_value后就不再关心zval了,这种就可以直接在栈上分配zval。分配完zval后需要将其设置为我们需要的类型以及设置其zend_value,PHP中定义的ZVAL_XXX()系列宏就是用来干这个的,这些宏第一个参数z均为要设置的zval的指针,后面为要设置的zend_value。

ZVAL_UNDEF(z)    zval被销毁
ZVAL_NULL(z)    zval设置为NULL
ZVAL_FALSE(z)    设置为false
ZVAL_TRUE(z)    设置为true
ZVAL_BOOL(z, b)    设置为布尔型,b为IS_TRUE、IS_FALSE,与上面两个等价
ZVAL_LONG(z, l)    设置为整形,l类型为zend_long  如:zval z; ZVAL_LONG(&z, 88);
ZVAL_DOUBLE(z, d)    设置为浮点型,d类型为double
ZVAL_STR(z, s)    设置字符串,将z的value设置为s,s类型为zend_string*,不会增加s的refcount,支持interned strings
ZVAL_NEW_STR(z, s)    同ZVAL_STR(z, s),s为普通字符串,不支持interned strings
ZVAL_STR_COPY(z, s)    将s拷贝到z的value,s类型为zend_string*,同ZVAL_STR(z,s),这里会增加s的refcount
ZVAL_ARR(z, a)    设置为数组,a类型为zend_array*
ZVAL_NEW_ARR(z)    新分配一个数组,主动分配一个zend_array
ZVAL_NEW_PERSISTENT_ARR(z)    创建持久化数组,通过malloc分配,需要手动释放
ZVAL_OBJ(z, o)    设置为对象,o类型为zend_object*
ZVAL_RES(z, r)    设置为资源,r类型为zend_resource*
ZVAL_NEW_RES(z, h, p, t)    新创建一个资源,h为资源handle,t为type,p为资源ptr指向结构
ZVAL_REF(z, r)    设置为引用,r类型为zend_reference*
ZVAL_NEW_EMPTY_REF(z)    新创建一个空引用,没有设置具体引用的value

获取zval的值及类型
zval的类型通过 Z_TYPE(zval) 、 Z_TYPE_P(zval*) 两个宏获取,这个值取的就是zval.u1.v.type ,但是设置时不要只修改这个type,而是要设置typeinfo,因为zval还有其它的标识需要设置,比如是否使用引用计数、是否可被垃圾回收、是否可被复制等等。

书写一个类似gettype()来取得变量的类型的hello_typeof():

PHP_FUNCTION(hello_typeof)
{zval *userval = NULL;if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &userval) == FAILURE) {RETURN_NULL();}switch (Z_TYPE_P(userval)) {case IS_NULL:RETVAL_STRING("NULL");break;case IS_TRUE:RETVAL_STRING("true");break;case IS_FALSE:RETVAL_STRING("false");break;case IS_LONG:RETVAL_STRING("integer");break;case IS_DOUBLE:RETVAL_STRING("double");break;case IS_STRING:RETVAL_STRING("string");break;case IS_ARRAY:RETVAL_STRING("array");break;case IS_OBJECT:RETVAL_STRING("object");break;case IS_RESOURCE:RETVAL_STRING("resource");break;default:RETVAL_STRING("unknown type");}
}

获取不同类型zval的value

Z_LVAL(zval)、Z_LVAL_P(zval_p)    返回zend_long
Z_DVAL(zval)、Z_DVAL_P(zval_p)    返回double
Z_STR(zval)、Z_STR_P(zval_p)    返回zend_string*
Z_STRVAL(zval)、Z_STRVAL_P(zval_p)    返回char*,即:zend_string->val
Z_STRLEN(zval)、Z_STRLEN_P(zval_p)    获取字符串长度
Z_STRHASH(zval)、Z_STRHASH_P(zval_p)    获取字符串的哈希值
Z_ARR(zval)、Z_ARR_P(zval_p)、Z_ARRVAL(zval)、Z_ARRVAL_P(zval_p)    返回zend_array*
Z_OBJ(zval)、Z_OBJ_P(zval_p)    返回zend_object*
Z_OBJ_HT(zval)、Z_OBJ_HT_P(zval_p)    返回对象的zend_object_handlers,即zend_object->handlers
Z_OBJ_HANDLER(zval, hf)、Z_OBJ_HANDLER_P(zv_p, hf)    获取对象各操作的handler指针,hf为write_property、read_property等,注意:这个宏取到的为只读,不要试图
修改这个值(如:Z_OBJ_HANDLER(obj, write_property) = xxx;),因为对象的handlers成员前加了const修饰符
Z_OBJCE(zval)、Z_OBJCE_P(zval_p)    返回对象的zend_class_entry*
Z_OBJPROP(zval)、Z_OBJPROP_P(zval_p)    获取对象的成员数组
Z_RES(zval)、Z_RES_P(zval_p)    返回zend_resource*
Z_RES_HANDLE(zval),Z_RES_HANDLE_P(zval_p)    返回资源handle
Z_RES_TYPE(zval)、Z_RES_TYPE_P(zval_p)    返回资源type
Z_RES_VAL(zval)、Z_RES_VAL_P(zval_p)    返回资源ptr
Z_REF(zval)、Z_REF_P(zval_p)    返回zend_reference*
Z_REFVAL(zval)、Z_REFVAL_P(zval_p)    返回引用的zval*

数组操作
数组作为运载其他变量的变量。内部实现上使用了众所周知的 HashTable .要创建将被返回PPHP的数组,最简单的方法:

向数字索引的数组增加指定类型的值

$arr = array();    => array_init(arr);    初始化一个新数组
$arr[] = NULL;     add_next_index_null(arr);
$arr[] = 42;     add_next_index_long(arr, 42);
$arr[] = true;     add_next_index_bool(arr, 1);
$arr[] = 3.14;     add_next_index_double(arr, 3.14);
$arr[] = 'foo';  add_next_index_string(arr, "foo", 1);
$arr[] = $myvar; add_next_index_zval(arr, myvar);    

向数组中指定的数字索引增加指定类型的值

$arr[0] = NULL;        add_index_null(arr, 0);
$arr[1]= 42;            add_index_long(arr, 1, 42);
$arr[2] = true;        add_index_bool(arr, 2, 1);
$arr[3] = 3.14;        add_index_double(arr, 3, 3.14);
$arr[4] = 'foo';    add_index_string(arr, 4, "foo", 1);
$arr[5] = $myvar;    add_index_zval(arr, 5, myvar);    

向关联索引的数组增加指定类型的值

$arr['abc'] = NULL;        add_assoc_null(arr, "abc");
$arr['def'] = 711;        add_assoc_long(arr, "def", 711);
$arr['ghi'] = true;        add_assoc_bool(arr, "ghi", 1);
$arr['jkl'] = 1.44;        add_assoc_double(arr, "jkl", 1.44);
$arr['mno'] = 'baz';    add_assoc_string(arr, "mno", "baz", 1);
$arr['pqr'] = $myvar;    add_assoc_zval(arr, "pqr", myvar);

字符串操作

//创建zend_string
zend_string *zend_string_init(const char *str, size_t len, int persistent);//字符串复制,只增加引用
zend_string *zend_string_copy(zend_string *s);//字符串拷贝,硬拷贝
zend_string *zend_string_dup(zend_string *s, int persistent);//将字符串按len大小重新分配,会减少s的refcount,返回新的字符串
zend_string *zend_string_realloc(zend_string *s, size_t len, int persistent);//延长字符串,与zend_string_realloc()类似,不同的是len不能小于s的长度
zend_string *zend_string_extend(zend_string *s, size_t len, int persistent);//截断字符串,与zend_string_realloc()类似,不同的是len不能大于s的长度
zend_string *zend_string_truncate(zend_string *s, size_t len, int persistent);//获取字符串refcount
uint32_t zend_string_refcount(const zend_string *s);//增加字符串refcount
uint32_t zend_string_addref(zend_string *s);//减少字符串refcount
uint32_t zend_string_delref(zend_string *s);//释放字符串,减少refcount,为0时销毁
void zend_string_release(zend_string *s);//销毁字符串,不管引用计数是否为0
void zend_string_free(zend_string *s);//比较两个字符串是否相等,区分大小写,memcmp()
zend_bool zend_string_equals(zend_string *s1, zend_string *s2);//比较两个字符串是否相等,不区分大小写
#define zend_string_equals_ci(s1, s2) \
(ZSTR_LEN(s1) == ZSTR_LEN(s2) && !zend_binary_strcasecmp(ZSTR_VAL(s1)
, ZSTR_LEN(s1), ZSTR_VAL(s2), ZSTR_LEN(s2)))
...

引用计数
在扩展中操作与PHP用户空间相关的变量时需要考虑是否需要对其引用计数进行加减,比如下面这个例子:

function test($arr){return $arr;
}
$a = array(1,2);
$b = test($a);

如果把函数test()用内部函数实现,这个函数接受了一个PHP用户空间传入的数组参数,然后又返回并赋值给了PHP用户空间的另外一个变量,这个时候就需要增加传入数组的refcount,因为这个数组由PHP用户空间分配,函数调用前refcount=1,传到内部函数时相当于赋值给了函数的参数,因此refcount增加了1变为2,这次增加在函数执行完释放参数时会减掉,等返回并赋值给$b后此时共有两个变量指向这个数组,所以内部函数需要增加refcount,增加的引用是给返回值的。test()翻译成内部函数:

PHP_FUNCTION(test)
{zval *arr;if(zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arr) == FAILURE){RETURN_FALSE;}//如果注释掉下面这句将导致core dumpedZ_TRY_ADDREF_P(arr);RETURN_ARR(Z_ARR_P(arr));
}

那么在哪些情况下需要考虑设置引用计数呢?

(1)变量赋值: 变量赋值是最常见的情况,一个用到引用计数的变量类型在初始赋值时其refcount=1,如果后面把此变量又赋值给了其他变量那么就会相应的增加其引用计数
(2)数组操作: 如果把一个变量插入数组中那么就需要增加这个变量的引用计数,如果要删除一个数组元素则要相应的减少其引用
(3)函数调用: 传参实际可以当做普通的变量赋值,将调用空间的变量赋值给被调函数空间的变量,函数返回时会销毁函数空间的变量,这时又会减掉传参的引用,这两个过程由内核完成,不需要扩展自己处理
(4)成员属性: 当把一个变量赋值给对象的成员属性时需要增加引用计数
PHP中定义了以下宏用于引用计数的操作:

//获取引用数:pz类型为zval*
#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_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_TRY_ADDREF_P(pz) do { \
if (Z_REFCOUNTED_P((pz))) { \
Z_ADDREF_P((pz)); \
} \
} while (0)#define Z_TRY_DELREF_P(pz) do { \
if (Z_REFCOUNTED_P((pz))) { \
Z_DELREF_P((pz)); \
} \
} while (0)#define Z_TRY_ADDREF(z) Z_TRY_ADDREF_P(&(z))
#define Z_TRY_DELREF(z) Z_TRY_DELREF_P(&(z))

这些宏操作类型都是zval或zval*,如果需要操作具体value的引用计数可以使用以下宏:

//直接获取zend_value的引用,可以直接通过这个宏修改value的refcount
#define GC_REFCOUNT(p) (p)->gc.refcount

另外还有几个常用的宏:

//判断zval是否用到引用计数机制
#define Z_REFCOUNTED(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_REFCO
UNTED) != 0)
#define Z_REFCOUNTED_P(zval_p) Z_REFCOUNTED(*(zval_p))
//根据zval获取value的zend_refcounted头部
#define Z_COUNTED(zval) (zval).value.counted
#define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p))

【php7扩展开发六】zval的操作相关推荐

  1. php获取字符串扩展,PHP7扩展开发之字符串处理

    标签: 本文和大家分享的主要是PHP7扩展开发中字符串的处理相关知识,希望通过本文的分享能帮助大家更好的学习php. 这次,我们来看看字符串在PHP扩展里面如何处理. 示例代码如下: $len = s ...

  2. php7扩展开发教程,Linux下PHP7扩展开发入门教程1:扩展开发流程

    本文将会基于PHP7开发一个最简单的扩展,随便取个名learn_ext,编译生成一个learn_ext.so文件,最终调用可以在php中调用learn_ext扩展中的函数来输出一个hello worl ...

  3. php7扩展开发教程,Laravel 7 扩展开发教程

    下面由Laravel入门教程栏目给大家介绍Laravel 7 扩展开发教程,希望对需要的朋友有所帮助! 步骤 1. 创建一个新项目 我更喜欢使用 Laravel 安装程序.laravel new la ...

  4. SQL2K数据库开发六之表操作创建产品表products

    1.在SQL Server企业管理器中的数据库下的"表"节点上右击鼠标,在弹出的菜单上点击"新建表". 2.在出现的表设计器中,为表中加入五个列:Product ...

  5. 【php7扩展开发一】注册一个内部函数hello world

    通过扩展可以将C语言实现的函数提供给PHP脚本使用,如同大量PHP内置函数一样,这些函数统称为内部函数(internal function),与PHP脚本中定义的用户函数不同,它们无需精力用户函数的编 ...

  6. 【php7扩展开发五】函数调用

    实际应用中,扩展可能需要调用用户自定义的函数或者其他扩展定义的内部函数,PHP提供的函数调用API的使用: ZEND_API int call_user_function(HashTable *fun ...

  7. 【php7扩展开发四】函数的参数 ,引用传参 ,返回值

    函数参数解析 之前我们定义的函数没有接收任何参数,那么扩展定义的内部函数如何读取参数呢?用户自定义函数在编译时会为每个参数创建一个zend_arg_info结构,这个结构用来记录参数的名称.是否引用传 ...

  8. 【php7扩展开发二】全局变量

    使用C语言开发程序时经常会使用全局变量进行数据存储,这就涉及前面已经介绍过的一个问题:线程安全,PHP设计了TSRM(即:线程安全资源管理器)用于解决这个问题,内核中频繁使用到的EG.CG等都是根据是 ...

  9. PHP7扩展开发(二):配置项与全局数值

    起步 Zend引擎提供了另种管理设置值(INI)的途径.现在弄个简单的,我们经常看到php.ini里有诸如 display_errors = On 这样的全局设置.假设我们需要为我们扩展定义一个值: ...

最新文章

  1. 最小费用最大流 ---- 2017icpc青岛现场赛 K Our Journey of Xian Ends (拆点控制原图点度 + 中间必经过的点设置成源点 + 起点设成汇点)
  2. Mapreduce编程1之WordCount
  3. python笔记本-如何用Python在笔记本电脑上分析100GB数据(上)
  4. C#集合--Dictionary
  5. React学习:事件绑定、组件定义、for、map循环-学习笔记
  6. 3D 立体 backface-visibility
  7. C语言九十六之实现经典的字符串反转(通过指针或下标操作)
  8. hbase动态更改行键设计_Hadoop HBase概念学习系列之优秀行键设计(十六)
  9. 查找文件夹下所有文件名字_我的电脑如何快速查找文件,分分钟钟找到你想要的文件!...
  10. 十九.激光和惯导LIO-SLAM框架学习之项目工程代码介绍---代码框架和一些文件解释
  11. cmd fsutil 命令 - 创建指定大小文件命令
  12. 机器人建模中移动关节如何建立坐标系_机器人标准DH建模法
  13. python安装计算机丢失api_Python安装后提示api-ms-win-crt-runtime-|1-1-0.dll丢失
  14. Python3 学习第十二弹: 补充something
  15. 抽象代数学习笔记三《群:对称性变换与对称性群》
  16. lisp坐标一键生成_如何利用lisp程序一次性提取CAD中点的坐标(不要点击每个点,太多了麻烦)...
  17. Computer:编程入门的简介相关的一些概念解释之详细攻略
  18. P4在table中使用ternary匹配
  19. Echarts使用二:全国地图与各省市地图联动
  20. 包装类的自动装箱,自动拆箱

热门文章

  1. Amazon Aurora:高吞吐量云原生关系数据库的设计考虑
  2. 前端面试分享:秋招总结(html和css篇)
  3. python怎么实现类似#define宏定义_Python系列学习笔记
  4. 加载gif动图_【知乎编辑技巧】GIF动图 的插入 2020.05
  5. tsl加密算法_HTTPS背后的加密算法(转)
  6. Invalid config event received: {version=0, server
  7. php实战https请求,用php发https请求
  8. java 向已存在的excel中追加数据 .
  9. WIN7如何更改计算机名称
  10. python通过connect对象连接数据库对吗_Python连接数据库学习之DB-API详解