ゼンド ハッシュ API (Zend Hash API) の基本的な使い方
PHP エクステンションの開発環境では、ハッシュテーブルを簡単に利用するための、便利な API が用意されています。
Zend Hash API
ハッシュテーブルはキーと値のペアを格納するデータ構造で、あるキーをもとに素早く値を取得する場合に適しています。
PHP エクステンションの開発環境では、ハッシュテーブルを簡単に利用し、それを PHP スクリプトでも利用可能とするための、 大変便利な Zend Hash API (ゼンドハッシュ API) が用意されています。
この資料では Zend Hash API を用いて PHP エクステンションのコード内でハッシュテーブルを作成し、それに値を格納、 取得、値の削除、クリーンアップ等を行い、基本的な動作を確認します。
コードの書き方
ハッシュテーブルのデータ型
ハッシュテーブルは HashTable 型として作成されます。これは zend/zend_hash.h にて定義されています。
typedef struct _hashtable { uint nTableSize; uint nTableMask; uint nNumOfElements; ulong nNextFreeElement; Bucket *pInternalPointer; /* Used for element traversal */ Bucket *pListHead; Bucket *pListTail; Bucket **arBuckets; dtor_func_t pDestructor; zend_bool persistent; unsigned char nApplyCount; zend_bool bApplyProtection; #if ZEND_DEBUG int inconsistent; #endif } HashTable;
ハッシュテーブルの初期化
ハッシュテーブルのためのメモリブロックの割当ては、emalloc(sizeof(HashTable)) でも良いのですが、 通常は ALLOC_HASHTABLE マクロで割り当てます。また解放は FREE_HASHTABLE マクロで行います。
ハッシュテーブルを初期化するための関数として、zend_hash_init 関数 (実際はマクロ) が用意されています。
int zend_hash_init( HashTable* ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent)
ht はハッシュテーブルへのポインタ。nSize はハッシュテーブルサイズ。pHashFunction は今は使われていないので常に NULL。 pDestructor は要素が削除されるときに呼び出される関数へのポインタ。persistent は永続性フラグ (pemalloc で割り当てるかどうか) です。
ちなみに、nSize は 2 の累乗数を指定します。もしそうでない正数を指定した場合、指定した値より大きい次の 2 の類乗数が内部で使われます。 例えば 10 を指定すると内部で 16 (=2の4乗) が使われます。尚、最小値は 8 です。もし 0 を nSize として渡すと、ハッシュテーブルサイズは 8 になります。
pDestructor は要素が削除されるときに呼び出される関数です。その関数のプロトタイプは次の通りです。
void destructor_func( void* pElement );
ハッシュテーブルへの値の格納
ハッシュテーブルに値を格納する関数 (マクロ) は多数ありますが、ここでは次の関数を取り上げます。
int zend_hash_add( HashTable* ht, char* arKey, uint nKeyLen, void** pData, uint nDataSize, void* pDest)
キーは文字列ですが、pData は zval 値を渡すことに注意してください。具体的には下記のコードを見てください。
ハッシュテーブルからの値の取得
ハッシュテーブルからキーを元にして zval 値を取得するには zend_hash_find 関数を利用します。
int zend_hash_find( HashTable* ht, char* arKey, uint nKeyLength, void** pData)
ハッシュテーブルに格納された値の削除
ハッシュテーブルからキーを元にして zval 値を削除するには zend_hash_del 関数を利用します。
int zend_hash_del( HashTable* ht, char* arKey, uint nKeyLen)
ハッシュテーブルの破棄・クリーンアップ
ハッシュテーブルの破棄は zend_hash_destroy 関数で行います。また、 メモリブロックの解放は FREE_HASHTABLE マクロで行います。
実行例
上記を踏まえ、ハッシュテーブルを作成し、初期化して、値を割当て (キーは "a")、値を削除、クリーンアップ、 という一連の流れを行うサンプルコードを書いてみます。
基本的な PHP エクステンションの開発環境の作り方、書き方、ビルド方法については、単純な PHP エクステンションの開発方法 を参考にしてください。
#include "php_keicodetest.h" zend_function_entry keicodetest_ext_functions[] = { PHP_FE(keicode_hash1, NULL) {NULL, NULL, NULL} }; zend_module_entry keicodetest_ext_module_entry = { STANDARD_MODULE_HEADER, "keicode.com Test PHP Extension", keicodetest_ext_functions, NULL, NULL, NULL, NULL, NULL, "1.0", STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(keicodetest_ext); void destructor_func( void* p ) { php_printf( "{%s} ", Z_STRVAL_PP((zval**) p) ); } void test_hashtable() { HashTable* ht = NULL; ALLOC_HASHTABLE( ht ); // HashTable の初期化 zend_hash_init( ht, 50, NULL, destructor_func, 0); // Zval 値の作成 zval* z; MAKE_STD_ZVAL(z); ZVAL_STRING( z, "Hello, world", 1 ); // ハッシュテーブルに値を格納 zend_hash_add( ht, "a", sizeof("a"), &z, sizeof(zval*), NULL); php_printf( "Getting the value...[" ); if( zend_hash_exists( ht, "a", sizeof("a") ) ) { // HashTable から値を取得 zval** p; zend_hash_find( ht, "a", sizeof("a"), (void**)&p ); // 結果を出力 php_printf( "%s", Z_STRVAL_PP(p) ); } else { php_printf( "Not found" ); } php_printf( "] OK\n" ); // 値を削除 php_printf( "Deleting..." ); zend_hash_del( ht, "a", sizeof("a") ); php_printf( "OK\n" ); php_printf( "Checking..." ); if( zend_hash_exists( ht, "a", sizeof("a") ) ) { php_printf( "[Still Found]" ); } else { php_printf( "[Not found]" ); } php_printf( " OK\n" ); // クリーンアップ zend_hash_destroy( ht ); FREE_HASHTABLE( ht ); } ZEND_FUNCTION(keicode_hash1) { test_hashtable(); }
上記のコードを実行すると、以下の出力がえられます。
> php test.php Getting the value...[Hello, world] OK Deleting...{Hello, world} OK Checking...[Not found] OK