zl程序教程

您现在的位置是:首页 >  后端

当前栏目

PHP源代码数组统计count分析

PHP统计数组 分析 源代码 count
2023-06-13 09:14:29 时间
zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下:
复制代码代码如下:

//文件1:zend/zend.h
/*
*zval
*/
typedefstruct_zval_structzval;
...
typedefunion_zvalue_value{
longlval;/*longvalue*/
doubledval;/*doublevalue*/
struct{
char*val;
intlen;
}str;
HashTable*ht;/*hashtablevalue*/
zend_object_valueobj;
}zvalue_value;

struct_zval_struct{
/*Variableinformation*/
zvalue_valuevalue;/*value*/
zend_uintrefcount__gc;
zend_uchartype;/*activetype*/
zend_ucharis_ref__gc;
};
//hash表的结构如下
//文件2:zend/zend_hash.h
typedefstruct_hashtable{
uintnTableSize;
uintnTableMask;
uintnNumOfElements;
ulongnNextFreeElement;
Bucket*pInternalPointer;/*Usedforelementtraversal*/
Bucket*pListHead;
Bucket*pListTail;
Bucket**arBuckets;
dtor_func_tpDestructor;
zend_boolpersistent;
unsignedcharnApplyCount;
zend_boolbApplyProtection;
#ifZEND_DEBUG
intinconsistent;
#endif
}
HashTable;

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

复制代码代码如下:

//文件3:zend/zend_operators.php
#defineZ_STRLEN(zval)(zval).value.str.len
...
#defineZ_STRLEN_P(zval_p)Z_STRLEN(*zval_p)
...
#defineZ_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;
longmode=COUNT_NORMAL;

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

switch(Z_TYPE_P(array)){
caseIS_NULL:
RETURN_LONG(0);
break;
caseIS_ARRAY:
RETURN_LONG(php_count_recursive(array,modeTSRMLS_CC));
break;
.....

//php_count_recursive的实现
staticintphp_count_recursive(zval*array,longmodeTSRMLS_DC)/*{{{*/
{
longcnt=0;
zval**element;

if(Z_TYPE_P(array)==IS_ARRAY){
//错误处理
if(Z_ARRVAL_P(array)->nApplyCount>1){
php_error_docref(NULLTSRMLS_CC,E_WARNING,"recursiondetected");
return0;
}
//通过zend_hash_num_elements直接获得长度
cnt=zend_hash_num_elements(Z_ARRVAL_P(array));

//如果指定了需要重新统计,则会进入一次循环统计
if(mode==COUNT_RECURSIVE){
HashPositionpos;

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_RECURSIVETSRMLS_CC);
Z_ARRVAL_P(array)->nApplyCount--;
}
}
}

returncnt;
}

//文件5:zend/zend_hash.c
//zend_hash_num_elements的实现
ZEND_APIintzend_hash_num_elements(constHashTable*ht)
{
IS_CONSISTENT(ht);

returnht->nNumOfElements;
}