2.PHP 扩展开始以及内核应用(1) --- PHP 变量在内核中的实现
2.PHP 变量在内核中的实现
1.变量的类型
php 在内核中是通过 zval 这个结构体来存储变量的,它定义在 Zend/zend.h 文件里。
struct _zval_struct {
/* Variable information */
//存储变量的值
zvalue_value value; /* value */ //变量的值
zend_uint refcount__gc; //表示引用计数
//变量具体的类型
zend_uchar type; /* active type */
zend_uchar is_ref__gc;//表示是否为引用
};
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; //数组 /* hash table value */
zend_object_value obj;//对象
zend_ast *ast;
} zvalue_value;
php 内核以后可能会改变变量的实现方式,所以检测 type 的方法可能以后不能用了。为了解决这个兼容问题,zend 头文件中定义了大量的红,供
我们检测,操作变量的使用。
Z_TYPE_P();
以一个P 结尾的宏参数大多数是 zval 型变量。此外获取变量的类型的宏还有2个,分别是 Z_TYPE 和 Z_TYPE_PP ,前者的参数是 zval型,后者的
参数是 *zval。
2.PHP 变量在内核中的实现
1.变量的类型
php 在内核中是通过 zval 这个结构体来存储变量的,它定义在 Zend/zend.h 文件里。
struct _zval_struct {
/* Variable information */
//存储变量的值
zvalue_value value; /* value */ //变量的值
zend_uint refcount__gc; //表示引用计数
//变量具体的类型
zend_uchar type; /* active type */
zend_uchar is_ref__gc;//表示是否为引用
};
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; //数组 /* hash table value */
zend_object_value obj;//对象
zend_ast *ast;
} zvalue_value;
php 内核以后可能会改变变量的实现方式,所以检测 type 的方法可能以后不能用了。为了解决这个兼容问题,zend 头文件中定义了大量的红,供
我们检测,操作变量的使用。
Z_TYPE_P();
以一个P 结尾的宏参数大多数是 zval 型变量。此外获取变量的类型的宏还有2个,分别是 Z_TYPE 和 Z_TYPE_PP ,前者的参数是 zval型,后者的
参数是 *zval。
2.变量的值
php 内核提供了3个基础宏来方便我们对变量的值进行操作,这几个宏同样以 Z_ 开头,并且 P 结尾和 PP 结尾,分别代表这参数是指针还是指向指针的指针。
IS_BOOL 型的 BVAL 组合: Z_BVAL, Z_BVAL_P, Z_BVAL_PP
IS_DOUBLE 的 DVAL 组合: Z_DVAL, Z_DVAL_P, ZDVAL_PP
string 型变量比较特殊,因为内核在保存 string 变量时,不仅保存了字符串的值,还保存了它的长度,所以它对应的两种宏组合 STRVAL 和 STRLEN,即:
Z_STRVAL, Z_STRVAL_P, Z_STRVAL_PP 与 Z_STRLEN, Z_STRLEN_P,Z_STRLEN_PP 。前一种宏返回 char* 型,即字符串地址;后一种返回的是 int 型,
即字符串的长度.
array 型变量的值其实是存储在 C 语言实现的 HashTable 中的,我们可以用 ARRVAL 组合宏(Z_ARRVAL,Z_ARRVAL_P,Z_ARRVAL_PP)。
对象是一个复杂的结构体(zend_object_value 结构体),不仅存储属性的定义,属性的值,还存储着访问权限,方法等信息。内核中定义了以下组合宏让我们方便
操作对象:
OBJ_HANDLE: 返回handle 标识符
OBJ_HT: handle 表
OBJCE:类定义
OBJPROP:HashTable 的属性
OBJ_HANDLER:在 OBJ_HT 中操作一个特殊的 handle 方法。
资源类型变量的值其实就是一个整数,可以用 RESVAL 组合宏来访问它,我们把它的值传给 zend_fetch_resource 函数,便可以得到这个资源的操作句柄,如
mysql 的连接句柄等。
3.创建 PHP 变量
创建一个 zval,最容易想到的就是创建一个 zval 指针,然后申请一块内存并让指针指向它。不要用 malloc(sizeof(zval))。内核给我们提供了相应的宏来处理,
理由和以前一样:为了代码漂亮并保持版本升级时的兼容性。这个宏是 MAKE_STD_ZVAL(pzv)。这个宏会用内核的方式申请一块内存并将地址赋给 pzv,并初始化它的
refcount__gc 和 is_ref__gc 两个属性. 更棒的是,它不但会自动处理内存不足的问题,还会在内存中选个最优的位置来申请。
除了 MAKE_STD_ZVAL() 宏函数,ALLOC_INIT_ZVAL() 宏函数也是用来干这件事的,唯一的不同是它会将 ptv 指的 zval 的类型设置为 IS_NULL。
ZVAL_NULL(pvz); // Z_TYPE_P(pzv) = IS_NULL;
ZVAL_BOOL(pzv,b); // Z_TYPE_P(pzv) = IS_BOOL; Z_BVAL_P(pzv) = b?1:0;
ZVAL_TRUE(pzv); // ZVAL_BOOL(pzv,1)
ZVAL_FALSE(pzv); // ZVAL_BOOL(pzv,0);
ZVAL_LONG(pzv,l); // Z_TYPE_P(pzv) = IS_LONG; Z_LVAL_P(pzv) = l;
ZVAL_DOUBLE(pzv,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(zv)=len; if (dup) { Z_STRVAL_P(pvz)=estndup(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 参数:
str,len这2个参数很好理解,因为我们知道内存中保存了字符串的地址和长度,后面的 dup 的意思很简单,它指明了该字符串是否需要被复制。值为1将先申请一块新内存,
并复制该字符串,然后把新内存的地址复制给 pzv,为 0 时则是直接把 str 地址赋值给 zval。
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 。
1.变量的类型
2.变量的值
3.创建 PHP 变量