php扩展移植,Extending and Embedding PHP-扩展和移植PHP(八)

第二章 由里到外讲变量

每种程序语言最常见的就是存取变量,PHP也不例外,虽然很多语言都要求在使用之前声明变量类型,但PHP可以虽然声明变量,存储任意类型的值。当需要存储时,会马上转为需要的类型。

由于你使用PHP的关系,可能你对此不熟悉,在本章里,我们将讲解PHP的父语言-C,在C里变量是怎样编码的,在C里变量需要严格的类型声明。

当然,给变量编码只是工作的一半,为了保存这些信息,还需要一个标签和一个容器,从用户角度,你可以把它们看作变量名和使用范围。

数据类型

在PHP里基本的数据存储单元是zval,或者叫zend值。被定义在zend.h的头文件里,是一个较小的只有四个成员的结构体,代码如下:

typedef struct _zval_struct {

zvalue_value value;

zend_uint refcount;

zend_uchar type;

zend_uchar is_ref;

} zval;

凭直觉应该很容易发现哪些类型对应结构体里的哪个成员,无符号整型对应refcount,无符号字符对应type和is_ref,成员value实际上也是一个枚举结构,定义如下:

typedef union _zvalue_value {

long lval;

double dval;

struct {

char *val;

int len;

} str;

HashTable *ht;

zend_object_value obj;

} zvalue_value;

该枚举类型能够允许zend存储不同的PHP数据类型。

在下表里是zend定义的八种数据类型

类型值

用途

IS_NULL

该类型用于一些在第一次使用前没有初始化的变量,也可以构建NULL常量,是一个特殊的non值,与false和0不同。

IS_BOOL

布尔变量只能是两种状态中的一个,true或者false.在用户空间的一些控制表达式,例如if,while,ternary,for会在判断的时候隐性的转为布尔值。

IS_LONG

整形类型在PHP里会被存储为系统的有符号long类型,32位系统,该类型的范围是-2147483658到+2147483647。有些例外的情况,当用户脚本试图存储一个超过该范围的值时会转为浮点型。(IS_DOUBLE)

IS_DOUBLE

浮点型使用系统的有符号double类型,浮点型存储的不是精确存储。一个公式确定有限的位数,这样能保证计算机存储一定范围的值,从2.225x10^ (- 308)到1.798x10^308,只使用8byte。很不增的是在十进制里存储小数不能你二进制那样干净。例如,十进制的小数0.5对应二进制的0.1,然而十进制的0.8对应的是0.1100110011循环,把二进制再转为十进制时,因为存储位数有限,就会出现偏差。再考虑十进制的1/3,也就是0.33333循环,但实际上0.33333*3并不是1,这样在计算机处理浮点数时就会出现混淆。

IS_STRING

字符串是PHP里最常用的类型,和C程序员一样。一个足够大的内存块保存所有byte和字符,字符串的指针被存储在zval里。

在PHP字符串里值得注意的是,字符串的长度总是明确的放在zval结构里,这允许字符串包含NULL而不被截断。这种方式可以让字符串类型安全的存储任何二进制数据。

注意,被分配给PHP字符串的内存大小总是尽可能小,也就是字符串长度加一。最后一个字符是结束符NULL,这样,方法里不需要二进制安全,也能在方法之间方便的传递字符串指针。

IS_ARRAY

数组是一个特殊类型,不像C里面数组是一个向量,而更像是一个复合的结构体,像HashTable一样。每个HashTable元素都保含两个相关的信息块:标签和数据。在PHP数组里,标签就是指key,数据就是key关联的zval变量。

IS_OBJECT

对象是一个多元素数据存储,进一步讲,包含方法,存取控制,一定范围的常量,还有事件勾子。作为一个扩展开发者,构建面向对象的代码,在PHP4和PHP5中一样容易。

IS_RESOURCE

一些数据类型不能简单的映射到用户空间,例如,标准输入输出的文件指针,或者一个mysql连接的句柄,不能简单的映射到标量数组。为了解决这些问题,PHP提供了一个资源类型,细节会在第九章里讲解,现在大家只要知道它存在就可以了。

在上表中IS_*被存储在zval中的type变量里,并且决定查看value变量的哪一部分。

最明显的方式根据type值决定使用zval中的哪个元素,看下面的代码:

void describe_zval(zval *foo)

{

if (foo->type == IS_NULL) {

php_printf("The variable is NULL");

} else {

php_printf("The variable is of type %d", foo->type);

}

}

上面的代码没问题,不过不是最好的。zend头文件里定义了大量zval存取的宏,作者希望在检测zval数据时使用这些宏来获取类型,这样做的目的在于当引擎有变动的时候,避免出现代码不兼容。下面是使用宏后的代码:

void describe_zval(zval *foo)

{

if (Z_TYPE_P(foo) == IS_NULL) {

php_printf("The variable is NULL");

} else {

php_printf("The variable is of type %d",

Z_TYPE_P(foo));

}

}

_P代表了指针的层级,Z_TYPE的参数代表没有指针,其实就是一个zval变量,Z_TYPE_P的参数是一个zval变量的指针,Z_TYPE_PP的参数是一个指针的指针。

注意,上面代码里使用了php_printf方法,该方法和终端输入输出的printf方法完全一致。然而它对WEB服务器是有优势的,使用了PHP的输出缓冲。在第五章你会学习更多关于这个方法以及对应的PHPWRITE方法。

数据值

相应类型,zval值的检测也需要三个宏,以Z开头,以可选的_P或者_PP结束。

对于简单的标题类型,布尔(boolean),长整形(long)和浮点型(double),宏都比较短,并且看起来差不多。

BVAL  LVAL  DVALvoid display_values(zval boolzv, zval *longpzv,

zval **doubleppzv)

{

if (Z_TYPE(boolzv) == IS_BOOL) {

php_printf("The value of the boolean is: %s\n",

Z_BVAL(boolzv) ? "true" : "false");

}

if (Z_TYPE_P(longpzv) == IS_LONG) {

php_printf("The value of the long is: %ld\n",

Z_LVAL_P(longpzv));

}

if (Z_TYPE_PP(doubleppzv) == IS_DOUBLE) {

php_printf("The value of the double is: %f\n",

Z_DVAL_PP(doubleppzv));

}

}

字符串变量,因为字符串变量有两个属性,字符串值和长度,所以需要一对宏来处理。

void display_string(zval *zstr)

{

if (Z_TYPE_P(zstr) != IS_STRING) {

php_printf("The wrong datatype was passed!\n");

return;

}

PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr));

}

数组是用HashTable存储的,可以使用ARRVAL的三个宏来存取,Z_ARRVAL(zv),Z_ARRVAL_P(pzv),Z_ARRVAL_PP(ppzv),在旧的引擎代码里,你也许会找到HASH_OF()宏,它需要一个zval*指针,和Z_ARRVAL_P宏类似,在新代码里不应该在使用了。

对象是一个复杂的结构体,有一系列的存取宏:OBJ_HANDLE返回句柄标识,OBJ_HT返回句柄表,OBJCE返回类的标识,OBJPROP返回属性的HashTable,OBJ_HANDLER用来操作一个在OBJ_HT表里的特殊的方法。不用担心这些宏的意义,会在第十,第十一章里讲解。

在zval里,一个资源类型被做为一个整形存储,使用RESVAL的三个宏,一个整数传递给zend_fetch_resource()方法,该方法查找注册的资源,资源类型在第9章里讲解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值