PHP では、配列の基礎となる実装はハッシュ テーブルであり、キーと値の形式で表示されます。 PHP の Zend エンジンには、さまざまなハッシュ テーブル操作のためのハッシュ テーブルを操作するための特別な API があります。
創造
ハッシュテーブルの場合、初期化方法は毎回同じであり、次の関数zend_hash_initによって完了します:
int zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent)ここで、ht はハッシュ テーブルへのポインタであり、既存のハッシュテーブル変数を参照できます。新しいハッシュテーブルのメモリを申請することもできます。一般的な方法は次のとおりです:
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 メソッド名(void *pElement)
pElement は削除された要素を指します
persistent は、永続的なハッシュ テーブルであるかどうかを示すフラグです。永続データはリクエストから独立しており、RSHUTDOWN 中にログアウトされません。ただし、1 に設定すると、ht はメモリを適用するときに pemalloc() を使用する必要があります。
例: 各 PHP リクエスト ライフ サイクルでsymbol_table を初期化すると、zend_hash_init(&EG(symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0); が表示されます。
設定を解除すると、ハッシュ テーブルに格納されている対応する zval* が zval_ptr_dtor() に送信されて破棄されます。
人口:
ハッシュ テーブルにデータを挿入および更新するための主な関数は 4 つあります:
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);最初の 2 つの関数は、php の $foo['bar'] = 'barvalue' などの文字列インデックスを持つデータをハッシュテーブルに追加し、その後拡張子に追加します。
zend_hash_add(fooHashTbl, "bar", sizeof("bar"), &barZval, sizeof(zval*), NULL);
対応するキー値と対応するテーブル値がハッシュテーブルに追加されます。
追加と更新の唯一の違いは、キーがすでに存在する場合、追加は失敗することです。
最後の 2 つの関数は、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 パラメータは、新しく追加された要素のアドレス値を保存するために使用できます。
を見つけてください 一般的に、ハッシュ テーブル内のデータを取得するには 2 つの方法があります:
りー
これは、以下の例でより明確に見ることができます:
int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength, void **pData); int zend_hash_index_find(HashTable *ht, ulong h, void **pData);ハッシュ テーブルの値を取得することに加えて、いくつかの要素の存在を知ることがより重要な場合もあります:
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 */ }
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里面。 }
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中索引位置相同的元素会被替换掉,而其他的元素则会被保留。
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); }