モノノフ日記

普通の日記です

Boost.phpを触ってみた

今年のPHPカンファレンスジャパンで個人的に一番面白かったid:moriyoshiの発表に触発されてBoost.phpを試してみました。

試した環境

debian lenny

githubからBoost.phpを取得する

とりあえず最新版だけ欲しい人は--depthオプション使うとすぐダウンロードも終わります。moriyoshi/boost.php · GitHub

$ git clone http://github.com/moriyoshi/boost.php.git

Boostのソースコードを取得する

動作させるのにBoostが必要なのでダウンロードして適当な所に展開。パッケージに含まれるヘッダファイルだけ必要なのでビルドは不要。yumやaptでさっくり入れてもよいかと思います。

添付しているテストコードをビルドしてみる

githubから落としたBoost.phpのコードにテストコードが付いてるのでそれらをビルドしてみます。
自分の環境ではMakefileのBoostのソースコードディレクトリを参照してるところを書き換えただけで動きました。php-config, phpコマンドに実行パスが通ってあればすんなり動くと思います。

Makefileの33〜36行目あたり
# This one is also to stay compatible with initial makefile
ifneq ($(wildcard $(HOME)/src/boost_1_40_0),)
CPPFLAGS+=-I$(HOME)/src/boost_1_40_0
endif

作成したsoファイルの動作を確認してみる

ビルドに成功するとtestsディレクトリにm001〜008の名前でsoファイルが作成されているはずです。ちゃんと動くか試してみます。m002.cppがadd,subという関数を定義しているサンプルなのでこのソースで試してみます。
m002.phpという名前のサンプルPHPを作成してコマンドラインからライブラリを読み込んで実行させます。

m002.php
<?php
echo add(1 + 2).PHP_EOL;
echo sub(5 - 3).PHP_EOL;
サンプルコード実行
$ cd tests
$ php -dextension_dir=`pwd` -dextension=m002.so m002.php
PHP Warning:  PHP Startup: Unable to load dynamic library '/home/Kiske/work/boost.php/tests/gd.so' - /home/Kiske/work/boost.php/tests/gd.so: cannot open shared object file: No such file or directory in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/home/Kiske/work/boost.php/tests/pdo.so' - /home/Kiske/work/boost.php/tests/pdo.so: cannot open shared object file: No such file or directory in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/home/Kiske/work/boost.php/tests/pdo_mysql.so' - /home/Kiske/work/boost.php/tests/pdo_mysql.so: cannot open shared object file: No such file or directory in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/home/Kiske/work/boost.php/tests/pdo_sqlite.so' - /home/Kiske/work/boost.php/tests/pdo_sqlite.so: cannot open shared object file: No such file or directory in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/home/Kiske/work/boost.php/tests/sqlite.so' - /home/Kiske/work/boost.php/tests/sqlite.so: cannot open shared object file: No such file or directory in Unknown on line 0
3
3

動作してますがなんかいっぱいWarning出てます。以前にaptで入れてたライブラリをロードしようとしてるからですね。気持ち悪いので今使わないライブラリは読み込まないようにconfファイルを編集します。/etc/php5/conf.d/ディレクトリに各ライブラリのiniファイルがあるのでそれぞれコメントアウトしました。

サンプルコード再実行
$ php -dextension_dir=`pwd` -dextension=m002.so m002.php
3
3

サンプルコードをいじって関数を作成してみる

m003.cppが2変数に入ってる文字列を結合してstrtoupperするサンプルなのでこれを改良して自前関数を作ってみることにします。何作ろうかなと考えてすぐ思いついたのがビジネスデイの徳丸さんの発表にあったhtmlspecialcharsをラッパーしたh関数のアイデアを頂くことにしました。

h関数の仕様
  1. htmlspecialcharsは関数名長いのでhに短縮
  2. 第2引数はENT_QUOTESで固定
  3. 第3引数はUTF-8で固定

サンプルコードに則って、拡張コードのファイル名はh001.cppという名前にします。ENT_QUOTESはPHPの定数で値は3なので直に指定してます。

h001.cpp
#include "boost/php/module.hpp"
#include "boost/php/function.hpp"

using namespace boost;

class h001_module
    : public php::module,
      public php::function_container<h001_module> {
public:
    class handler
        : public php::module::handler {
    public:
        handler(h001_module* mod)
            :php::module::handler(mod) {}

        php::value_ptr h(std::string a) {
            php::function htmlspecialchars("htmlspecialchars");
            return htmlspecialchars(a, 3, "UTF-8");
        }
    };
public:
    h001_module(zend_module_entry* entry)
        : php::module(entry) {
        entry->functions =
             defun("h", &handler::h);
    }
};

#define BOOST_PHP_MODULE_NAME h001
#define BOOST_PHP_MODULE_CAPITALIZED_NAME H001
#define BOOST_PHP_MODULE_VERSION "0.1"
#define BOOST_PHP_MODULE_CLASS_NAME h001_module

#include "boost/php/module_def.hpp"

Makefileソースコードを指定するあたりを書き換えます。43〜47行目あたりです。

Makefile
tests/h%.o: tests/h%.cpp
    $(CXX) -DCOMPILE_DL_M$* $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<

tests/h%.so: tests/h%.o
    $(LD) $(CXXFLAGS) $(LDFLAGS) -o $@ $^

ビルドして実際に動かしてみます。h001.phpをサンプルコードとして作成します。

h001.php
<?php var_dump(h('<script>alert("foobar")</script>')) ?>
サンプルコード実行*1
$ php -dextension_dir=`pwd` -dextension=h001.so h001.php
string(56) "&lt;script&gt;alert(&quot;hogehoge&quot;)&lt;/script&gt;"

簡単に拡張が書けちゃいますね。すごい。もっと中身を理解したら色々できそうです。

*1:変換されちゃうので&を全角にしてます