ゼンド ハッシュ 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

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説