配列などを使った実践的な関数を作成する

CodeZine / 2013年10月1日 14時0分

 本連載では、PHPエクステンションの作成方法を紹介します。前回は基本的な関数の作成方法を紹介しましたが、今回はより実践的な関数の作成方法として、配列の扱い方や、複数の引数の扱い方、また、関数の引数情報の定義し、リフレクションなどで関数情報を扱えるようにします。

■対象読者

 PHPでの基本構文を理解している方で、PHPエクステンションに興味がある方、さらに深くPHPを知りたい方で、C言語の基本的な構文を理解している方を対象としています。

■必要な環境

 この記事では、PHP 5.4を使用し、Linux環境で確認を行っています。インストール方法は初回を参照してください。

■配列の扱い方

 配列操作はPHPでは非常に基本的な内容ではありますが、エクステンション側で配列を扱うようになると、各配列内のデータについてはこれまでのように引数の型チェックができないため、自前で型の操作を行う必要があります。また、配列を引数に取る場合や、配列を返す場合にはこれまでの記述方法とは多少異なり、マクロだけでは処理が行えません。しかも、配列といってもPHPでは連想配列($array["key"])とリスト配列($array[0])のような型がありますので、それらに応じて処理をする必要があり、これまでよりも難しくなってきます。

●配列の引数

 連想配列を引数にする関数の例を以下に示します。

連想配列を引数とする関数(hellofunc.cの抜粋)
PHP_FUNCTION(hellofunc_arg_array){ zval *hash; if (zend_parse_parameters( // ……(1) ZEND_NUM_ARGS() TSRMLS_CC, "a" , &hash) == FAILURE ){ return; } zval **val; // 配列から"key1"の値を見つける if(zend_hash_find(HASH_OF(hash), // ……(2) "key1",sizeof("key1"),(void**)&val) == SUCCESS ){ int type,len; char *strg; type = Z_TYPE_PP(val); // ……(3) if(type == IS_STRING){ // ……(4) len = spprintf(&strg,0,"found value : [%s]",Z_STRVAL_PP(val)); } else{ len = spprintf(&strg,0,"found invalid value type : [%d]",type); } RETURN_STRINGL(strg,len,0); } RETURN_NULL(); }
 (1)のzend_parse_parameters()には"a"を指定して配列を取得しますが、実際にはzval型であるためにそのままでは配列型として扱えません。そこで、(2)HASH_OFマクロを用いて連想配列(Hashtable)型としてアクセスします。また、(2)のzend_hash_find()関数を用いて"key1"の値を見つけます。見つけた値の型もzval型ですので、(3)のZ_TYPE_PPマクロを使って実際の型を調べます。(4)のケースでは型が文字列型(IS_STRING)であるか調べています。このように、型を調べる処理が配列を扱うと多くなってしまいます。また、他の型をチェックするには以下のマクロを使います。

zval変数の型(Zend/zend.hの抜粋)
#define IS_NULL 0 #define IS_LONG 1 #define IS_DOUBLE 2 #define IS_BOOL 3 #define IS_ARRAY 4 #define IS_OBJECT 5 #define IS_STRING 6 #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_ARRAY 9 #define IS_CALLABLE 10
 続いて、リスト配列が引数の場合の例を示します。ほぼ先ほどと同様ですが、(1)zend_hash_num_elements()関数でリストのサイズを取得します。(2)では固定的に2番目の値を取得して処理をしていますが、これらの部分で配列の数にあわせてインデックスで操作していく必要があります。

リスト配列を引数とする関数(hellofunc.cの抜粋)
PHP_FUNCTION(hellofunc_arg_list){ zval *hash; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a" , &hash) == FAILURE ){ return; } int len,type; char *strg; HashTable *htable = HASH_OF(hash); zval **val; uint size = zend_hash_num_elements(htable); // ……(1) // 2個目の値(0から数えるので、"1"番目になる) if( size >= 1 && zend_hash_index_find(htable,1,(void**)&val) == SUCCESS){ // ……(2) type = Z_TYPE_PP(val); : (省略) } RETURN_NULL(); }
●配列の戻り値

 引数の場合と同様に、連想配列を返す関数とリスト配列を返す関数を作成します。以下のような値を連想配列として返す関数を作成します。

戻り値として返す連想配列
array(6) { ["key_str"]=> string(4) "val1" ["key_int"]=> int(100) ["key_bool"]=> bool(false) ["key_double"]=> float(0.3) ["key_null"]=> NULL ["key_array"]=> array(1) { ["key_str2"]=> string(4) "val2" } }
 この連想配列では値の型はすべて一致していませんし、また、key_arrayという2次元配列になっている部分があります。

連想配列を返す関数(hellofunc.cの抜粋)
PHP_FUNCTION(hellofunc_return_array){ array_init(return_value); // ……(1) add_assoc_string(return_value,"key_str","val1",1); // ……(2) add_assoc_long(return_value,"key_int",100); // ……(3) add_assoc_bool(return_value,"key_bool",0); // ……(4) add_assoc_double(return_value,"key_double",0.3); // ……(5) add_assoc_null(return_value,"key_null"); // ……(6) // 配列の中にさらに配列を入れる zval *val_array; MAKE_STD_ZVAL(val_array); // ……(7) array_init(val_array); add_assoc_string(val_array,"key_str2","val2",1); add_assoc_zval(return_value,"key_array",val_array); // ……(8) }
 配列を戻り値として扱うには(1)のarray_init()関数を使って初期化を行います。続いて(2)から(6)のようにadd_assoc_*関数を使ってキーと値を追加していきます。次に、配列の中に配列を設定する方法を紹介します。まず、(7)のMAKE_STD_ZVAL()マクロを使ってzval変数を初期化します。続いて、(1)と同様にarray_init()関数で初期化したらadd_assoc_*関数を使って配列へデータを追加します。戻り値に配列を追加するには、(8)add_assoc_zval()関数を使います。

 次にリスト配列を返す関数を作成します。こちらは先ほどと違い、値を設定するのにadd_index_*関数を使う部分のみが異なります。

リスト配列を返す関数(hellofunc.cの抜粋)
PHP_FUNCTION(hellofunc_return_list){ array_init(return_value); add_index_string(return_value,0,"val1",1); add_index_long(return_value,1,100); add_index_bool(return_value,2,1); add_index_double(return_value,3,0.5); add_index_null(return_value,4); }
 上記例で示したadd_assoc_*やadd_index_*の詳しい情報を知りたい方はZend/zend_API.hの定義を参照する必要があります。



CodeZine

トピックスRSS

ランキング