您的位置:首页 > 编程语言 > PHP开发

PHP扩展中返回数组源码分析

2014-07-09 18:24 501 查看
最近在公司的工作时负责实现一些PHP扩展。在这些扩展中,经常需要返回数组。我都是这么操作的:

array_init(return_value);

zval* arr;

ALLOC_INIT_ZVAL(arr);

array_init(arr);

add_assoc_long(arr,"num",121);

add_assoc_string(arr,"string",”hello world!”,1);

add_assoc_zval(return_value,"array",arr);

虽然会用,但其实心里一直没底,不知道上面的这个用法是否会出错。

通过今天查看php 5.3的源码,终于知道了,上面的用法是安全的。

要想弄清楚上面的代码到底都干了什么,需要弄明白3个函数——array_init()、ALLOC_INIT_ZVAL和add_assoc_xxx(其实这个有一簇类似的函数,具体的请参考源码zend_API.h文件)。

1. array_init()函数分析

该函数位于zend_API.h中,定义如下:

#define array_init(arg)			_array_init((arg), 0 ZEND_FILE_LINE_CC)


_array_init()函数定义位于zend_API.c中,负责将arg初始化为一个数组类型的zval,并将arg的类型赋值为IS_ARRAY,具体如下:

ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC)
{
	ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));

	_zend_hash_init(Z_ARRVAL_P(arg), size, NULL, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
	Z_TYPE_P(arg) = IS_ARRAY;
	return SUCCESS;
}


_zend_hash_init()函数位于Zend_hash.c中,完成对arg的初始化,其中需要注意的是对persistent这个成员的初始化(该参数决定了是调用操作系统的内存函数还是调用ZendMM来给自己分配内存)。

因为在_array_init()函数中传给_zend_hash_init()函数的persistent参数为0,所以内存分配方式为“非持久性”。

小结:array_init()函数负责将一个zval类型的变量初始化为数组类型,并设置其相关的内存分配方式为“非持久性”。

2. ALLOC_INIT_ZVAL宏分析

ALLOC_INIT_ZVAL负责申请一块空间给zval,然后对其进行初始化,具体定义如下:

#define ALLOC_INIT_ZVAL(zp)						\
	ALLOC_ZVAL(zp);		\
	INIT_ZVAL(*zp);


ALLOC_ZVAL宏位于Zend_gc.h文件中,调用emalloc申请一块空间给zval,并为PHP垃圾回收机制(详细的请参考《深入理解PHP内核》第六章第四节(http://www.php-internals.com/))对该空间做一些初始化,具体代码如下:

#define ALLOC_ZVAL(z) 									\
	do {												\
		(z) = (zval*)emalloc(sizeof(zval_gc_info));		\
		GC_ZVAL_INIT(z);								\
	} while (0)


INIT_ZVAL宏位于Zend.h中,主要是对zval结构做一些初始化,具体如下:

#define INIT_ZVAL(z) z = zval_used_for_init;


3. add_assoc_xxx簇函数分析(以add_assoc_long为例)

add_assoc_long()函数位于zend_API.h中,将一个long值添加到数组中,代码如下:

<pre name="code" class="cpp">#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key)+1, __n)




add_assoc_long_ex()函数位于zend_API.c中,负责将long值转换为PHP中的变量存储结构zval,然后将这个zval添加到数组中。具体代码如下:

ZEND_API int add_assoc_long_ex(zval *arg, const char *key, uint key_len, long n) /* {{{ */
{
	zval *tmp;

	MAKE_STD_ZVAL(tmp);
	ZVAL_LONG(tmp, n);

	return zend_symtable_update(Z_ARRVAL_P(arg), key, key_len, (void *) &tmp, sizeof(zval *), NULL);
}


MAKE_STD_ZVAL宏位于zend.h中,负责申请一块zval空间并初始化,具体代码如下:

#define MAKE_STD_ZVAL(zv)				 \
	ALLOC_ZVAL(zv); \
	INIT_PZVAL(zv);


ALLOC_ZVAL的分析上面已经说过了,这里看一下INIT_PZVAL,这个宏也是对zval结构做一些初始化,代码如下:

<pre name="code" class="cpp">#define INIT_PZVAL(z)		\
	(z)->refcount__gc = 1;	\
	(z)->is_ref__gc = 0;




总结:经过上述3个函数的分析,发现它们所使用的内存都是利用ZendMM机制申请得来的,而且都是“非持久性”内存,所以我们就可以很放心了。因为这些操作所申请的内存会在每次请求后被自动“释放”(其实并没有真正的释放,而是这块内存再次变得可用了)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: