PHPエクステンションで基本的な関数を作成する

CodeZine / 2013年9月9日 14時0分

 本連載では、PHPエクステンションを初めて作成する方に向けて、全体の流れや、関数やクラスの作成方法、より実践的にライブラリをPHPエクステンションとして利用する方法を紹介します。今回は関数の作成方法を中心に、引数や戻り値の扱い方など、関数を作成するための基本的な仕組みを紹介します。

■対象読者

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

■必要な環境

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

■関数追加の流れ

 最初にhellofunc_null()という関数を定義していき、関数の定義を追加する流れを復習します。このhellofunc_null()という関数は、仮にPHPで記述するのであれば、以下のような関数になります。

PHPで実装したときのhellofunc_null()関数
function hellofunc_null(){ return NULL; }
 ここでは、この関数をエクステンションとして定義していきます。その際に主に3か所に記述を行います。

hellofunc_null.cの抜粋(関数定義の追加)
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の最後に記述します。

hellofunc.cの抜粋(hellofunc_null関数の実装)
PHP_FUNCTION(hellofunc_null){ RETURN_NULL(); }
 これで以下のようにビルドを行えば、一通りの流れが終了したことになります。

ビルドの実行(config.shの中身)
$ ./configure \ --enable-hellofunc \ --enable-debug \ --with-php-config=/usr/local/php-ext/bin/php-config $ make
 このコードはサンプルコード内にconfig.shというスクリプトで用意してありますので、そちらを使用することもできます。

 また、実際にビルドが終了しましたら、以下のコマンドで実行することができます。

実行方法(exec.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で記述すると、以下のようになります。

PHPでhellofunc_str()を実装した場合
function hellofunc_str($arg){ return sprintf("your input is [%s]",$arg); }
 PHPでは簡単に記述できた内容も、エクステンションとして記述する場合には少々複雑になります。マクロがあると少々理解し難いため、マクロを展開したものを下記に示します。

hellofunc_str関数をマクロ展開したときの結果
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への操作をいろいろと行っていることが分かります。

 また、実際の引数としてどのような変数名が使われているかも確認する必要があります。変数の意味は現時点ではそれほど重要ではありませんが、実際に自分が記述するコード内の変数名と重複してしまうと思わぬエラーに悩まされることがあります。

 全体の内容について大まかに分かったところで、続いて型の説明と引数の扱い方、戻り値の扱い方を示します。



CodeZine

トピックスRSS

ランキング