2.3 创建PHP变量

我们已经知道php变量在内核中其实是通过zval结构来实现的,也初步了解如何设置一个zval结构的类型和值。这一节我们将在前两节的基础上,彻底掌握对zval结构的操控,其间将引入很多超棒的新宏。

在code的时候,很希望在内核中创建的zval可以让用户在PHP语言里以变量的形式使用,为了实现这个功能,我们首先要创建一个zval。最容易想到的办法便是创建一个zval指针,然后申请一块内存并让指针指向它。如果你脑海里浮现出了malloc(sizeof(zval))的影子,那么请你立即刹车,不要用malloc来做这件事情,内核给我们提供了相应的宏来处理这件事,理由和以前一样:为了代码漂亮并保持版本升级时的兼容性。这个宏是:MAKE_STD_ZVAL(pzv)。这个宏会用内核的方式来申请一块内存并将其地址付给pzv,并初始化它的refcount和is_ref两个属性,更棒的是,它不但会自动的处理内存不足问题,还会在内存中选个最优的位置来申请。

除了MAKE_STD_ZVAL()宏函数,ALLOC_INIT_ZVAL()宏函数也是用来干这件事的,唯一的不同便是它会将pzv所指的zval的类型设置为IS_NULL;

申请完空间后,我们便可以给这个zval赋值了。基于已经介绍的宏,也许我们需要Z_TYPE_P(p) = IS_NULL来设置其是null类型,并用Z_SOMEVAL形式的宏来为它赋值,但是现在你有了更好更短的选择!

内核中提供一些宏来简化我们的操作,可以只用一步便设置好zval的类型和值。

新宏 其它宏的实现方法
ZVALNULL(pvz); (注意这个Z和VAL之间没有下划线!) Z_TYPE_P(pzv) = IS_NULL;(IS_NULL型不用赋值,因为这个类型只有一个值就是null,^^)
ZVAL_BOOL(pzv, b); (将pzv所指的zval设置为IS_BOOL类型,值是b) Z_TYPE_P(pzv) = IS_BOOL;Z_BVAL_P(pzv) = b ? 1 : 0;
ZVAL_TRUE(pzv); (将pzv所指的zval设置为IS_BOOL类型,值是true) ZVAL_BOOL(pzv, 1);
ZVAL_FALSE(pzv); (将pzv所指的zval设置为IS_BOOL类型,值是false) ZVAL_BOOL(pzv, 0);
ZVAL_LONG(pzv, l); (将pzv所指的zval设置为IS_LONG类型,值是l) Z_TYPE_P(pzv) = IS_LONG;Z_LVAL_P(pzv) = l;
ZVAL_DOUBLE(pzv, d); (将pzv所指的zval设置为IS_DOUBLE类型,值是d) Z_TYPE_P(pzv) = IS_DOUBLE;Z_DVAL_P(pzv) = d;
ZVAL_STRINGL(pzv,str,len,dup);(下面单独解释) Z_TYPE_P(pzv) = IS_STRING;Z_STRLEN_P(pzv) = len;if (dup) {Z_STRVAL_P(pzv) =estrndup(str, len + 1);} else {Z_STRVAL_P(pzv) = str;}
ZVAL_STRING(pzv, str, dup); ZVAL_STRINGL(pzv, str,strlen(str), dup);
ZVAL_RESOURCE(pzv, res); Z_TYPE_P(pzv) = IS_RESOURCE;Z_RESVAL_P(pzv) = res;

ZVAL_STRINGL(pzv,str,len,dup)中的dup参数

先阐述一下ZVAL_STRINGL(pzv,str,len,dup);str和len两个参数很好理解,因为我们知道内核中保存了字符串的地址和它的长度,后面的dup的意思其实很简单,它指明了该字符串是否需要被复制。值为 1 将先申请一块新内存并赋值该字符串,然后把新内存的地址复制给pzv,为 0 时则是直接把str的地址赋值给zval。

《抚琴居》上的一篇文章说这项特性将会在你仅仅需要创建一个变量并将其指向一个已经由 Zend 内部数据内存时变得很有用。

ZVAL_STRINGL与ZVAL_STRING的区别

如果你想在某一位置截取该字符串或已经知道了这个字符串的长度,那么可以使用宏 ZVAL_STRINGL(zval, string, length, duplicate) ,它显式的指定字符串长度,而不是使用strlen()。这个宏该字符串长度作为参数。但它是二进制安全的,而且速度也比ZVAL_STRING快,因为少了个strlen。

ZVAL_RESOURCE约等于ZVAL_LONG

上一节中我们说过PHP中的资源类型的值其实就是一个整数,所以ZVAL_RESOURCE和ZVAL_LONG的工作差不多,只不过它会把zval的类型设置为 IS_RESOURCE。