PHP7/5扩展开发函数手册(4) - 数组

本文介绍了PHP扩展开发中与数组相关的操作,包括数组初始化、添加数据、获取HashTable、查找元素、创建及销毁HashTable等,并提供了PHP5和PHP7的差异示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

  • String 的两个宏定义
  • 初始化数组 array_init
  • 添加数组数据 add_assoc_null
  • 获取数组中HashTable
  • 根据 key 获取数组的 value
  • 创建一个HashTable
  • 获取HashTable元素数量
  • 获取HashTable元素的最小/最大值
  • HashTable遍历操作
  • 获取指定HashPosition位置处的值
  • HashTable数据更新
  • HashTable的销毁
  • HashTable 遍历修改操作示例

String 的两个宏定义

在Zend 内核中,针对字符串,定义了两个宏, 这个在后续的字符串处理中经常需要用到,大家留意

----zend_portability.h----

#define ZEND_STRL(str) (str), (sizeof(str)-1)

#define ZEND_STRS(str) (str), (sizeof(str))

初始化数组

int array_init(zval *arg);

在初始化数组之前, 你需要首先为 zval 分配内存。如下示例:

----php7_wrapper.h----
#if PHP_MAJOR_VERSION < 7 /* PHP Version 5*/
	#define SW_MAKE_STD_ZVAL(p)               MAKE_STD_ZVAL(p)
	#define SW_ALLOC_INIT_ZVAL(p)             ALLOC_INIT_ZVAL(p)
	#define sw_zval_ptr_dtor(p)	          zval_ptr_dtor(*p) //zval销毁

#else /* PHP Version 7 */
        //栈上分配空间
	#define SW_MAKE_STD_ZVAL(p)             zval _stack_zval_##p; p = &(_stack_zval_##p)
	#define SW_ALLOC_INIT_ZVAL(p)           do{p = (zval *)emalloc(sizeof(zval)); bzero(p, sizeof(zval));}while(0)
	#define sw_zval_ptr_dtor(p)	        zval_ptr_dtor(*p) //zval销毁

#endif

----swoole_server.c----

static PHP_METHOD(swoole_http_client, __construct)
{
	...
	
    //SW_MAKE_STD_ZVAL分配的zval内存必须在使用后手动释放
    zval *headers;
    SW_MAKE_STD_ZVAL(headers);
    array_init(headers); //初始化为数组
    zend_update_property(swoole_http_client_class_entry_ptr, getThis(), ZEND_STRL("headers"), headers TSRMLS_CC);
    sw_zval_ptr_dtor(&headers);
    
    ...
    
    //SW_ALLOC_INIT_ZVAL 分配的 zval 内存由 php 回收
    zval *ports;
    SW_ALLOC_INIT_ZVAL(ports);
    array_init(ports); //初始化为数组
    zend_update_property(swoole_http_client_class_entry_ptr, getThis(), ZEND_STRL("ports"), ports TSRMLS_CC);
    
    ...
}

添加数组数据

//按特定的字符串 key 添加数据
int add_assoc_null(zval *arg, char *key);
int add_assoc_bool(zval *arg, char *key, int val);
int add_assoc_long(zval *arg, char *key, long val);
int add_assoc_double(zval *arg, char *key, double val);
int add_assoc_resource(zval *arg, char *key, int val);
int add_assoc_string(zval *arg, char *key, char *val, int dup);
int add_assoc_stringl(zval *arg, char *key, char *val, uint len, int dup);
int add_assoc_zval(zval *arg, char *key, zval *val);

//按数组索引下标 index 添加数据
int add_index_null(zval *arg, ulong idx);
int add_index_bool(zval *arg, ulong idx, int val);
int add_index_long(zval *arg, ulong idx, long val);
int add_index_resource(zval *arg, ulong idx, int val);
int add_index_double(zval *arg, ulong idx, double val);
int add_index_string(zval *arg, ulong idx, char *val, int dup);
int add_index_stringl(zval *arg, ulong idx, char *val, uint len, int dup);
int add_index_zval(zval *arg, ulong index, zval *val);

//在数组尾部 append 数据,相当于 $arr[] = $new_append_value;
int add_next_index_null(zval *arg);
int add_next_index_bool(zval *arg, int val);
int add_next_index_long(zval *arg, long val);
int add_next_index_resource(zval *arg, int val);
int add_next_index_double(zval *arg, double val);
int add_next_index_string(zval *arg, char *val, int dup);
int add_next_index_stringl(zval *arg, char *val, uint len, int dup);
int add_next_index_zval(zval *arg, zval *val);
参数用途
arg将要被添加数据的数组
key / idx要添加数据的数组的键值或者索引下标
val要添加的特定数据类型的值,这些值将被放到数组HashTable中,注意,这些函数不会使得原始zval的引用计数自动增加
lenadd_*_stringl 函数需要用到,用于指定要添加的字符串长度
dup对于字符串类型,该标志接受1或0来指明字符串内容是否被拷贝

 

示例:

----php7_wrapper.h----

#if PHP_MAJOR_VERSION < 7 /* PHP Version 5*/
	#define sw_add_assoc_string                   add_assoc_string
	#define sw_add_assoc_stringl                  add_assoc_stringl
	#define sw_add_assoc_stringl_ex               add_assoc_stringl_ex
	#define sw_add_assoc_double_ex                add_assoc_double_ex
	#define sw_add_assoc_long_ex                  add_assoc_long_ex
	#define sw_add_next_index_stringl             add_next_index_stringl
	
#else /* PHP Version 7 */
	#define sw_add_assoc_string(array, key, value, duplicate)   add_assoc_string(array, key, value)
	#define sw_add_assoc_stringl(__arg, __key, __str, __length, __duplicate)   add_assoc_stringl_ex(__arg, __key, strlen(__key), __str, __length)
	static sw_inline int sw_add_assoc_stringl_ex(zval *arg, const char *key, size_t key_len, char *str, size_t length, int __duplicate)
	{
	    return add_assoc_stringl_ex(arg, key, key_len - 1, str, length);
	}
	
	static sw_inline int sw_add_assoc_double_ex(zval *arg, const char *key, size_t key_len, double value)
	{
	    return add_assoc_double_ex(arg, key, key_len - 1, value);
	}
	
	static sw_inline int sw_add_assoc_long_ex(zval *arg, const char *key, size_t key_len, long value)
	{
	    return add_assoc_long_ex(arg, key, key_len - 1, value);
	}
	
	#define sw_add_next_index_stringl(arr, str, len, dup)    add_next_index_stringl(arr, str, len)
	
#endif

----test.c----

double swoole_microtime(void)
{
    struct timeval t;
    gettimeofday(&t, NULL);
    return (double) t.tv_sec + ((double) t.tv_usec / 1000000);
}

static int test()
{
	zval *row_array = NULL;
	
	SW_ALLOC_INIT_ZVAL(row_array);
	array_init(row_array);
    
	add_assoc_null(row_array, client->response.columns[i].name);
	add_assoc_bool(row_array, "open_http_protocol", 1);
	add_assoc_long(row_array, "size", 0);
    
	struct in_addr sin_addr;
	sin_addr.s_addr = fd;
	sw_add_assoc_string(row_array, "remote_ip", inet_ntoa(sin_addr), 1);
	
	sw_add_assoc_stringl(row_array, "request_uri", ctx->request.path, ctx->request.path_len, 1);
	
	sw_add_assoc_stringl_ex(row_array, ZEND_STRS("path"), path, path_len, 1);
	
	double now_float = swoole_microtime();
	sw_add_assoc_double_ex(row_array, ZEND_STRS("request_time_float"), now_float);
	
	sw_add_next_index_stringl(row_array, cookie, strlen(cookie), 0);
}

 

获取数组中HashTable

通过宏 Z_ARRVAL_P 来获取数组的 HashTable 数据结构:

static int test() 
{
	zset* zval;
	HashTable *vht;
		
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zset) == FAILURE)
	{
	    return;
	}
		    
	vht = Z_ARRVAL_P(zset);
}

 

根据 key 获取数组的 value

//按字符串键值查找
int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength, void **pData);
//按数字索引查找
int zend_hash_index_find(HashTable *ht, ulong index, void **pData);

int zend_hash_quick_find(HashTable *ht, char *arKey, uint nKeyLength,ulong hash_value, void **pData);
参数用途
ht从数组中获取的 HashTable
arKey要查找的字符串键值名
nKeyLength不包含NULL结束符的键值名的长度
hash_value通过 zend_get_hash_value 函数预先计算的哈希值,用于快速查询 zend_hash_quick_find 函数中
index按数字索引查找的索引下标


在下面,我将 zend_hash_find 封装为 PHP5,和 PHP7 下的两个函数。

----php7_wrapper.h----

#if PHP_MAJOR_VERSION < 7 /* PHP Version 5*/
	static inline int sw_zend_hash_find(HashTable *ht, char *k, int len, void **v)
	{
	    zval **tmp = NULL;
	    if (zend_hash_find(ht, k, len, (void **) &tmp) == SUCCESS)
	    {
	        *v = *tmp;
	        return SUCCESS;
	    }
	    else
	    {
	        *v = NULL;
	        return FAILURE;
	    }
	}
#else /* PHP Version 7 */
	static inline int sw_zend_hash_find(HashTable *ht, char *k, int len, void **v)
	{
	    zval *value = zend_hash_str_find(ht, k, len);
	    if (value == NULL)
	    {
	        return FAILURE;
	    }
	    else
	    {
	        *v = (void *) value;
	        return SUCCESS;
	    }
	}
#endif /* PHP Version */

#define php_swoole_array_get_value(ht, str, v)	(sw_zend_hash_find(ht, str, strlen(str), (void **) &v) == SUCCESS && !ZVAL_IS_NULL(v)) //根据 key 获取数组 value

----test.c----

PHP_METHOD(get_array_value_by_key)
{
	zval *zset;
	HashTable *vht;
	zval *v;
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zset) == FAILURE)
        {
        return;
        }
    
        vht = Z_ARRVAL_P(zset);
    
	//process_num
	if (php_swoole_array_get_value(vht, "process_num", v))
	{
		php_var_dump(v);
	}
    
        RETURN_TRUE;
}

 

创建一个HashTable

创建一个原始HashTable。 在可能的情况下,应优先使用array_init()和zval_ptr_dtor()方法来完成这些任务,在初始化之前,ht 需要首先通过emalloc、pemalloc或更常见的ALLOC_HASHTABLE(ht)动态分配。 ALLOC_HASHTABLE 宏使用来自特殊池的预先大小的内存块来加速所需的分配时间,并且通常优先于ht = emalloc(sizeof(HashTable))

int zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction,
	dtor_func_t pDestructor, zend_bool persistent);
int zend_hash_init_ex(HashTable *ht, uint nSize, hash_func_t pHashFunction,
	dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection);
参数用途
ht将被创建的 HashTable 指针
nSize预期持有的元素数量,数量越多将需要更多的内存,但是如果它太小的话,内存不够用时大小会扩充为现在的2倍,但是重建索引是一个开销很大的操作。
pHashFunction指定散列函数,已过时。 较旧版本的Zend引擎允许重写散列函数。 目前的版本强制DJBX33A。
pDestructor每当从HashTable中删除元素或替换元素时,都会自动调用该函数。
persistent是否分配持久化内存
bApplyProtection设置为非零值时,迭代遍历HashTable的尝试将被限制为最大递归次数。


获取HashTable元素数量

//返回 HashTable 元素数量
int zend_hash_num_elements(HashTable *ht);
//返回HashTable中下一个可分配的索引号
ulong zend_hash_next_free_element(HashTable *ht);
参数用途
ht需要查询的HashTable

 

获取HashTable元素的最小/最大值

以下函数用于查找HashTable中的最小/最大值,并放入pData之战返回,并返回他在数组中的索引下标
int zend_hash_minmax(HashTable *ht, compare_func_t compare_func, int findmax, void **pData TSRMLS_DC);

参数用途
ht需要查询的HashTable
comapre_func用于确定最大/最小值的比较函数
findmax非0 - 查找最大值  ; 0 - 查找最小值
pData最小/最大值数据将置于pData中返回

HashTable遍历操作

//移动指针到HashTable头部
void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos);
//遍历指针往前移
int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos);
//遍历指针往后移
int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos);
//移动指针到HashTable尾部
void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos);
//HashTable中是否还有下一个元素
int zend_hash_has_more_elements(HashTable *ht);
参数用途
ht要移动的HashTable
pos返回遍历位置值。 该值将由zend_hash_internal_pointer_reset_ex()自动初始化,不需要销毁

 

获取指定HashPosition位置处的值

//返回pos指示的HashTable位置处的键类型,包含 HASH_KEY_IS_LONG,HASH_KEY_IS_STRING或HASH_KEY_NON_EXISTANT。
int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos);

//获取pos指示的HashTable位置处的键值
int zend_hash_get_current_key_ex(HashTable *ht, char **str_index, uint *str_length, ulong *num_index, zend_bool duplicate, HashPosition *pos);

//获取pos指示的HashTable位置处的数据
int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos);
参数用途
ht要查询的HashTable指针
pos指向当前 HashTable 的位置处
str_index用于返回pos指示的HashTable 位置处的键值 (字符串类型)
str_length包含结束符NULL 的 str_index 的长度
num_index用于返回pos指示的HashTable 位置处的键值 (数字索引类型数组)
duplicate是否需要拷贝复制键值名(key name)
pData用于返回pos指示的HashTable 位置处的数据

HashTable数据更新

int zend_hash_add(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest);
int zend_hash_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest);
int zend_hash_quick_add(HashTable *ht, char *arKey, uint nKeyLength, ulong hash_value, void *pData, uint nDataSize, void **pDest);
int zend_hash_quick_update(HashTable *ht, char *arKey, uint nKeyLength, ulong hash_value, void *pData, uint nDataSize, void **pDest);
int zend_hash_index_update(HashTable *ht, ulong index, void *pData, uint nDataSize, void **pDest);
int zend_hash_next_insert(HashTable *ht, void *pData, uint nDataSize, void **pDest);
参数用途
ht将被修改的HashTable
arKey以NULL结尾的键值
nKeyLength包含结束符NULL的键值arKey的长度
index数组下标索引
hash_value在快速查询的时候,预先计算hash值
pData将要存储的数据的指针
nDataSize以字节为单位的存储数据的大小
pDest如果需要,则填充指向pData指向的数据副本所在的指针,该指针位于HashTable中。 允许进行适当的修改。

 

HashTable的销毁

HashTable的销毁包含下面4个函数, 各有不同,zend_hash_clean()只会清空HashTable的内容,而destroy变体将释放所有内部结构并使HashTable无法使用。 优雅的destroy 需要稍长时间; 但是,它使HashTable保持一致状态,保证在销毁期间进行访问和修改。

void zend_hash_clean(HashTable *ht);
void zend_hash_destroy(HashTable *ht);
void zend_hash_graceful_destroy(HashTable *ht);
void zend_hash_graceful_reverse_destroy(HashTable *ht);

 

HashTable 遍历修改操作示例

注意 PHP 7 和 PHP 5 的 HashTable 处理的不同:

----php7_wrapper.h----

#if PHP_MAJOR_VERSION < 7 /* PHP Version 5*/
        //数组遍历
	#define SW_HASHTABLE_FOREACH_START(ht, entry)\
	    zval **tmp = NULL;\
	    for (zend_hash_internal_pointer_reset(ht);\
	        zend_hash_has_more_elements(ht) == SUCCESS; \
	        zend_hash_move_forward(ht)) {\
	        if (zend_hash_get_current_data(ht, (void**)&tmp) == FAILURE) {\
	            continue;\
	        }\
	        entry = *tmp;
	//字符串key遍历       
	#define SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, entry)\
	    zval **tmp = NULL; ulong_t idx;\
	    for (zend_hash_internal_pointer_reset(ht); \
	            (ktype = zend_hash_get_current_key_ex(ht, &k, &klen, &idx, 0, NULL)) != HASH_KEY_NON_EXISTENT; \
	            zend_hash_move_forward(ht)\
	        ) { \
	    if (zend_hash_get_current_data(ht, (void**)&tmp) == FAILURE) {\
	        continue;\
	    }\
	    entry = *tmp;\
	    klen --;
    
	#define SW_HASHTABLE_FOREACH_END() }

	#define sw_zend_hash_get_current_key(a,b,c,d) zend_hash_get_current_key_ex(a,b,c,d,0,NULL)
	#define sw_zend_hash_del                      zend_hash_del
	#define sw_zend_hash_update                   zend_hash_update
	#define sw_zend_hash_index_find               zend_hash_index_find
	#define sw_zend_hash_add                      zend_hash_add
	#define sw_zend_hash_index_update             zend_hash_index_update

#else /* PHP Version 7*/
	#define SW_HASHTABLE_FOREACH_START(ht, _val) ZEND_HASH_FOREACH_VAL(ht, _val);  {
	
	#define SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, _val) zend_string *_foreach_key;\
	    ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _foreach_key, _val);\
	    if (!_foreach_key) {k = NULL; klen = 0; ktype = 0;}\
	    else {k = _foreach_key->val, klen=_foreach_key->len; ktype = 1;} {

	#define SW_HASHTABLE_FOREACH_END()                 } ZEND_HASH_FOREACH_END();
	
	static inline int sw_zend_hash_del(HashTable *ht, char *k, int len) {
	    return zend_hash_str_del(ht, k, len - 1);
	}
	
	static inline int sw_zend_hash_update(HashTable *ht, char *k, int len, zval *val, int size, void *ptr) {
	    return zend_hash_str_update(ht, (const char*)k, len -1, val) ? SUCCESS : FAILURE;
	}
	
	static inline int sw_zend_hash_add(HashTable *ht, char *k, int len, void *pData, int datasize, void **pDest) {
	    return zend_hash_str_add(ht, k, len - 1, pData) ? SUCCESS : FAILURE;
	}
	
	static inline int sw_zend_hash_index_update(HashTable *ht, int key, void *pData, int datasize, void **pDest) {
	    return zend_hash_index_update(ht, key, pData) ? SUCCESS : FAILURE;
	}

#endif

----swoole_client.c----
//字符串遍历,下面模拟一个 array_keys 函数
HashTable* ycroute_get_array_keys(zval *p) {
	if(!ycroute_is_array(p)) {
		return NULL;
	}
	
	char * key;
	zval *value;
	uint32_t key_len;
	int key_type;
	ulong_t num = 0;
	
	HashTable *new_hash;
	ALLOC_HASHTABLE(new_hash);
	zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(p)), NULL, ZVAL_PTR_DTOR, 0);
    
	YC_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(p), key, key_len, key_type, value)
			if (HASH_KEY_IS_STRING != key_type) { //非字符串
				continue;
			}
			
			zval *zval_key;
			YC_ALLOC_INIT_ZVAL(zval_key);
			YC_ZVAL_STRING(zval_key, key, 1);
			yc_zend_hash_index_update(new_hash, num, (void*) zval_key, sizeof(zval *), NULL);
			num++;
  YC_HASHTABLE_FOREACH_END();
  return new_hash;
}

//数组遍历
static int client_select_wait(zval *sock_array, fd_set *fds TSRMLS_DC)
{
    zval *element = NULL;
    int sock;

    ulong_t num = 0;

#if PHP_MAJOR_VERSION < 7
    HashTable *new_hash;
    char *key = NULL;
    zval **dest_element = NULL;
    uint32_t key_len;

    ALLOC_HASHTABLE(new_hash);
    //初始化 new_hash ,大小为 sock_array 数组的元素个数。
    zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(sock_array)), NULL, ZVAL_PTR_DTOR, 0);

    SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(sock_array), element)
        sock = swoole_convert_to_fd(element TSRMLS_CC);
        if ((sock < FD_SETSIZE) && FD_ISSET(sock, fds)) {
            switch (sw_zend_hash_get_current_key(Z_ARRVAL_P(sock_array), &key, &key_len, &num)) { //判断键值类型
            	case HASH_KEY_IS_STRING:
                sw_zend_hash_add(new_hash, key, key_len, (void * ) &element, sizeof(zval *), (void ** )&dest_element);
                break;
            	case HASH_KEY_IS_LONG:
                sw_zend_hash_index_update(new_hash, num, (void * ) &element, sizeof(zval *), (void ** )&dest_element);
                break;
            }
            if (dest_element) {
                sw_zval_add_ref(dest_element);
            }
        }
        num ++;
    SW_HASHTABLE_FOREACH_END();

    zend_hash_destroy(Z_ARRVAL_P(sock_array));
    efree(Z_ARRVAL_P(sock_array));

    zend_hash_internal_pointer_reset(new_hash);
    Z_ARRVAL_P(sock_array) = new_hash;
#else
    zval new_array;
    array_init(&new_array);
    zend_ulong num_key;
    zend_string *key;
    zval *dest_element;

    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sock_array), num_key, key, element) {
        sock = swoole_convert_to_fd(element TSRMLS_CC);
        if ((sock < FD_SETSIZE) && FD_ISSET(sock, fds)) {
            if (key) {
                dest_element = zend_hash_add(Z_ARRVAL(new_array), key, element);
            } else {
                dest_element = zend_hash_index_update(Z_ARRVAL(new_array), num_key, element);
            } 
            if (dest_element) {
                Z_ADDREF_P(dest_element);
            }
        }
        num++;
    } ZEND_HASH_FOREACH_END();

    zval_ptr_dtor(sock_array);
    ZVAL_COPY_VALUE(sock_array, &new_array);
#endif
    return num ? 1 : 0;
}

 

HashTable的拷贝与合并

void zend_hash_copy(HashTable *dst, HashTable *src, copy_ctor_func_t pCopyConstructor, 
        void *tmp, uint size);
void zend_hash_merge(HashTable *dst, HashTable *src, copy_ctor_func_t pCopyConstructor,
        void *tmp, uint size, int overwrite);
void zend_hash_merge_ex(HashTable *dst, HashTable *src, copy_ctor_func_t pCopyConstructor,
        uint size, merge_checker_func_t pMergeSource,void *pParam);

zend_hash_copy()函数将src中的每个元素都将被复制到dst。使用pCopyConstructor方法可以保证在需要时执行其他资源复制。 zend_hash_merge()与zend_hash_copy()唯一的不同在于最后的overwrite参数. 当将它设置为非0值时, zend_hash_merge()的行为和zend_hash_copy()一致. 当它设置为0时,跳过已经存在的元素。zend_hash_merge_ex()允许使用一个合并检查函数有选择的拷贝。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值