深入PHP内核之数组

定义:

PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。此类型在很多方面做了优化,因此可以把它当成真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合,栈,队列以及更多可能性。数组元素的值也可以是另一个数组。树形结构和多维数组也是允许的。

这是手册中对PHP数组的定义,本质上是一种键-值映射的关系,算是一种散列表(哈希表)。

到这里我不得不说,PHP内核中的神器了  HashTable

HashTable即具有双向链表的优点,PHP中的定义的变量保存在一个符号表里,而这个符号表其实就是一个HashTable,它的每一个元素都是一个zval*类型的变量。不仅如此,保存用户定义的函数、类、资源等的容器都是以HashTable的形式在内核中实现的。

因此,PHP的数组读写都可以在O(1)内完成,这是非常高效的,因此开销和C++、Java相比也就是hashtable的创建了

我们看一下PHP定义数组

<?php  
    $array = array();  
    $array["key"] = "value";  

 在内核中

zval* array;  
array_init(array);  
add_assoc_string(array, "key", "value", 1);

 这样就可以了

看一下源码是怎么实现

在zend_API.h 文件中,这是一个宏定义

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

 接着看,在zend_API.c 文件中

/* Argument parsing API -- andrei */
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_alloc.h文件中

#define ALLOC_HASHTABLE_REL(ht) \
        (ht) = (HashTable *) emalloc_rel(sizeof(HashTable))

 再接着看

ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_
FILE_LINE_DC)
{
        uint i = 3;//初始化是为1<<3=8

        SET_INCONSISTENT(HT_OK);
		//最大个数为1>>31 = 2^31 =2147483648
        if (nSize >= 0x80000000) {
                /* prevent overflow */
                ht->nTableSize = 0x80000000;
        } else {
                while ((1U << i) < nSize) {
                        i++;
                }
                ht->nTableSize = 1 << i;
        }

        ht->nTableMask = 0;     /* 0 means that ht->arBuckets is uninitialized */
        ht->pDestructor = pDestructor; /* 元素的析构函数(指针) */
        ht->arBuckets = (Bucket**)&uninitialized_bucket;
        ht->pListHead = NULL;  /* 头元素, 用于线性遍历 */
        ht->pListTail = NULL;  /* 尾元素, 用于线性遍历 */
        ht->nNumOfElements = 0; /* HashTable中实际元素的个数 */
        ht->nNextFreeElement = 0; /* 下个空闲可用位置的数字索引 */
        ht->pInternalPointer = NULL; /* 内部位置指针, 会被reset, current这些遍历函数使用 */
		ht->persistent = persistent;
        ht->nApplyCount = 0;/* 循环遍历保护 */
        ht->bApplyProtection = 1;
        return SUCCESS;
}

 以上是数组初始化可以参考  http://www.laruence.com/2009/08/23/1065.html

然后赋值的过程

zval* val;  
MAKE_STD_ZVAL(val);  
ZVAL_STRING(val, "value", 0);  
zend_hash_add(h, "key", 4, &val, sizeof(zval*), NULL);  

 内核为我们提供了方便的宏来管理数组

ZEND_API int add_index_long(zval *arg, ulong idx, long n);
ZEND_API int add_index_null(zval *arg, ulong idx);
ZEND_API int add_index_bool(zval *arg, ulong idx, int b);
ZEND_API int add_index_resource(zval *arg, ulong idx, int r);
ZEND_API int add_index_double(zval *arg, ulong idx, double d);
ZEND_API int add_index_string(zval *arg, ulong idx, const char *str, int duplicate);
ZEND_API int add_index_stringl(zval *arg, ulong idx, const char *str, uint length, int duplicate);
ZEND_API int add_index_zval(zval *arg, ulong index, zval *value);

ZEND_API int add_next_index_long(zval *arg, long n);
ZEND_API int add_next_index_null(zval *arg);
ZEND_API int add_next_index_bool(zval *arg, int b);
ZEND_API int add_next_index_resource(zval *arg, int r);
ZEND_API int add_next_index_double(zval *arg, double d);
ZEND_API int add_next_index_string(zval *arg, const char *str, int duplicate);
ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate);

ZEND_API int add_assoc_long_ex(zval *arg, const char *key, uint key_len, long n);
ZEND_API int add_assoc_null_ex(zval *arg, const char *key, uint key_len);
ZEND_API int add_assoc_bool_ex(zval *arg, const char *key, uint key_len, int b);
ZEND_API int add_assoc_resource_ex(zval *arg, const char *key, uint key_len, int r);
ZEND_API int add_assoc_double_ex(zval *arg, const char *key, uint key_len, double d);
ZEND_API int add_assoc_string_ex(zval *arg, const char *key, uint key_len, char *str, int duplicate);
ZEND_API int add_assoc_stringl_ex(zval *arg, const char *key, uint key_len, char *str, uint length, int duplicate);
ZEND_API int add_assoc_zval_ex(zval *arg, const char *key, uint key_len, zval *value);

#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key)+1, __n)
#define add_assoc_null(__arg, __key) add_assoc_null_ex(__arg, __key, strlen(__key) + 1)
#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key)+1, __b)
#define add_assoc_resource(__arg, __key, __r) add_assoc_resource_ex(__arg, __key, strlen(__key)+1, __r)
#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key)+1, __d)
#define add_assoc_string(__arg, __key, __str, __duplicate) add_assoc_string_ex(__arg, __key, strlen(__key)+1, __str, __duplicate)
#define add_assoc_stringl(__arg, __key, __str, __length, __duplicate) add_assoc_stringl_ex(__arg, __key, strlen(__key)+1, __str, __len
gth, __duplicate)
#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key)+1, __value)

 具体实现

$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");  
$arr[] = $var;  add_next_index_zval(arr, zval);  

$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] = $var;     add_index_zval(arr, 5, zval); 

$arr["abc"] = NULL; add_assoc_null(arr, "abc");  
$arr["def"] = 42;   add_assoc_long(arr, "def", 42);  
$arr["ghi"] = true; add_assoc_bool(arr, "ghi", 1);  
$arr["jkl"]  = 3.14 add_assoc_double(arr, "jkl", 3.14);  
$arr["mno"]="foo"   add_assoc_string(arr, "mno", "foo", 1");  
$arr["pqr"] = $var; add_assoc_zval(arr, "pqr", zval);  

 扩展中测试

PHP_FUNCTION(array_test1)
{
	zval *value;
	zval *element;
	char *s="This is a test";
	char *key="a";
	MAKE_STD_ZVAL(element);
	MAKE_STD_ZVAL(value);
	array_init(value);
	ZVAL_STRING(element,s,strlen(s));
	zend_hash_update(value->value.ht,key,strlen(key)+1,(void*)&element,sizeof(zval*),NULL);
	ZEND_SET_SYMBOL(EG(active_symbol_table),"siren",value); 
}

PHP_FUNCTION(array_test2){  
	char* str;  
	zval* subarray;  
	array_init(return_value);
	array_init(subarray);  	
	add_next_index_string(return_value, "test", 1);  
	str = estrdup("This is a test");  
	add_next_index_string(return_value, str, 0);  
	add_assoc_double(return_value, "double", 3.14);  
	add_next_index_string(subarray, "hello", 1);  
	add_assoc_zval(return_value, "xsubarray", subarray);  
}  

 

posted @ 2015-07-28 23:13  踏雪无痕SS  阅读(654)  评论(0编辑  收藏  举报