使用C开发PHP扩展全过程及相关细节…

来源:互联网 发布:js 打开页面 编辑:程序博客网 时间:2024/06/10 09:12
之前一篇介绍了PHP扩展环境搭建及大致项目结构流程介绍
这章将围绕PHP以及C语言变量进行介绍

C语言字符串指针及操作函数
这里主要介绍字符串操作相关,可能大家会很奇怪,为什么我介绍的都是关于变量和字符串的操作知识。事实上我们的网页服务器大部分时间都是在拼接字符串,所以我们用c扩展的时候大部分时间都是在针对内存空间内的字符串进行操作,而php扩展和php交互的时候是通过变量进行传递所以字符串操作和转换是这个里面的基本操作……

以下指针数据类型都是char类型,不同类型的指针移动指针时会移动不同长度。

实际看几个例子
char长度为一字节,16进制为0xFF     2进制为1111 1111
int为两个字节    16进制为 0xFFFF,  2进制为1111 1111 1111 1111
注意:如果在声明类型未指定 unsigned时,那么以上两个类型的 2进制第一位将作为正负值判断而不作为表示数值1代表负数,0为正数。

注意:上文提到的int并不一定是两个字节,这个长度取决于系统,在32位系统和64位系统中实际表示长度不同。所以,对这类不同系统有变长的变量用其他指针读取的时候务必用sizeof算一下他的长度。

char teststring[50];
声明一个teststring字符串,最大长度50,这个方式在程序执行完毕后会自动释放,这个方式很环保……我目前最喜欢的方式,当然这个方式会浪费一定内存空间……并且不灵活(定长)……不过好在目前这个需求是够用的……

char *ptest;
声明一个字符串指针,注意目前他指向地址未知,我们程序在指定有效空间之前不要对他进行操作,否则肯定会会导致程序崩溃,系统不稳定。

下面两步就是给指针指向一个合法分配的内存空间。

ptest=teststring;
让指针指向teststring的存储空间,这里指针变量实际存储的是这个char串的首个char的内存地址,对与c来说一个字符串在内存中是一个连续的内存空间,在读取使用字符串的时候碰到'\0'时代表这个字符串结束,如果某字符串超过分配空间大小……等着系统挂掉吧……c是不会检测溢出这类事情的……

ptest=malloc(sizeof(char)*50);
此为在运行期间动态分配一块长度为char类型长度*50的空间,这里务必注意在使用完毕后程序结束之前必须释放,否则会导致这块内存在系统重启前不能被其他程序使用,如果调用频繁会导致系统没有可用内存……。

ptest 当前指针地址

*(ptest+1)相当于teststring[1],即指向第二个字符;

(*ptest)++相当于teststring[0]++,即指向的第一个字符变量内值+1;

ptest++相当于teststring[1],这里不同的是以后代码使用ptest时他指向的地址将不是第一个字符,而是第二个,再次提示不同数据类型步进字节个数不同。

关于字符串结尾注意事项
malloc后以及teststring声明后,我们在上面操作中并没有给他赋任何数值,所以他们目前的内容是随机的,务必在对他们进行赋值后再做其他读取操作,至少要赋值'\0'在首个char内,否则很可能会导致输出50个char内存空间内数据后还会继续输出后面的内存空间内数据直到碰到'\0'。如果没有进行任何初始化动作直接进行操作的结果会很悲剧。

C一些变量操作函数,这里只是简单罗列一下具体细节自己查看下
strcat(dst,src);                   字符串拼接,将src追加在dst字符串结尾
sprintf(dst,"%ld",idx)             将int转换成显示的char,如123转换成"123"保存到dst内
sscanf("123456 ", "%s",dst);      将string转换成int,保存到dst内
memset(dst,'\0',100);              将dst内存区域长度100全部填入\0
strncpy(dst, src,len);            将src,拷贝到dst,拷贝长度len,这个不会在结尾打\0要注意

php扩展的一些函数
zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "s", &str1,&strlen);用于接收函数后参数
注意这里的s代表传入数据为字符串,后面str1为接收字符串变量,strlen为字符串长度,目前已知字符串长度超过4k会被自动截断。其他类型及相关方法,可以参考网上资料……


注意:记得在变量之间来回拷贝追加的时候记得自己判断下追加长度是否超过目前(最大空间长度-1,减1是给结尾\0用),否则会溢出……哎各种溢出。另外,注意一下这几个函数输入输出的数据类型……

PHP变量在C语言的操作
PHP内的脚本经过Zend引擎“翻译”后,变成OPCODE,Zend根据OPCODE做调用以下c方法完成变量的操作
由于内容很多,这里不一一叙述,只是列出一个网上找到的PHP数组操作与C数组操作对应函数操作对比

PHP语法          C语法(arr是zval*)         意义
$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);

c指针包装的字符串在回传给PHP的时候建议用estrdup处理下,要不回去获取的可能是未知数据。

PHP的数组在C内读取
主要有两个方式我就介绍我目前使用的方式
看代码:
// input paramter
zval *zval_cookie;//用zval接收php传给php扩展内函数的参数

// running var
HashTable*hash_cookie;                        //使用哈希表在c内保存传入数组
HashPositionhash_pointer;                     //当前哈希表指针变量
inthash_count;                                //哈希表内数据个数

//temp var
char*key;                                     //key指针
zval**data;                                   //哈希表内单条数据value的指针
zvaltmp_data;                                 //value转换后的字符串指针
inttype;                                      //key的数据类型
uintkey_len;                                  //key的长度
ulongidx;                                     //当前数组索引id

hash_cookie = Z_ARRVAL_P(zval_cookie);//将传入php数组转换成哈希表
//count cookie item
hash_count = zend_hash_num_elements(hash_cookie);//获取哈希表内数据条数

//遍历哈希表
for(
               zend_hash_internal_pointer_reset_ex(hash_cookie,&hash_pointer);//重置哈希指针位置
               zend_hash_get_current_data_ex(hash_cookie, (void**)&data,&hash_pointer)==SUCCESS;    //判断是否还有没有遍历的数据(通过获取value是否成功判定)
               zend_hash_move_forward_ex(hash_cookie,&hash_pointer)   //向下一条挪动哈希指针
       )
{
       //上面已经获取了哈希表内当前指针指向的value,这里获取key
       type =zend_hash_get_current_key_ex(hash_cookie,&key,&key_len,&idx,0,&hash_pointer);

       //注意这里convert_to_string操作会改变指针指定区域数据,所以用zval_copy_ctor另外复制了一份
       tmp_data = **data;
       zval_copy_ctor(&tmp_data);
       convert_to_string(&tmp_data);//转换成字符串

       zval_dtor(&tmp_data);//释放tmp_data
}



PHP扩展调用PHP函数,在PHP扩展调用PHP函数有点麻烦,下面给出我调通的代码,有兴趣可以直接拿去用
zval function_name;//要调用的函数名
zval *retval;//返回结果
zval **argv[2];//参数集
zval *param;//参数1
MAKE_STD_ZVAL(retval);
MAKE_STD_ZVAL(param);

ZVAL_STRING(param, xxxx, 1);//把要加密的字符串扔给param
argv[0] = &param;
argv[1] = &return_data;

ZVAL_STRING(&function_name, "parse_str",1);//将要调用的函数名称传进去
//下面的参数介绍,第三个函数名,第四个返回值,第五个参数个数,第六个参数
if (call_user_function(EG(function_table), NULL,&function_name, retval, 2, *argv) == FAILURE){
       //调用失败
       RETURN_BOOL(0);
}
else {
       //调用成功
       RETURN_ZVAL(return_data, 1, 1);
}


结果返回
宏说明RETVAL_RESOURCE(resource)设定返回值为指定的一个资源。RETVAL_BOOL(bool)设定返回值为指定的一个布尔值。RETVAL_NULL设定返回值NULLRETVAL_LONG(long)设定返回值为指定的一个长整数。RETVAL_DOUBLE(double)设定返回值为指定的一个双精度浮点数。RETVAL_STRING(string, duplicate)设定返回值为指定的一个字符串,duplicate 含义同 RETURN_STRING。RETVAL_STRINGL(string, length, duplicate)设定返回值为指定的一个定长的字符串。其余跟 RETVAL_STRING 相同。这个宏速度更快而且是二进制安全的。RETVAL_EMPTY_STRING设定返回值为空字符串。RETVAL_FALSE设定返回值为布尔值假。RETVAL_TRUE设定返回值为布尔值真。

原创粉丝点击