PHPエクステンションをビルドする

さて、PHP自体のコンパイルはみてきましたので、次はエクステンションのコンパイルへとすすみましょう。エクステンションのビルドがどのように行われ、指定可能なオプションによってどのように変わるかをみていきます。

エクステンションのロード

前回のセクションでふれたように、PHPエクステンションはPHPバイナリへと静的にビルドされるか、共有オブジェクト( .so ) へとコンパイルするかのどちらかになります。組み込みのエクステンションの多くにとって静的な関連付けがデフォルトの動作ですが、 ./configure--enable-EXTNAME=shared あるいは --with-EXTNAME=shared と明示的に指定することで共有オブジェクトを生成することも可能です。

静的なエクステンションはそのままで有効ですが、共有オブジェクトはiniファイルで extension あるいは zend_extension を使ってロードする必要があります。[1] どちらのオプションも .so ファイルの絶対パスか extension_dir の設定からの相対パスを受け取ります( zend_extension での相対パスはPHP 5.5からのみ有効で、それ以前は絶対パスでなければなりません)。

例として、次のようなconfigureのコマンドをみてみましょう。

~/php-src> ./configure --prefix=$HOME/myphp \
                     --enable-debug --enable-maintainer-zts \
                     --enable-opcache --with-gmp=shared

この場合、opcacheのエクステンションGMPのエクステンションがコンパイルされて共有オブジェクトが modules/ に格納されます。extension_dir を変更するか、絶対パスを渡すことでロードすることができます。

~/php-src> sapi/cli/php -dzend_extension=`pwd`/modules/opcache.so \
                      -dextension=`pwd`/modules/gmp.so
# あるいは
~/php-src> sapi/cli/php -dextension_dir=`pwd`/modules \
                      -dzend_extension=opcache.so -dextension=gmp.so

make install のステップで、両方の .so ファイルはPHPのインストール場所のエクステンションのディレクトリに移動されます。エクステンションのディレクトリは php-config --extension-dir で確認でき、上記のビルドオプションでは、 /home/myuser/myphp/lib/php/extensions/no-debug-non-zts-MODULE_API となるでしょう。この値はiniファイルでの extension_dir のデフォルトでもあるので、明示的に指定する必要はなく直接エクステンションをロードできます。

~/myphp> bin/php -dzend_extension=opcache.so -dextension=gmp.so

ここでひとつの疑問が残ります。静的ロードと共有オブジェクトのロードのどちらの仕組みを使うべきでしょうか?共有オブジェクトはベースとなるPHPバイナリをもつことができ、php.iniで追加的にロードすることが可能です。ディストリビューションでは基本のPHPパッケージを提供し、エクステンションは別のパッケージとして配布することでこの仕組みを利用しています。一方、PHPバイナリを自身でコンパイルする場合は、必要なエクステンションがわかっているでしょうからこの必要はありません。

大雑把に言えば、組み込みのエクステンションは静的にロードし、その他のものは共有オブジェクトをロードするようにします。理由としては単に、エクステンションを外部からビルドする方が一見して確認できて容易だからです。別の利点としては、PHPをビルドしなおさなくともエクステンションを更新できます。

[1]通常のエクステンションとZendエクステンションの違いについては後ほど説明します。今のところはZendエクステンションはより低レベル(例えばopcacheやxdebug)なもので、ZendEngine自体の動作をフックしているという理解で十分です。

PECLからエクステンションをインストールする

PECL (PHP Extension Community Library) では数多くのPHPエクステンションが提供されています。主なPHPのディストリビューションから除かれたエクステンションは大抵PECL上には残り続けます。同様に、現在はPHPに組み込まれているエクステンションも以前はPECLで配布されていたエクステンションだったのです。

PHPのビルドのconfigureの段階で、 --without-pear と指定しない限り、 make install でPEARの一部としてPECLがダウンロードされてインストールされます。pecl スクリプトは $PREFIX/bin ディレクトリの中にあります。エクステンションのインストールは単に pecl install EXTNAME とするだけです。

~/myphp> bin/pecl install apcu-4.0.2

このコマンドでは APCu がダウンロード、コンパイル、そしてインストールされます。結果、 apcu.so ファイルがエクステンションのディレクトリに格納され、iniオプションで extension=apcu.so とすればロードできます。

pecl install はエンドユーザーにとって大変手軽ですが、エクステンションの開発者にとっては興味がありません。以下では、手動でエクステンションをビルドする方法である、PHPのソースツリーにインポートするやり方(静的ロードが可能になります)と外的にビルドするやり方(共有オブジェクトをロードする必要がある)の2つの方法を説明していきます。

PHPのソースツリーにエクステンションを追加する

サードパーティ製のPHPエクステンションと組み込みのエクステンションには基本的に違いはありません。PHPのソースツリーの中にエクステンションをコピーするだけで、通常のビルド手順を実行することでエクステンションをビルドすることができます。APCuを例に試してみましょう。

まず初めに、PHPソースツリーの ext/EXTNAME のディレクトリにエクステンションのソースコードを配置する必要があります。エクステンションがGitから取得できるなら、 ext/ の中でリポジトリからcloneするだけです。

~/php-src/ext> git clone https://github.com/krakjoe/apcu.git

あるいはソースコードのtarballをダウンロードして解凍するのでもよいです。

/tmp> wget http://pecl.php.net/get/apcu-4.0.2.tgz
/tmp> tar xzf apcu-4.0.2.tgz
/tmp> mkdir ~/php-src/ext/apcu
/tmp> cp -r apcu-4.0.2/. ~/php-src/ext/apcu

エクステンションは config.m4 ファイルを含んでおり、autoconfでのエクステンション固有のビルド方法が記述されています。 ./configure スクリプトに組み込むため、もう一度 ./buildconf を実行しなければなりません。設定ファイルが確実に再生成されるのを保証するために、事前に削除しておくことをお勧めします。

~/php-src> rm configure && ./buildconf --force

./config.nice スクリプトでAPCuを既存の設定ファイルに追加することもできますし、全体の設定をコマンドラインで指定しなおすのでもいいです。

~/php-src> ./config.nice --enable-apcu
# または
~/php-src> ./configure --enable-apcu # --other-options

最後に make -jN を実行してビルドします。 --enable-apcu=shared を使ってないので、エクステンションはPHPバイナリに静的に関連付けられます。つまり、エクステンションを使うのに追加の作業はありません。当たり前ですがインストールするには make install を実行してください。

phpizeを使ってエクステンションをビルドする

PHPをビルドする でも既に述べたように phpize スクリプトを使ってPHPとは分離してエクステンションをビルドすることも可能です。

phpize はPHPのビルドにとっての ./buildconf と同じような役割を果たします。つまり、まず $PREFIX/lib/php/build のファイルをコピーすることでPHPビルドシステムをエクステンションの中にインポートします。これらのファイルの中には acinclude.m4 (PHP’s M4 macros)や、 phpize.m4 (これは configure.in にリネームされメインのビルド手順を含んでいます)や run-tests.php などがあります。

phpize はautoconfを呼び出してエクステンションのビルドをカスタマイズするのに使用される ./configure ファイルを生成します。--enable-apcu と指定する必要はありません。代わりに、 php-config スクリプトのパスを指定するため --with-php-config を使用する必要があります。

/tmp/apcu-4.0.2> ~/myphp/bin/phpize
Configuring for:
PHP Api Version:         20121113
Zend Module Api No:      20121113
Zend Extension Api No:   220121113

/tmp/apcu-4.0.2> ./configure --with-php-config=$HOME/myphp/bin/php-config
/tmp/apcu-4.0.2> make -jN && make install

エクステンションをビルドする際は常に --with-php-config を指定すべきです(ただしPHPが1つだけインストールされている場合は除く)。さもないと、 ./configure で正確にどのPHPに対してのバージョンやフラグを使うかを決めることができません。また php-config を指定することで、 make install.so ファイル( modules/ ディレクトリに格納されています)が正しいエクステンションのディレクトリに移動することを保証されます。

run-tests.php ファイルもまた phpize の段階でコピーされますので、 make test (あるいはrun-tests.phpを明示的に実行)でエクステンションのテストを実行することができます。

コンパイル済みのオブジェクトを削除してくれる make clean もまた利用可能で、これによってエクステンションの全体をリビルドできるようになります。加えて、phpizeには phpize --clean という削除用のオプションがあります。これは /configure で生成されたファイル同様、 phpize によってインポートされたファイルを全て削除します。

エクステンションについての情報を表示する

PHPのCLIバイナリにはエクステンションについての情報を表示するためのいくつかのオプションがあります。既にふれた -m はロードされているエクステンションの全てを一覧表示します。これによってエクステンションが正常にロードされているかを検証することができます。

~/myphp/bin> ./php -dextension=apcu.so -m | grep apcu
apcu

ある機能についての情報を表示するための --r からはじまるオプションがいくつかあります。例えば、 --ri を使えば、エクステンションの設定値が表示されます。

~/myphp/bin> ./php -dextension=apcu.so --ri apcu
apcu

APCu Support => disabled
Version => 4.0.2
APCu Debugging => Disabled
MMAP Support => Enabled
MMAP File Mask =>
Serialization Support => broken
Revision => $Revision: 328290 $
Build Date => Jan  1 2014 16:40:00

Directive => Local Value => Master Value
apc.enabled => On => On
apc.shm_segments => 1 => 1
apc.shm_size => 32M => 32M
apc.entries_hint => 4096 => 4096
apc.gc_ttl => 3600 => 3600
apc.ttl => 0 => 0
# ...

--re はエクステンションによって追加されたiniの設定値、定数、関数、クラスの全てが表示されます。

~/myphp/bin> ./php -dextension=apcu.so --re apcu
Extension [ <persistent> extension #27 apcu version 4.0.2 ] {
  - INI {
    Entry [ apc.enabled <SYSTEM> ]
      Current = '1'
    }
    Entry [ apc.shm_segments <SYSTEM> ]
      Current = '1'
    }
    # ...
  }

  - Constants [1] {
    Constant [ boolean APCU_APC_FULL_BC ] { 1 }
  }

  - Functions {
    Function [ <internal:apcu> function apcu_cache_info ] {

      - Parameters [2] {
        Parameter #0 [ <optional> $type ]
        Parameter #1 [ <optional> $limited ]
      }
    }
    # ...
  }
}

--re は通常のエクステンションのみで動作し、Zendエクステンションには代わりに --rz を使用します。opcacheで試してみましょう。

~/myphp/bin> ./php -dzend_extension=opcache.so --rz "Zend OPcache"
Zend Extension [ Zend OPcache 7.0.3-dev Copyright (c) 1999-2013 by Zend Technologies <http://www.zend.com/> ]

ご覧のように、役に立つような情報が表示されていません。理由としてはopcacheは通常のエクステンションとZendエクステンションの両方を登録していますが、通常のエクステンションの方にiniの設定値や定数や関数などの全てが含まれているからです。そのため、このケースでは --re を使うべきでしょう。他のZendエクステンションでは --rz を通して情報を取得できます。