PHP源代码数组统计count分析
PHP源代码数组统计count分析
发布时间:2016-12-29 来源:查字典编辑
摘要:zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保...

zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下:

复制代码 代码如下:

//文件1:zend/zend.h

/*

* zval

*/

typedef struct _zval_struct zval;

...

typedef union _zvalue_value {

long lval; /* long value */

double dval; /* double value */

struct {

char *val;

int len;

} str;

HashTable *ht; /* hash table value */

zend_object_value obj;

} zvalue_value;

struct _zval_struct {

/* Variable information */

zvalue_value value; /* value */

zend_uint refcount__gc;

zend_uchar type; /* active type */

zend_uchar is_ref__gc;

};

//hash表的结构如下

//文件2:zend/zend_hash.h

typedef struct _hashtable {

uint nTableSize;

uint nTableMask;

uint nNumOfElements;

ulong nNextFreeElement;

Bucket *pInternalPointer; /* Used for element traversal */

Bucket *pListHead;

Bucket *pListTail;

Bucket **arBuckets;

dtor_func_t pDestructor;

zend_bool persistent;

unsigned char nApplyCount;

zend_bool bApplyProtection;

#if ZEND_DEBUG

int inconsistent;

#endif

}

HashTable;

一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:

复制代码 代码如下:

//文件3:zend/zend_operators.php

#define Z_STRLEN(zval) (zval).value.str.len

...

#define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)

...

#define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)

而对于数组的count操作,其实有两种结果,在count 的api中也提到了第二个参数mode《http://www.php.net/manual/en/function.count.php》,这个mode参数指明了,是否需要重新统计,而它的重新统计将会遍历一次数组,效率上是O(N)[N:长度],默认情况下是不重新统计,那这个时候将会直接输出hashtable中的nNumOfElements,此时的效率也是O(1)次:count代码如下:

复制代码 代码如下:

//文件4:ext/standard/array.c

PHP_FUNCTION(count)

{

zval *array;

long mode = COUNT_NORMAL;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {

return;

}

switch (Z_TYPE_P(array)) {

case IS_NULL:

RETURN_LONG(0);

break;

case IS_ARRAY:

RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));

break;

.....

//php_count_recursive的实现

static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */

{

long cnt = 0;

zval **element;

if (Z_TYPE_P(array) == IS_ARRAY) {

//错误处理

if (Z_ARRVAL_P(array)->nApplyCount > 1) {

php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");

return 0;

}

//通过zend_hash_num_elements直接获得长度

cnt = zend_hash_num_elements(Z_ARRVAL_P(array));

//如果指定了需要重新统计,则会进入一次循环统计

if (mode == COUNT_RECURSIVE) {

HashPosition pos;

for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);

zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;

zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)

) {

Z_ARRVAL_P(array)->nApplyCount++;

cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);

Z_ARRVAL_P(array)->nApplyCount--;

}

}

}

return cnt;

}

//文件5:zend/zend_hash.c

//zend_hash_num_elements的实现

ZEND_API int zend_hash_num_elements(const HashTable *ht)

{

IS_CONSISTENT(ht);

return ht->nNumOfElements;

}

推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
相关阅读
网友关注
最新php教程学习
热门php教程学习
编程开发子分类