ビルドする
次にビルドを行います。まず、先ほど編集したconfig.m4をconfigureオプションとして有効になるようphpizeコマンドを利用します。
$ /usr/local/php-ext/bin/phpize
これはダイナミックモジュールを作成する場合に使用するコマンドです。実行すると、そのエクステンションのためだけのconfigureスクリプトを作成することができます。次に実際にconfigureスクリプトのhelpを見て、helloworldエクステンションのオプションが作成されたか確認します。
$ ./configure --help | grep helloworld --enable-helloworld Enable helloworld support
オプションが使えることを確認したら、PHPをインストールしたときと同じような手順でビルドしていきます。ただし、複数のPHPをインストールしている時には特に注意すべきことがあります。それは何処にインストールしてあるPHPを使ってPHPをコンパイルするかです。これに関しては、--with-php-configを使って指定できるので、今回のエクステンション作成用にインストールしたPHPを使うように指定します。
$ ./configure --enable-helloworld --enable-debug --with-php-config=/usr/local/php-ext/bin/php-config $ make
--with-php-configを指定しない場合、筆者の環境では以下のようなエラーがmake時に表示されました。
ext/helloworld/helloworld.c:43: error: 'PHP_FE_END' undeclared here (not in a function)
これは必要なマクロ定義がないことによるエラーです。RPMで既に入っていたPHP 5.3.3のヘッダファイルには、そのような定義がないことによってエラーが発生しています。このような問題はエクステンション開発に慣れてくると間違いにすぐ気がつくものですが、不慣れなうちは対処が難しいものです。面倒ではありますが、--with-php-configを指定する癖を付けましょう。
ビルドに成功すると、以下のファイルが作成されています。
modules/helloworld.so
実行してみる
では、実際に実行してみましょう。
$ /usr/local/php-ext/bin/php -d extension=modules/helloworld.so -f helloworld.php
まずは、-dオプションを使ってビルドしたエクステンションを指定します。これでmake installを実行しなくても、作成したエクステンションを指定した実行ができます。実行結果は下記のとおりです。
Functions available in the test extension: confirm_helloworld_compiled Congratulations! You have successfully modified ext/helloworld/config.m4. Module helloworld is now compiled into PHP.
確認スクリプトの内容を見る
実際にコードを見る前に、確認スクリプトが何をやっているのか確認してみます。
// 省略 $module = 'helloworld'; $functions = get_extension_funcs($module); echo "Functions available in the test extension:$br\n"; foreach($functions as $func) { echo $func."$br\n"; } echo "$br\n"; $function = 'confirm_' . $module . '_compiled'; if (extension_loaded($module)) { $str = $function($module); } else { $str = "Module $module is not compiled into PHP"; } echo "$str\n";
8行目から12行目では、helloworldエクステンション内で定義されている関数名一覧を取得しています。先ほどの実行結果では、該当の部分として以下のように出力されました。
Functions available in the test extension: confirm_helloworld_compiled
つまりhelloworldエクステンションには、confirm_helloworld_compiled関数が定義されていることがわかります。次に、16行目ではその関数を実行しています。以下のコードを実行していることと同様の処理を行っています。
confirm_helloworld_compiled("helloworld");
この処理の実行結果は以下のようになります。
Congratulations! You have successfully modified ext/helloworld/config.m4. Module helloworld is now compiled into PHP.
この内容を踏まえ、続いて作成されたhelloworld.cの中身を見ていきます。
C言語側のソースを見る
helloworld.c側でconfirm_helloworld_compiledの記述がされている部分を探すと、22行目に以下のようなコードがあり、関数を定義している部分だと推測がつきます。
const zend_function_entry helloworld_functions[] = { PHP_FE(confirm_helloworld_compiled, NULL) /* For testing, remove later. */ PHP_FE_END /* Must be the last line in helloworld_functions[] */ };
さらに探してみますと、以下のような定義が見つかり、関数の中身を定義している部分だと推測がつきます。
PHP_FUNCTION(confirm_helloworld_compiled) { // 省略 }
この内容に沿って新たに関数を定義していけば、問題なさそうだと判断できるのではないでしょうか。ただしPHPに限らず、C言語でモジュール開発などを行うとマクロが多用されています。従ってC言語といえども、参考書に載っているようなC言語の記述方法とは異なりますが、ある程度、ルールとしきたりと割り切って開発を進めていく必要があります。
どうしても中身を知りたい場合は、マクロを展開した後のコードも参照する必要があります。その場合は、以下のようにgccのマクロ展開オプションなどを使用し、実際のマクロ展開後のコードを眺めることはできます。
$ gcc -E -I /usr/local/php-ext/include/php/Zend/ -I /usr/local/php-ext/include/php/main/ helloworld.c
ただし、マクロの展開後は人が読むことを考慮したものではありません。まずは、マクロのまま理解するようにしてください。
エクステンションの全体の構造
エクステンションの全体の構造を定義しているのが、zend_module_entry構造体です。この構造体にエクステンションの動作を定義しています。今回のサンプルでは以下のような定義がされています。
zend_module_entry helloworld_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "helloworld", helloworld_functions, PHP_MINIT(helloworld), PHP_MSHUTDOWN(helloworld), PHP_RINIT(helloworld), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(helloworld), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(helloworld), #if ZEND_MODULE_API_NO >= 20010901 "0.1", /* Replace with version number for your extension */ #endif STANDARD_MODULE_PROPERTIES };
この構造体の関係と、実際にコード内で定義する場所を図に示します。
このzend_module_entry構造体の定義はそのままに、当てはまる部分にコードを追記していけば、簡単なエクステンションは作成できるようになっています。
最後に
今回はPHPエクステンションを作成しビルドから実行までの流れを確認し、そしてそのソースを通じて構造と関数の定義するための手順を紹介しました。ここまでの内容を参考に、自分が興味があるエクステンションのコードを読むことで、より一層の理解が深まると思います。次回は、今回紹介できなかった様々なパラメータの取得方法や戻り値の作成方法などを通じて、より本格的な関数の作成方法を紹介します。