SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PHPエクステンションの作り方

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

PHPエクステンションの作り方 第3回

  • このエントリーをはてなブックマークに追加

 本連載では、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の定義を参照する必要があります。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
関数を引数に取る方法

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
PHPエクステンションの作り方連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/7385 2013/10/01 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング