php扩展 c,传参,传数组,zvar类型,全局变量

接收参数
在原来的hello 扩展上面继续

修改php_hello.h,添加下面函数原型
PHP_FUNCTION(hello_greetme); 

修改hello.c
PHP_FE(hello_bool, NULL)
PHP_FE(hello_null, NULL)
PHP_FE(hello_greetme, NULL)
{NULL, NULL, NULL}
};
在hello.c最后面加上 函数 hello_greetme 实现部分
PHP_FUNCTION(hello_greetme)
{
char *name;
int name_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
RETURN_NULL();
}

php_printf("Hello %s ", name);

RETURN_TRUE;
}

zend_parse_parameters函数是取参数的主要函数
ZEND_NUM_ARGS() TSRMLS_CC 不怎么明白
"s" 表示接收一个 string 参数
第三个参数,表示把接收到的这个string型参数赋值给name,是个引用
第四个参数是返回 string的长度 beta

下面是第二个参数的详细表格
php变量 c变量
Boolean b zend_bool 
Long l long 
Double d double 
String s char*, int 
Resource r zval* 
Array a zval* 
Object o zval* 
zval z zval* 

zval结构
struct {
union {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} value;
zend_uint refcount;
zend_uchar type;
zend_uchar is_ref;
} zval;

一段小程序,帮助理解
PHP_FUNCTION(hello_dump)
{
zval *uservar;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", uservar) == FAILURE) {
RETURN_NULL();
}

switch (Z_TYPE_P(uservar)) {
case IS_NULL:
php_printf("NULL ");
break;
case IS_BOOL:
php_printf("Boolean: %s ", Z_LVAL_P(uservar) ? "TRUE" : "FALSE");
break;
case IS_LONG:
php_printf("Long: %ld ", Z_LVAL_P(uservar));
break;
case IS_DOUBLE:
php_printf("Double: %f ", Z_DVAL_P(uservar));
break;
case IS_STRING:
php_printf("String: ");
PHPWRITE(Z_STRVAL_P(uservar), Z_STRLEN_P(uservar));
php_printf(" ");
break;
case IS_RESOURCE:
php_printf("Resource ");
break;
case IS_ARRAY:
php_printf("Array ");
break;
case IS_OBJECT:
php_printf("Object ");
break;
default:
php_printf("Unknown ");
}

RETURN_TRUE;



下面函数是获取 zval的 类型
Z_TYPE(zval), 
Z_TYPE_P(zval*), 
Z_TYPE_PP(zval**).

convert_to_*() 把 zval变量转化成相应类型

Z_*_P() 宏用来设置type和值
zval_ptr_dtor()释放内存

{
zval *temp;

ALLOC_INIT_ZVAL(temp);

Z_TYPE_P(temp) = IS_LONG;
Z_LVAL_P(temp) = 1234;

zval_ptr_dtor(&temp);
}

使用更加简单的的方式
ZVAL_LONG(temp, 1234); 

下面5种写法 达到的效果都是一样的
RETURN_LONG(42);

RETVAL_LONG(42);
return;

ZVAL_LONG(return_value, 42);
return;

Z_TYPE_P(return_value) = IS_LONG;
Z_LVAL_P(return_value) = 42;
return;

return_value->type = IS_LONG;
return_value->value.lval = 42;
return;

实际上return_value是每个PHP_FUNCTION中的一个参数,默认是null

zend_hash类似 函数是从一个数组中读出值
PHP_FUNCTION(hello_array_strings)
{
zval *arr, **data;
HashTable *arr_hash;
HashPosition pointer;
int array_count;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
RETURN_NULL();
}

arr_hash = Z_ARRVAL_P(arr);
array_count = zend_hash_num_elements(arr_hash);

php_printf("The array passed contains %d elements ", array_count);

for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); 
zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; 
zend_hash_move_forward_ex(arr_hash, &pointer)) {
convert_to_string_ex(data);
PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data));
php_printf(" ");
}
RETURN_TRUE;
}
上面就要特别注意这个函数了
convert_to_string_ex 他会改变原来的值,效果等同于 set_type()
所以要特殊处理一下for循环
for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); 
zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; 
zend_hash_move_forward_ex(arr_hash, &pointer)) {
zval temp;
temp = **data;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf(" ");
zval_dtor(&temp);
}

上面还要注意的是
temp = **data;这个仅仅是拷贝了data的值部分,因为data是zval类型,里面还有其他的一些数据,比如 char* strings, or 
HashTable* arrays
这里就需要使用 zval_copy_ctor(&temp); 来完整的拷贝
用 zval_dtor(&temp); 释放分配的内存

遍历数组
for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); 
zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; 
zend_hash_move_forward_ex(arr_hash, &pointer)) {
zval temp;
char *key;
int key_len;
long index;

if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) {
PHPWRITE(key, key_len);
} else {
php_printf("%ld", index);
}

php_printf(" => ");

temp = **data;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf(" ");
zval_dtor(&temp);
}

用函数 zend_hash_get_current_key_ex() 获取当前数组的索引,
可能是下面一种
HASH_KEY_IS_STRING
HASH_KEY_IS_LONG
HASH_KEY_NON_EXISTANT

下面也是一种遍历数组的方式
static int php_hello_array_walk(zval **element TSRMLS_DC)
{
zval temp;

temp = **element;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf(" ");
zval_dtor(&temp);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_arg(zval **element, char *greeting TSRMLS_DC)
{
php_printf("%s", greeting);
php_hello_array_walk(element TSRMLS_CC);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_args(zval **element, int num_args, va_list args, zend_hash_key *hash_key)
{
char *prefix = va_arg(args, char*);
char *suffix = va_arg(args, char*);
TSRMLS_FETCH();

php_printf("%s", prefix);
php_hello_array_walk(element TSRMLS_CC);
php_printf("%s ", suffix);

return ZEND_HASH_APPLY_KEEP;
}

PHP_FUNCTION(hello_array_walk)
{
zval *zarray;
int print_newline = 1;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zarray) == FAILURE) {
RETURN_NULL();
}

zend_hash_apply(Z_ARRVAL_P(zarray), (apply_func_t)php_hello_array_walk TSRMLS_CC);
zend_hash_apply_with_argument(Z_ARRVAL_P(zarray), (apply_func_arg_t)php_hello_array_walk_arg, "Hello " TSRMLS_CC);
zend_hash_apply_with_arguments(Z_ARRVAL_P(zarray), (apply_func_args_t)php_hello_array_walk_args, 2, "Hello ", "Welcome to my extension!");

RETURN_TRUE;
}

zend_hash_apply 这个函数的第二个参数很像回调函数,比如
php_hello_array_walk 举个例子
假如数组 zarray 有两个元素, php_hello_array_walk就执行两次, php_hello_array_walk里现在的返回值是 
ZEND_HASH_APPLY_KEEP,也可以返回 ZEND_HASH_APPLY_STOP,和 ZEND_HASH_APPLY_REMOVE,分别表示删除这个数组元素
和停止循环.


下面是一个从php的 全局变量中找到只的扩展函数
EG就是保存全局变量的散列表

PHP_FUNCTION(hello_get_global_var)
{
char *varname;
int varname_len;
zval **varvalue;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &varname, &varname_len) == FAILURE) {
RETURN_NULL();
}

if (zend_hash_find(&EG(symbol_table), varname, varname_len + 1, (void**)&varvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined variable: %s", varname);
RETURN_NULL();
}

*return_value = **varvalue;
zval_copy_ctor(return_value);
}

这个函数是报错函数,这里会发出一个notice
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined variable: %s", varname);


向全局变量散列表中增加一个变量
PHP_FUNCTION(hello_set_local_var)
{
zval *newvar;
char *varname;
int varname_len;
zval *value;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &varname, &varname_len, &value) == FAILURE) {
RETURN_NULL();
}

ALLOC_INIT_ZVAL(newvar);
*newvar = *value;
zval_copy_ctor(newvar);
zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, &newvar, sizeof(zval*), NULL);

RETURN_TRUE;

}





最后几个文件的代码

config.m4

PHP_ARG_ENABLE(hello, [whether to enable Hello World support],
[ --enable-hello Enable Hello World support])

if test "$PHP_HELLO" = "yes"; then
AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

php_hello.h

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1

#ifdef ZTS
#include "TSRM.h"
#endif

ZEND_BEGIN_MODULE_GLOBALS(hello)
long counter;
zend_bool direction;
ZEND_END_MODULE_GLOBALS(hello)

#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
PHP_FUNCTION(hello_greetme);
PHP_FUNCTION(hello_add);
PHP_FUNCTION(hello_dump);
PHP_FUNCTION(hello_array);
PHP_FUNCTION(hello_array_strings);
PHP_FUNCTION(hello_array_walk);
PHP_FUNCTION(hello_array_value);
PHP_FUNCTION(hello_get_global_var);
PHP_FUNCTION(hello_set_local_var);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif


hello.c


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_hello.h"

ZEND_DECLARE_MODULE_GLOBALS(hello)

static function_entry hello_functions[] = {
PHP_FE(hello_world, NULL)
PHP_FE(hello_long, NULL)
PHP_FE(hello_double, NULL)
PHP_FE(hello_bool, NULL)
PHP_FE(hello_null, NULL)
PHP_FE(hello_greetme, NULL)
PHP_FE(hello_add, NULL)
PHP_FE(hello_dump, NULL)
PHP_FE(hello_array, NULL)
PHP_FE(hello_array_strings, NULL)
PHP_FE(hello_array_walk, NULL)
PHP_FE(hello_array_value, NULL)
PHP_FE(hello_get_global_var, NULL)
PHP_FE(hello_set_local_var, NULL)
{NULL, NULL, NULL}
};

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_HELLO_WORLD_EXTNAME,
hello_functions,
PHP_MINIT(hello),
PHP_MSHUTDOWN(hello),
PHP_RINIT(hello),
NULL,
NULL,
#if ZEND_MODULE_API_NO >= 20010901
PHP_HELLO_WORLD_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

PHP_INI_BEGIN()
PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
hello_globals->direction = 1;
}

PHP_RINIT_FUNCTION(hello)
{
HELLO_G(counter) = 0;

return SUCCESS;
}

PHP_MINIT_FUNCTION(hello)
{
ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);

REGISTER_INI_ENTRIES();

return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
UNREGISTER_INI_ENTRIES();

return SUCCESS;
}

PHP_FUNCTION(hello_world)
{
RETURN_STRING("Hello World", 1);
}

PHP_FUNCTION(hello_long)
{
if (HELLO_G(direction)) {
HELLO_G(counter)++;
} else {
HELLO_G(counter)--;
}

RETURN_LONG(HELLO_G(counter));
}

PHP_FUNCTION(hello_double)
{
RETURN_DOUBLE(3.1415926535);
}

PHP_FUNCTION(hello_bool)
{
RETURN_BOOL(1);
}

PHP_FUNCTION(hello_null)
{
RETURN_NULL();
}

PHP_FUNCTION(hello_greetme)
{
zval *zname;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zname) == FAILURE) {
RETURN_NULL();
}

convert_to_string(zname);

php_printf("Hello ");
PHPWRITE(Z_STRVAL_P(zname), Z_STRLEN_P(zname));
php_printf(" ");

RETURN_TRUE;
}

PHP_FUNCTION(hello_add)
{
long a;
double b;
zend_bool return_long = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ld|b", &a, &b, &return_long) == FAILURE) {
RETURN_NULL();
}

if (return_long) {
RETURN_LONG(a + b);
} else {
RETURN_DOUBLE(a + b);
}
}

PHP_FUNCTION(hello_dump)
{
zval *uservar;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &uservar) == FAILURE) {
RETURN_NULL();
}

switch (Z_TYPE_P(uservar)) {
case IS_NULL:
php_printf("NULL ");
break;
case IS_BOOL:
php_printf("Boolean: %s ", Z_LVAL_P(uservar) ? "TRUE" : "FALSE");
break;
case IS_LONG:
php_printf("Long: %ld ", Z_LVAL_P(uservar));
break;
case IS_DOUBLE:
php_printf("Double: %f ", Z_DVAL_P(uservar));
break;
case IS_STRING:
php_printf("String: ");
PHPWRITE(Z_STRVAL_P(uservar), Z_STRLEN_P(uservar));
php_printf(" ");
break;
case IS_RESOURCE:
php_printf("Resource ");
break;
case IS_ARRAY:
php_printf("Array ");
break;
case IS_OBJECT:
php_printf("Object ");
break;
default:
php_printf("Unknown ");
}

RETURN_TRUE;
}

PHP_FUNCTION(hello_array)
{
char *mystr;
zval *mysubarray;

array_init(return_value);

add_index_long(return_value, 42, 123);

add_next_index_string(return_value, "I should now be found at index 43", 1);

add_next_index_stringl(return_value, "I'm at 44!", 10, 1);

mystr = estrdup("Forty Five");
add_next_index_string(return_value, mystr, 0);

add_assoc_double(return_value, "pi", 3.1415926535);

ALLOC_INIT_ZVAL(mysubarray);
array_init(mysubarray);
add_next_index_string(mysubarray, "hello", 1);
php_printf("mysubarray->refcount = %d ", mysubarray->refcount);
mysubarray->refcount = 2;
php_printf("mysubarray->refcount = %d ", mysubarray->refcount);
add_assoc_zval(return_value, "subarray", mysubarray);

php_printf("mysubarray->refcount = %d ", mysubarray->refcount);
}

PHP_FUNCTION(hello_array_strings)
{
zval *arr, **data;
HashTable *arr_hash;
HashPosition pointer;
int array_count;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
RETURN_NULL();
}

arr_hash = Z_ARRVAL_P(arr);
array_count = zend_hash_num_elements(arr_hash);

php_printf("The array passed contains %d elements ", array_count);

for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {

zval temp;
char *key;
int key_len;
long index;

if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) {
PHPWRITE(key, key_len);
} else {
php_printf("%ld", index);
}

php_printf(" => ");

temp = **data;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf(" ");
zval_dtor(&temp);
}

RETURN_TRUE;
}

static int php_hello_array_walk(zval **element TSRMLS_DC)
{
zval temp;

temp = **element;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf(" ");
zval_dtor(&temp);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_arg(zval **element, char *greeting TSRMLS_DC)
{
php_printf("%s", greeting);
php_hello_array_walk(element TSRMLS_CC);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_args(zval **element, int num_args, va_list args, zend_hash_key *hash_key)
{
char *prefix = va_arg(args, char*);
char *suffix = va_arg(args, char*);
TSRMLS_FETCH();

php_printf("%s", prefix);
php_hello_array_walk(element TSRMLS_CC);
php_printf("%s ", suffix);

return ZEND_HASH_APPLY_KEEP;
}

PHP_FUNCTION(hello_array_walk)
{
zval *zarray;
int print_newline = 1;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zarray) == FAILURE) {
RETURN_NULL();
}

zend_hash_apply(Z_ARRVAL_P(zarray), (apply_func_t)php_hello_array_walk TSRMLS_CC);
zend_hash_internal_pointer_reset(Z_ARRVAL_P(zarray));
zend_hash_apply_with_argument(Z_ARRVAL_P(zarray), (apply_func_arg_t)php_hello_array_walk_arg, "Hello " TSRMLS_CC);
zend_hash_apply_with_arguments(Z_ARRVAL_P(zarray), (apply_func_args_t)php_hello_array_walk_args, 2, "Hello ", "Welcome to my extension!");

RETURN_TRUE;
}

PHP_FUNCTION(hello_array_value)
{
zval *zarray, *zoffset, **zvalue;
long index = 0;
char *key = NULL;
int key_len = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &zarray, &zoffset) == FAILURE) {
RETURN_NULL();
}

switch (Z_TYPE_P(zoffset)) {
case IS_NULL:
index = 0;
break;
case IS_DOUBLE:
index = (long)Z_DVAL_P(zoffset);
break;
case IS_BOOL:
case IS_LONG:
case IS_RESOURCE:
index = Z_LVAL_P(zoffset);
break;
case IS_STRING:
key = Z_STRVAL_P(zoffset);
key_len = Z_STRLEN_P(zoffset);
break;
case IS_ARRAY:
key = "Array";
key_len = sizeof("Array") - 1;
break;
case IS_OBJECT:
key = "Object";
key_len = sizeof("Object") - 1;
break;
default:
key = "Unknown";
key_len = sizeof("Unknown") - 1;
}

if (key && zend_hash_find(Z_ARRVAL_P(zarray), key, key_len + 1, (void**)&zvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined index: %s", key);
RETURN_NULL();
} else if (!key && zend_hash_index_find(Z_ARRVAL_P(zarray), index, (void**)&zvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined index: %ld", index);
RETURN_NULL();
}

*return_value = **zvalue;
zval_copy_ctor(return_value);
}

PHP_FUNCTION(hello_get_global_var)
{
char *varname;
int varname_len;
zval **varvalue;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &varname, &varname_len) == FAILURE) {
RETURN_NULL();
}

if (zend_hash_find(&EG(symbol_table), varname, varname_len + 1, (void**)&varvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined variable: %s", varname);
RETURN_NULL();
}

*return_value = **varvalue;
zval_copy_ctor(return_value);
}

PHP_FUNCTION(hello_set_local_var)
{
zval *newvar;
char *varname;
int varname_len;
zval *value;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &varname, &varname_len, &value) == FAILURE) {
RETURN_NULL();
}

ALLOC_INIT_ZVAL(newvar);
*newvar = *value;
zval_copy_ctor(newvar);

zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, &newvar, sizeof(zval*), NULL);

RETURN_TRUE;
}

posted @ 2011-05-04 09:07  hellovigoss  阅读(1787)  评论(0编辑  收藏  举报