Hello, World! するだけの PHP Extension を作る
来月開催される ZendEngine勉強会@東京に向けて, 少しでも予習しておこうと思い, 以下のページを参考にやってみました.
PHP Extension を作ろう第1回 - まずは Hello World DSAS開発者の部屋
しかし, この記事そのままのやり方では上手くいきませんでした.
書かれたのが 2006 年ということもあり, 一部情報が古くなっている部分があるようです.
そこで, 上記の記事を参考にしつつ, 調べてまとめてみました.
0. 前提とする環境
私は以下の環境で検証を行いました.
LAMP 環境は tasksel コマンドで構築したものです.
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=10.10 DISTRIB_CODENAME=maverick DISTRIB_DESCRIPTION="Ubuntu 10.10" $ php -v PHP 5.3.3-1ubuntu9.3 with Suhosin-Patch (cli) (built: Jan 12 2011 16:08:14) Copyright (c) 1997-2009 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies $ apache2ctl -v Server version: Apache/2.2.16 (Ubuntu) Server built: Nov 18 2010 21:17:29 $ gcc -v Using built-in specs. Target: i686-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.4-14ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu Thread model: posix gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
また, phpize というコマンドが必要になるのでインストールしておきましょう.
Debian/Ubuntu であれば以下のコマンドでインストールできると思います.
$ sudo apt-get install php5-dev
1. PHP のソースコードを入手する
Zend API を利用するためのヘッダファイルや, PHP Extension を作るためのスケルトンを生成するプログラム等を手に入れるのが目的です.
2011/01/31 現在の最新版は 5.3.5 ですが, ローカルにインストールされているバージョンに合わせて, 私は 5.3.3 をダウンロードしています.
- PHP: Downloads 最新バージョンのソースコード
- PHP: Releases 以前のバージョンのソースコード (5.3.3 はこちら)
適当なディレクトリにダウンロードして, 展開します.
$ sudo tar xvzf php-5.3.3.tar.gz -C /path/to/somewhere
2. PHP Extension のスケルトンを生成する
コマンド一発で, スケルトン (ひな形) のファイル群を生成してくれます.
これで生成されたファイルをもとに, PHP Extension を作っていきます.
$ cd /path/to/someshere/php-5.3.3/ext $ ./ext_skel --extname=helloworld Creating directory helloworld Creating basic files: config.m4 config.w32 .cvsignore helloworld.c php_helloworld.h CREDITS EXPERIMENTAL tests/001.phpt helloworld.php [done]. To use your new extension, you will have to execute the following steps: 1. $ cd .. 2. $ vi ext/helloworld/config.m4 3. $ ./buildconf 4. $ ./configure --[with|enable]-helloworld 5. $ make 6. $ ./php -f ext/helloworld/helloworld.php 7. $ vi ext/helloworld/helloworld.c 8. $ make Repeat steps 3-6 until you are satisfied with ext/helloworld/config.m4 and step 6 confirms that your module is compiled into PHP. Then, start writing code and repeat the last two steps as often as necessary.
3. config.m4 を編集する
以下の箇所のコメントアウトを外します.
この辺は元の記事に従っています.
diff --git a/config.m4 b/config.m4 | |
index bfd3203..76367b3 100644 | |
--- a/config.m4 | |
+++ b/config.m4 | |
@@ -13,9 +13,9 @@ dnl [ --with-helloworld Include helloworld support]) | |
dnl Otherwise use enable: | |
-dnl PHP_ARG_ENABLE(helloworld, whether to enable helloworld support, | |
-dnl Make sure that the comment is aligned: | |
-dnl [ --enable-helloworld Enable helloworld support]) | |
+PHP_ARG_ENABLE(helloworld, whether to enable helloworld support, | |
+Make sure that the comment is aligned: | |
+[ --enable-helloworld Enable helloworld support]) | |
if test "$PHP_HELLOWORLD" != "no"; then | |
dnl Write more examples of tests here... |
4. helloworld() 関数を追加する
php_helloworld.h を以下のように編集します.
diff --git a/php_helloworld.h b/php_helloworld.h | |
index 1e6ca1f..d9feaad 100644 | |
--- a/php_helloworld.h | |
+++ b/php_helloworld.h | |
@@ -43,6 +43,7 @@ PHP_RSHUTDOWN_FUNCTION(helloworld); | |
PHP_MINFO_FUNCTION(helloworld); | |
PHP_FUNCTION(confirm_helloworld_compiled); /* For testing, remove later. */ | |
+PHP_FUNCTION(helloworld); | |
/* | |
Declare any global variables you may need between the BEGIN |
そして, helloworld.c も編集します.
diff --git a/helloworld.c b/helloworld.c | |
index 6702143..8d65619 100644 | |
--- a/helloworld.c | |
+++ b/helloworld.c | |
@@ -40,6 +40,7 @@ static int le_helloworld; | |
*/ | |
const zend_function_entry helloworld_functions[] = { | |
PHP_FE(confirm_helloworld_compiled, NULL) /* For testing, remove later. */ | |
+ PHP_FE(helloworld, NULL) | |
{NULL, NULL, NULL} /* Must be the last line in helloworld_functions[] */ | |
}; | |
/* }}} */ | |
@@ -165,6 +166,13 @@ PHP_FUNCTION(confirm_helloworld_compiled) | |
RETURN_STRINGL(strg, len, 0); | |
} | |
/* }}} */ | |
+ | |
+PHP_FUNCTION(helloworld) | |
+{ | |
+ printf("Hello, World!\n"); | |
+ return; | |
+} | |
+ | |
/* The previous line is meant for vim and emacs, so it can correctly fold and | |
unfold functions in source code. See the corresponding marks just before | |
function definition, where the functions purpose is also documented. Please |
5. ビルドする
以下のコマンドを, helloworld.c 等のファイルのあるディレクトリ上で実行します.
$ phpize $ ./configure $ make
これでビルドは完了です.
ここで, 今ビルドした Extension を読み込んで試してみたいところですが, ここからが問題です.
元の記事のとおりではうまく読み込むことができないので, さらにいくつかの設定が必要です.
6. dl() 関数を有効にする
dl() 関数とは, PHP Extension を Dynamic Load (動的読込) を行う関数ですが, デフォルトでは使えないように設定されているようです.
無効化されている場合は, dl() の呼び出し時に以下のようなエラーメッセージが表示されます.
$ php -a Interactive shell php > dl("helloworld.so"); PHP Warning: dl(): Dynamically loaded extensions aren't enabled in php shell code on line 1
php.ini で dl() 関数を有効化しましょう.
CLI 版 PHP の php.ini のパスは以下のようにして調べられます.
Debian/Ubuntu の場合は, デフォルトでは Apache2 用と CLI 用とで php.ini が分かれているので注意しましょう.
$ php -i | grep "Configuration File" Configuration File (php.ini) Path => /etc/php5/cli Loaded Configuration File => /etc/php5/cli/php.ini
Apache2 の mod_php の設定ファイルのパスは, PHP スクリプト内で phpinfo() 関数を呼び出し, Web ブラウザ上で確認できます.
php.ini のパスを確認したら, 以下のように編集しましょう.
enable_dl = On
7. helloworld.so を extension_dir に置く
元の記事では, dl() 関数に相対パスを渡して helloworld.so を読み込んでいますが, セキュリティ上の理由からか, ファイル名しか指定できなくなっているようです.
ディレクトリ名を指定した場合, 以下のようなエラーメッセージが表示されます.
$ php -a Interactive shell php > dl('module/helloworld.so'); PHP Warning: dl(): Temporary module name should contain only filename in php shell code on line 1
php.ini で設定した extension_dir に helloworld.so を置く必要があります.
extension_dir は以下のように調べられます.
$ php -i | grep extension_dir extension_dir => /usr/lib/php5/20090626+lfs => /usr/lib/php5/20090626+lfs
mod_php の場合は, やはり phpinfo() をブラウザ上から確かめる必要があります.
ビルドする度にコピーするのでは面倒なので, ここではシンボリックリンクを使用しています.
$ sudo ln -s /path/to/php-5.3.3/ext/helloworld/modules/helloworld.so /usr/lib/php5/20090626+lfs/
8. 実行する
$ php -a Interactive shell php > dl('helloworld.so'); php > helloworld(); Hello, World!
見事, helloworld() 関数を実行することができました !
今回はここまでです.
これを元に, 私も何か PHP Extension を作っていきたいと思います.
See also
- PHP Extension を作ろう第1回 - まずは Hello World DSAS開発者の部屋
元ネタはほぼこの記事です. - PHP: dl - Manual
dl() 関数のマニュアル - PHP: 実行時設定 - Manual
enable_dl についてのマニュアル.