程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> php的擴展與嵌入--php擴展中的數組和哈希表1

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);
}





















  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved