対象読者
PHPでの基本構文を理解していてPHPエクステンションに興味がある方、C言語の基本的な構文を理解していてさらに深くPHPを知りたい方を対象としています。
必要な環境
この記事では、PHP 5.4を使用し、Linux環境で確認を行っています。インストール方法については、前回の記事を参照してください。
関数追加の流れ
最初にhellofunc_null()という関数を定義していき、関数の定義を追加する流れを復習します。このhellofunc_null()という関数は、仮にPHPで記述するのであれば、以下のような関数になります。
function hellofunc_null(){ return NULL; }
ここでは、この関数をエクステンションとして定義していきます。その際に主に3か所に記述を行います。
PHP_FUNCTION(hellofunc_null); // ……(1) const zend_function_entry hellofunc_functions[] = { PHP_FE(hellofunc_null,NULL) // ……(2) : PHP_FE_END //……(3) };
(1)PHP_FUNCTIONマクロを用いてhellofunc_null()関数がphpの関数として使えることを定義します。C言語なので、後で定義する関数があることを示す必要がありますから、このように一番最初に定義だけを記述しています。この定義はヘッダファイル(php_hellofunc.h)に記述してもよいですが、説明上の関係もあり今回はすべてhellofunc.cファイルに記述します。次に、(2)PHP_FEマクロを用いてhellofunc_null関数をzend_function_entry構造体に定義します。また、(3)PHP_FE_ENDマクロは定義の終了をしめすマクロになっており、このマクロを必ず最後に記述する必要があります。このマクロはPHPのバージョンによっては定義されていない場合がありますので、バージョンには注意してください。これで、PHPから関数を呼び出すための準備が整いました。
次に、関数の実体の実装を行います。先ほどのPHPでの実装を、エクステンションでの記述に書き直すと以下のようになります。この定義をhellofunc.cの最後に記述します。
PHP_FUNCTION(hellofunc_null){ RETURN_NULL(); }
これで以下のようにビルドを行えば、一通りの流れが終了したことになります。
$ ./configure \ --enable-hellofunc \ --enable-debug \ --with-php-config=/usr/local/php-ext/bin/php-config $ make
このコードはサンプルコード内にconfig.shというスクリプトで用意してありますので、そちらを使用することもできます。
また、実際にビルドが終了しましたら、以下のコマンドで実行することができます。
$ /usr/local/php-ext/bin/php -d extension=modules/hellofunc.so -f hellofunc.php
インストールされているディレクトリなどは、前回のインストール内容のとおりになっていますのでご注意ください。
関数の引数と戻り値の扱いについて
続いて、文字列型の引数と戻り値を例に実際の関数定義がどのようになっているかをより深く見ていきます。以下の関数定義は、1つの文字列の引数を取り、その入力を含んだ文字列を返す関数です。
PHP_FUNCTION(hellofunc_str){ char *arg = NULL; int arg_len, len; char *strg; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s" , &arg, &arg_len) == FAILURE ){ return; } len = spprintf(&strg,0,"your input is [%s]",arg); RETURN_STRINGL(strg,len,0); }
同じような関数をPHPで記述すると、以下のようになります。
function hellofunc_str($arg){ return sprintf("your input is [%s]",$arg); }
PHPでは簡単に記述できた内容も、エクステンションとして記述する場合には少々複雑になります。マクロがあると少々理解し難いため、マクロを展開したものを下記に示します。
void zif_hellofunc_str(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used ){ char *arg = ((void *)0); int arg_len, len; char *strg; if (zend_parse_parameters((ht) , "s" , &arg, &arg_len) == -1 ){ return; } len = spprintf(&strg,0,"your input is [%s]",arg); { do { const char *__s=(strg); int __l=len; zval *__z = (return_value); (*__z).value.str.len = __l; (*__z).value.str.val = (0?_estrndup((__s), (__l) , "hellofunc.c", 252 , ((void *)0), 0):(char*)__s); (*__z).type = 6; } while (0); return; }; }
ここで引数と戻り値がどのように扱われているかを理解することは非常に重要です。というのも、PHPは型が柔軟な言語仕様であるため、その実装をC言語として行う場合はそのギャップが存在します。それらを埋めるために、PHPのエクステンション開発ではさまざまなマクロを使うようになるのです。
展開されたマクロを見ても引数に文字列が指定されておらず、また、戻り値もありません。このような形がPHPエクステンション特有の実装形式です。
引数はzend_parse_paramters()関数を通じて取得することになります。また、戻り値はreturn_valueという変数がPHPから実行された時には戻り値です。上記例ではRETURN_STRINGL関数(実際にはマクロになります)がその処理を行っており、このマクロ展開でもreturn_valueへの操作をいろいろと行っていることが分かります。
また、実際の引数としてどのような変数名が使われているかも確認する必要があります。変数の意味は現時点ではそれほど重要ではありませんが、実際に自分が記述するコード内の変数名と重複してしまうと思わぬエラーに悩まされることがあります。
全体の内容について大まかに分かったところで、続いて型の説明と引数の扱い方、戻り値の扱い方を示します。