はじめに
この記事はPHP Advent Calendar 2018の24日目の記事です。
西日本応援プロジェクト真夏の大LT大会で徳丸さんが古くはphp-3.0.18から最新のバージョンまでmacOS上に環境構築しているという話をしていたので、同じようなことをAlpineLinuxでやったらどうなるのか、という記録がこの記事です。
あらかじめ書いておくと、徳丸さんの発表とちがって、この記事は基本的に無益です。
前提
- docker for Mac を利用
- AlpineLinux 3.8
- AlpineLinuxのパッケージングシステム(abuild/apk)を利用する
- Apache1.3用のmod_php版とCLI版(php-4.2.x以前はCGI版)をビルド
初めてabuildを使いましたが、RPMより書きやすい気がしますね。
成果物
作ったものは、http://github.com/odz/a-php にあります。
FROM alpine:3.8
ARG PHP_VERSION
ADD https://odz.github.com/a-php/oda.org@gmail.com.rsa.pub /etc/apk/keys
RUN echo 'https://odz.github.com/a-php/php' >> /etc/apk/repositories && \
apk add \
php-apache1.3~${PHP_VERSION} \
php-pear~${PHP_VERSION}
CMD ["/usr/bin/apache", "-F"]
このようなDockerfileを作成して、
docker build --build-arg PHP_VERSION=4.0.0 -t php:4.0.0 .
などとすれば、Apache 1.3、PHP 4.0.0のDocker Imageができます。
Document Rootは /var/www/localhost/htdocs
です。
Apache 1.3
mod_php版をビルドするためにはまず、Apache 1.3が必要だが、さすがにいまどきの環境ににApache 1.3のパッケージはないので、自力でビルドする。が、Apache 1.3もそれなりにパッチをあてないとビルドできない。
getline.patch
getlineという関数が標準ライブラリのものと名前が衝突しているので、名前を変更しないと行けない。
libc-decl.patch
glibcには sys_sliglist
というシンボルが定義されているのだが、Alpine Linuxで利用している musl libc にはそんなものはないのと、判定をミスっていて
glibc 2.0以前向けにrlim_t
をtypedef
しようとしているのでこれを削除。
busybox_sed.patch
busybox の sed が GNU sed と違って \+
を解釈できないので、修正。よくわかっていないけど、\+
は GNU sed の拡張ということで良いよね?
no-setsid.patch
これはあてなくてもビルド自体はできる。
Apache 1.3 は foreground で動かす場合(-Fオプション指定)もsetsid(2) をコールし、エラーの場合はプロセス終了してしまうのである。説明はめんどいので飛ばすが、Docker で Apache を動かす場合に、この仕様は大変つらい感じなので、setsid(2) をコールしないように修正。
-fgnu89-inline
gcc の古いバージョンでは extern inline
を指定された関数はインライン展開されたあと、関数定義自体は残らないらしい。いまのデフォルト動作はそうではないのだが、そのような動作にするためには-fgnu89-inline
オプションを指定すれば良いらしい。これを指定しないとmultiple definitionでリンク時にエラーになる。
Apache 1.3 でいうと以下のコードですね。
#define INLINE extern ap_inline
INLINE int ap_os_is_path_absolute(const char *file);
#include "os-inline.c"
INLINE int ap_os_is_path_absolute(const char *file)
{
return file[0] == '/';
}
PHP 4.0.0
number4.tar.gz
bcmath を有効にしようとすると
You do not have the bcmath package. Please read the README.BCMATH file.
といわれそのとおりに README.BCMATH を読むとライセンスの問題でBC Math ライブラリは PHP とは別配布になったので、http://www.php.net/extra/number4.tar.gz からダウンロードしてくるべし、みたいなことが書いてあるわけです。が、そのURLにアクセスしてもダウンロードできない。
結構苦労して探してどこかからダウンロードしてきたのだけど、どこだったかはよく覚えていない。
ちなみに、ライセンス問題が解決されたのか PHP 4.0.4 からは同梱されるようになっている。
extern-static.patch
古い gcc では同一変数名でextern
付きの宣言とstatic
付きの宣言が重複していても
問題なかったらしい。GCC 4 以降はそのようなコードはエラーになるようになっているので、
変数名を変更して対応。ap_php_opterr
と ap_php_optind
に関してはそもそもextern
宣言の方が不要なので削除。
ちなみに、PHP 4.0.5 で問題は修正され、このパッチは不要になる。
mysql-obsolete.patch
PHP 4.0.0 は MySQL 3.x 時代に存在していたmysql_createdb
とmysql_dropdb
を参照しているため、リンクまでうまく行っても実行時にシンボルが足りないため、実行時にエラーになる。古い MySQL を持ってくるのでも良いのだけど、めんどいのでPHP側を修正して対応。
なお、これも PHP 4.1.0 で同様の対応が取られるのでパッチ不要になる。
mariadb-port.patch
Alpine Linux には MySQL ではなく、MariaDB が提供されているので、
この記事でのPHPのビルドにも MariaDB を利用しているんだが、
MariaDB の Client Library は関数名、ヘッダ名もオリジナルと合わせているし、
ライブラリの名前も libmariadb.so だけでなく、libmysqlclient.so も提供されているくせに
MYSQL_PORT
がdefineされないという状態である。おしいな。
ということで、#define MYSQL_PORT MARIADB_PORT
としておく。
zend_optimization.patch
sed s/-O[0-9]*//
として最適化オプションを削除したコンパイルオプションを生成しようとしているのだが、Alpine Linuxのデフォルト状態だと-Os
オプションが指定されているので、
この状態だとs
だけ残って悲しいことになる。ということで、sed s/-O[s0-9]*//
に修正して対応。
これも PHP 4.0.5 で同様の対応が取られる。
zlib.patch
zlib には
gzFile gzopen(const char *, const char *);
int gzclose(gzFile file);
int gzgetc(gzFile file);
といった関数群があって、PHP はこれらへのbindingが提供されているのだが、PHP 4.0.0の時期のコード自体は全般的に gzopen
の戻り値や gzgetc
へのパラメータの型をgzFile
ではなく、gzFile *
と勘違いしている模様である。
zlib 1.2.5 までは typedef void *gzFile
なので問題ないが、zlib 1.2.6 以降は typedef struct gzFile_s *gzFile
であり、gzgetc
はこの構造体のメンバにアクセスするマクロになっているので、
この辺の兼ね合いで zlib 1.2.6 以降だとコンパイルエラーになるという。
gzgetc
だけ care しておけばとりあえずビルドはできる。テストはしてない。
この問題は PHP 4.3.0 で gzgetc
他の実装が修正されパッチ不要になる。
その他
いろいろ試していて気づいたのだが、PHP 4.0.0 は php.ini
の extension_dir
ディレクティブの設定が効かないというバグがあるようだ。このため、extension
ディレクティブはフルパスで指定する必要がある。
なぜ、そんなバグが放置したままリリースされたのか・・・
時間切れ
ここまで書いて時間切れ。
いろいろあるので、あとで追記する。