设为首页 收藏本站
查看: 851|回复: 0

[经验分享] PHP源码分析-HashTable API

[复制链接]

尚未签到

发表于 2017-3-24 09:57:14 | 显示全部楼层 |阅读模式
  一、创建HashTable

int zend_hash_init(
HashTable *ht,//指向一个HashTable
uint nSize,//nSize是指这个HashTable可以拥有的元素的最大数量。在我们添加新的元素时,这个值会根据情况决定是否自动增长,这个值永远都是2的次方,如果你给它的值不是一个2的次方
//的形式,那它将自动调整成大于它的最小的2的次方值。它的计算方法就像这样:nSize = pow(2, ceil(log(nSize, 2)))。(HashTable能够包含任意数量的元素,这个值只是为了提前申请好内存,提高性
//能,省的不停的进行rehash操作)
hash_func_t pHashFunction,//pHashFunction是早期的参数,为向前兼容所以没有去掉。直接赋值NULL即可。
dtor_func_t pDestructor,//一个回调函数,当我们删除或者修改HashTable中其中一个元素时候便会调用,它的函数原型必须是这样的:void method_name(void *pElement),一般使用宏ZVAL_PTR_DTOR即可。
zend_bool persistent//值为0或1,如果是1那么这个HashTable将永远存在于内存中,而不会在RSHUTDOWN阶段自动被注销掉。此时第一个参数ht所指向的地址必须是通过pemalloc()函数申请的。
);


注:

#define ZVAL_PTR_DTOR (void (*)(void *)) zval_ptr_dtor_wrapper  删除元素,回调析构该元素。
  

  二、添加


int zend_hash_add(
HashTable *ht,//待操作的ht
char *arKey,//索引,如"my_key"
uint nKeyLen,//字符串索引的长度,如6
void **pData,//要插入的数据,注意它是void **类型的。
uint nDataSize,//例如,存放zval类型变量,那么nDataSize=sizeof(zval*)
void *pDest//如果操作成功,则pDest=*pData;
);
int zend_hash_next_index_insert(
HashTable *ht,//待操作的ht
void *pData,//要插入的数据
uint nDataSize,
void **pDest
);


三、更新

int zend_hash_update(
HashTable *ht,
char *arKey,//索引字符串
uint nKeyLen,//索引字符串长度
void *pData,
uint nDataSize,
void **pDest
);
int zend_hash_index_update(
HashTable *ht,
ulong h,
void *pData,
uint nDataSize,
void **pDest
);

  四、查找


int zend_hash_find(
HashTable *ht,
char *arKey, //索引字符串
uint nKeyLength,//索引字符串长度
void **pData//找到元素,则将元素指向pData
);
int zend_hash_index_find(
HashTable *ht,
ulong h, //数字索引
void **pData//找到元素,则将元素指向pData
);


五、检测

int zend_hash_exists(
HashTable *ht,
char *arKey,
uint nKeyLen
);
int zend_hash_index_exists(
HashTable *ht,
ulong h
);这两个函数返回SUCCESS或者FAILURE,分别代表着是否存在  

  六、加速
  ulong zend_get_hash_value(char *arKey, uint nKeyLen);//返回索引的hash值

通过使用zend_get_hash_value函数得到索引hash值,之后对hashTable进行添加、修改等操作使用quick系列函数可以避免重复计算字符串的hash值,达到加速加速的目的。

int zend_hash_quick_add(
HashTable *ht,
char *arKey,
uint nKeyLen,
ulong hashval,//zend_get_hash_value函数得到的hash值
void *pData,
uint nDataSize,
void **pDest
);
int zend_hash_quick_update(
HashTable *ht,
char *arKey,
uint nKeyLen,
ulong hashval,
void *pData,
uint nDataSize,
void **pDest
);
int zend_hash_quick_find(
HashTable *ht,
char *arKey,
uint nKeyLen,
ulong hashval,
void **pData
);
int zend_hash_quick_exists(
HashTable *ht,
char *arKey,
uint nKeyLen,
ulong hashval
);


七、数组之间的复制与合并
void zend_hash_copy(
HashTable *target,//*source中的所有元素都会通过pCopyConstructor函数Copy到*target中去
HashTable *source,//target中原有的与source中索引位置的数据会被替换掉,而其它的元素则会被保留,原封不动。
copy_ctor_func_t pCopyConstructor,
void *tmp,//tmp参数是为了兼容PHP4.0.3以前版本的,现在赋值为NULL即可。
uint size//size参数代表每个元素的大小,对于PHP语言中的数组来说,这里的便是sizeof(zval*)了。
);
void zend_hash_merge(
HashTable *target,
HashTable *source,
copy_ctor_func_t pCopyConstructor,
void *tmp,
uint size,
int overwrite
);zend_hash_merge()与zend_hash_copy唯一的不同便是多了个int类型的overwrite参数,当其值非0的时候,两个函数的工作是完全一样的;如果overwrite参数为0,则zend_hash_merge函数就不会对target中已有索引的值进行替换了。
void zend_hash_merge_ex(
HashTable *target,
HashTable *source,
copy_ctor_func_t pCopyConstructor,
uint size,
merge_checker_func_t pMergeSource,
void *pParam
);zend_hash_merge_ex函数又繁琐了些,与zend_hash_copy相比,其多了两个参数,多出来的pMergeSoure回调函数允许我们选择性的进行merge,而不是全都merge。  

  八、遍历
  PHP中数组的本质是HashTable,可以使用foreach来遍历PHP中的数组。在内核中则是使用zend_hash_apply函数。

zend_hash_apply接收一个回调函数,并将HashTable的每一个元素都传递给回调函数。这里的回调函数相当于PHP中的foreach循环主体。



回调函数的返回值有一个共同的约定:
ZEND_HASH_APPLY_KEEP结束当前请求,进入下一个循环。与PHP语言forech语句中的一次循环执行完毕或者遇到
continue关键字的作用一样。
ZEND_HASH_APPLY_STOP跳出,与PHP语言forech语句中的break关键字的作用一样。
ZEND_HASH_APPLY_REMOVE删除当前的元素,然后继续处理下一个。相当于在PHP语言中:unset($foo
[$key]);continue;


1、无索引遍历

typedef int (*apply_func_t)(void *pDest TSRMLS_DC);

void zend_hash_apply(HashTable *ht,apply_func_t apply_func TSRMLS_DC);



看看PHP语言中的forech循环。
<?php
function foreach_test($arr){
foreach($arr as $val) {
echo "The value is: $val\n";
}
}
?>


在内核中实现函数foreach_test。

//zend_hash_apply回调函数,相当于PHP中的foreach主体
int php_foreach_body(zval **zval TSRMLS_DC){
zval tmpcopy = **zval;//重新copy一个zval,防止破坏原数据
zval_copy_ctor(&tmpcopy);
INIT_PZVAL(&tmpcopy);//初始化refcount__gc=1, is_ref__gc=0
convert_to_string(&tmpcopy); //转换为字符串
php_printf("The value is:");
PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));
php_printf("\n");
zval_dtor(&tmpcopy);
return ZEND_HASH_APPLY_KEEP;
}
PHP_FUNCTION(foreach_test){
zval *arr;//相当于php中function的参数$arr
if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) ){
RETURN_NULL();
}
zend_hash_apply(Z_ARRVAL_P(arr), php_foreach_body TSRML_CC);//开始遍历
}


  2、有索引遍历
  为了能在遍历时同时接收索引的值,可以使用void zend_hash_apply_with_arguments:

typedef int (*apply_func_args_t)(void *pDest,int num_args, va_list args, zend_hash_key *hash_key);
void zend_hash_apply_with_arguments(
HashTable *ht,
apply_func_args_t apply_func,
int numargs, //传递参数的个数,通过指定参数个数,决定va_end()
...
);//这个函数通过C语言中的可变参数特性来接收参数。


PHP中带索引的遍历
<?php
foreach($arr as $key => $val)
{
echo "The value of $key is: $val\n";
}
?>


内核中实现:
int php_foreach_body_and_key(zval **val, int num_args, va_list args, zend_hash_key *hash_key){
zval tmpcopy = **val;//重新copy一个zval,防止破坏原数据
php_printf("附带参数个数%d<br>", num_args);
char **a, **b;
a = va_arg(args, char**);//args是可变参数的起始地址
b = va_arg(args, char**);
php_printf("参数1:%s, 参数2:%s<br>", *a, *b);
INIT_PZVAL(&tmpcopy);
zval_copy_ctor(&tmpcopy);
convert_to_string(&tmpcopy);
php_printf("The value of ");
if( hash_key->nKeyLength ){
PHPWRITE(hash_key->arKey, hash_key->nKeyLength);
}else{
php_printf("%ld", hash_key->h);
}
php_printf(" is: ");
PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));
php_printf("\n");
zval_dtor(&tmpcopy);
return ZEND_HASH_APPLY_KEEP;
}
PHP_FUNCTION(foreach_test){
zval *arr;//相当于php中function的参数$arr
if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) ){
RETURN_NULL();
}
char *a="var1", *b="var2"; //可变参数a,b
zend_hash_apply_with_arguments(arrht, php_foreach_body_and_key, 2, &a, &b);//开始遍历,并传递2个可参数
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-354455-1-1.html 上篇帖子: php smarty变量的修饰 下篇帖子: PHP框架三(视图)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表