搜索
首页后端开发php教程php的扩展与嵌入--php扩展中的数组和哈希表1_PHP教程

在php中,数组的底层实现就是哈希表,都是以key-value的形式出现的。在php的Zend引擎中,针对不同的哈希表操作,都有着专门的对哈希表进行操作的api。


Creation

对于哈希表而言,每次初始化的方式都是一样的,都由下面这个函数zend_hash_init来完成:

int zend_hash_init(HashTable *ht, uint nSize,
    hash_func_t pHashFunction,
    dtor_func_t pDestructor, zend_bool persistent)
其中ht是指向哈希表的指针,既可以对一个已存在的hashtable变量取引用。也可以为新的hashtable申请内存。一般的方法就是:

ALLOC_HASHTABLE(ht),相当于ht = emalloc(sizeof(HashTable));。

nSize是哈希表的最大元素数,是为了提前申请好内存考虑的。如果它不是2的指数倍,会根据下式增长nSize = pow(2, ceil(log(nSize, 2)));,比如如果给了5,那么会增长到8.这个应该是为了内存管理比较方便所采用的机制。

pHashFunction属于以前版本的zend eigine函数,在新版本中一直设为NULL即可。

pDestructor指向当哈希表中的元素被删掉的时候(zend_hash_del() zend_hash_update())所调用的方法的入口,也就是一个相应的回调函数。假如说给定了method_name函数,那么在函数实现的时候:

void method_name(void *pElement)
pElement指向被删掉的元素

persistent这个是一个标志位,表示是否是持久型的哈希表,持久型的数据是独立于请求之外的,不会在RSHUTDOWN的时候被注销掉。但是如果设1的话,那么ht在申请内存的时候一定要使用pemalloc().

举个例子:在每个php请求生命周期中对symbol_table初始化的时候都会看到zend_hash_init(&EG(symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0);
每当unset的时候,相应的存储在哈希表中的zval*都被发送给zval_ptr_dtor()进行销毁。


Population:

有四种主要的插入和更新哈希表中数据的函数:

int zend_hash_add(HashTable *ht, char *arKey, uint nKeyLen,
                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_next_index_insert(HashTable *ht,
                void *pData, uint nDataSize, void **pDest);
前两个函数添加带字符串索引的数据到hashtable中,比如php中$foo['bar'] = 'barvalue',那么在扩展中:

zend_hash_add(fooHashTbl, "bar", sizeof("bar"), &barZval, sizeof(zval*), NULL);

就把相应key值和对应的表值加入到了hashtable中去了。

add和update唯一的区别是如果key已经存在的话,add会失败的。

后两个函数是向ht中添加数字索引的数据。

zend_hash_next_index_insert()函数不需要索引值参数,而是自己直接计算出下一个数字索引值。

而如果想自己获得下一个元素的数字索引值也可以通过zend_hash_next_free_element()来获得索引。
ulong nextid = zend_hash_next_free_element(ht);
zend_hash_index_update(ht, nextid, &data, sizeof(data), NULL);
上面这段代码就相当于:

zend_hash_next_index_insert(HashTable *ht, &data,sizeof(data),NULL).

其中pDest参数可以用来存储新加入的元素的地址值。



Recall:查找

一般来说,有两种获得哈希表中数据的方法:

int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength,
                                        void **pData);
int zend_hash_index_find(HashTable *ht, ulong h, void **pData);

在下面的这个例子中可以更清楚的看到:

void hash_sample(HashTable *ht, sample_data *data1)
{
   sample_data *data2;
   ulong targetID = zend_hash_next_free_element(ht);//获取下一个索引的位置
   if (zend_hash_index_update(ht, targetID,
           data1, sizeof(sample_data), NULL) == FAILURE) {//把数据data1插入到哈希表的下一个索引的位置中去
       /* Should never happen */
       return;
   }
   if(zend_hash_index_find(ht, targetID, (void **)&data2) == FAILURE) {//利用id去寻找哈希表中的值,如果找到的话把值放在data2中。
       /* Very unlikely since we just added this element */
       return;
   }
   /* data1 != data2, however *data1 == *data2 */
}
除了获得哈希表中的值之外,有的时候更重要的是知道一些元素的存在:

int zend_hash_exists(HashTable *ht, char *arKey, uint nKeyLen);
int zend_hash_index_exists(HashTable *ht, ulong h);
分别针对字符串索引和数字的索引。返回的是1和0.
if (zend_hash_exists(EG(active_symbol_table),
                                "foo", sizeof("foo"))) {//确定活动的符号表中是否存在foo变量
    /* $foo is set */
} else {
    /* $foo does not exist */
}


Quick Population and Recall 当需要对同一个字符串的key进行许多操作的时候比如先检测有没有,然后插入再修改之类的,可以使用zend_get_hash_value来进行提速。这个函数的返回值可以和quick系列的函数使用,从而达到加速的目的。因为不需要再重复计算字符串的散列值,而是直接使用已有的散列值
ulong zend_get_hash_value(char *arKey, uint nKeyLen);
用这个返回值传给下面的quick系列函数就可以达到加速的目的:
int zend_hash_quick_add(HashTable *ht,
    char *arKey, uint nKeyLen, ulong hashval,
    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 php_sample_hash_copy(HashTable *hta, HashTable *htb,
                    char *arKey, uint nKeyLen TSRMLS_DC)
{
    ulong hashval = zend_get_hash_value(arKey, nKeyLen);//获得用来加速的散列值hashval
    zval **copyval;
    if (zend_hash_quick_find(hta, arKey, nKeyLen,
                hashval, (void**)©val) == FAILURE) {//首先要在hta table里面找到相应的元素,并且存储在copyval中。
        /* arKey doesn't actually exist */
        return;
    }
    /* The zval* is about to be owned by another hash table */
    (*copyval)->refcount__gc++;//相应zval*变量的引用次数+1
    zend_hash_quick_update(htb, arKey, nKeyLen, hashval,
                copyval, sizeof(zval*), NULL);//把从hta中拿来的copyval放在htb里面。
}

注意并没有zend_hash_del函数。

Copy and Merging 有三个方法可以进行数据的拷贝,先来看第一个:
typedef void (*copy_ctor_func_t)(void *pElement);
void zend_hash_copy(HashTable *target, HashTable *source,
            copy_ctor_func_t pCopyConstructor,
            void *tmp, uint size);
在source中的每个元素都会被拷贝到target中.通过pCopyConstructor的处理可以使得在拷贝变量的时候对这些变量的ref_count进行加一的操作。target中原有的与source中索引位置相同的元素会被替换掉,而其他的元素则会被保留。
tmp这里放NULL,低版本才会用到。
size的话代表每个元素的大小,一般是sizeof(zval *)。
void zend_hash_merge(HashTable *target, HashTable *source,
            copy_ctor_func_t pCopyConstructor,
            void *tmp, uint size, int overwrite);
主要是多了一个overwrite的参数,如果非0,那就跟copy一样,如果是0,那就对于已经存在的元素就不会进行复制了。

下面的这一组函数允许使用一个归并的检查进行选择性的复制:
typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht,
    void *source_data, zend_hash_key *hash_key, void *pParam);
void zend_hash_merge_ex(HashTable *target, HashTable *source,
            copy_ctor_func_t pCopyConstructor, uint size,
            merge_checker_func_t pMergeSource, void *pParam);
pMergeSource回调函数使得可以选择性的进行合并,而不是全部合并,这个给人的感觉有点像c语言里面快速排序函数所留的函数入口,可以决定排序的方式。
下面给出了一个应用的例子:
zend_bool associative_only(HashTable *ht, void *pData,
            zend_hash_key *hash_key, void *pParam)
{
    /* True if there's a key, false if there's not */
    return (hash_key->arKey && hash_key->nKeyLength);//字符串类型的key,因为存在nKeyLength
}
void merge_associative(HashTable *target, HashTable *source)
{
    zend_hash_merge_ex(target, source, zval_add_ref,
                sizeof(zval*), associative_only, NULL);
}





















www.bkjia.comtruehttp://www.bkjia.com/PHPjc/635039.htmlTechArticle在php中,数组的底层实现就是哈希表,都是以key-value的形式出现的。在php的Zend引擎中,针对不同的哈希表操作,都有着专门的对哈希表进行...
声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
PHP类型提示如何起作用,包括标量类型,返回类型,联合类型和无效类型?PHP类型提示如何起作用,包括标量类型,返回类型,联合类型和无效类型?Apr 17, 2025 am 12:25 AM

PHP类型提示提升代码质量和可读性。1)标量类型提示:自PHP7.0起,允许在函数参数中指定基本数据类型,如int、float等。2)返回类型提示:确保函数返回值类型的一致性。3)联合类型提示:自PHP8.0起,允许在函数参数或返回值中指定多个类型。4)可空类型提示:允许包含null值,处理可能返回空值的函数。

PHP如何处理对象克隆(克隆关键字)和__clone魔法方法?PHP如何处理对象克隆(克隆关键字)和__clone魔法方法?Apr 17, 2025 am 12:24 AM

PHP中使用clone关键字创建对象副本,并通过\_\_clone魔法方法定制克隆行为。1.使用clone关键字进行浅拷贝,克隆对象的属性但不克隆对象属性内的对象。2.通过\_\_clone方法可以深拷贝嵌套对象,避免浅拷贝问题。3.注意避免克隆中的循环引用和性能问题,优化克隆操作以提高效率。

PHP与Python:用例和应用程序PHP与Python:用例和应用程序Apr 17, 2025 am 12:23 AM

PHP适用于Web开发和内容管理系统,Python适合数据科学、机器学习和自动化脚本。1.PHP在构建快速、可扩展的网站和应用程序方面表现出色,常用于WordPress等CMS。2.Python在数据科学和机器学习领域表现卓越,拥有丰富的库如NumPy和TensorFlow。

描述不同的HTTP缓存标头(例如,Cache-Control,ETAG,最后修饰)。描述不同的HTTP缓存标头(例如,Cache-Control,ETAG,最后修饰)。Apr 17, 2025 am 12:22 AM

HTTP缓存头的关键玩家包括Cache-Control、ETag和Last-Modified。1.Cache-Control用于控制缓存策略,示例:Cache-Control:max-age=3600,public。2.ETag通过唯一标识符验证资源变化,示例:ETag:"686897696a7c876b7e"。3.Last-Modified指示资源最后修改时间,示例:Last-Modified:Wed,21Oct201507:28:00GMT。

说明PHP中的安全密码散列(例如,password_hash,password_verify)。为什么不使用MD5或SHA1?说明PHP中的安全密码散列(例如,password_hash,password_verify)。为什么不使用MD5或SHA1?Apr 17, 2025 am 12:06 AM

在PHP中,应使用password_hash和password_verify函数实现安全的密码哈希处理,不应使用MD5或SHA1。1)password_hash生成包含盐值的哈希,增强安全性。2)password_verify验证密码,通过比较哈希值确保安全。3)MD5和SHA1易受攻击且缺乏盐值,不适合现代密码安全。

PHP:服务器端脚本语言的简介PHP:服务器端脚本语言的简介Apr 16, 2025 am 12:18 AM

PHP是一种服务器端脚本语言,用于动态网页开发和服务器端应用程序。1.PHP是一种解释型语言,无需编译,适合快速开发。2.PHP代码嵌入HTML中,易于网页开发。3.PHP处理服务器端逻辑,生成HTML输出,支持用户交互和数据处理。4.PHP可与数据库交互,处理表单提交,执行服务器端任务。

PHP和网络:探索其长期影响PHP和网络:探索其长期影响Apr 16, 2025 am 12:17 AM

PHP在过去几十年中塑造了网络,并将继续在Web开发中扮演重要角色。1)PHP起源于1994年,因其易用性和与MySQL的无缝集成成为开发者首选。2)其核心功能包括生成动态内容和与数据库的集成,使得网站能够实时更新和个性化展示。3)PHP的广泛应用和生态系统推动了其长期影响,但也面临版本更新和安全性挑战。4)近年来的性能改进,如PHP7的发布,使其能与现代语言竞争。5)未来,PHP需应对容器化、微服务等新挑战,但其灵活性和活跃社区使其具备适应能力。

为什么要使用PHP?解释的优点和好处为什么要使用PHP?解释的优点和好处Apr 16, 2025 am 12:16 AM

PHP的核心优势包括易于学习、强大的web开发支持、丰富的库和框架、高性能和可扩展性、跨平台兼容性以及成本效益高。1)易于学习和使用,适合初学者;2)与web服务器集成好,支持多种数据库;3)拥有如Laravel等强大框架;4)通过优化可实现高性能;5)支持多种操作系统;6)开源,降低开发成本。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境