ubuntu karmic(9.10)とphp5のgzopen/gzopen64問題

(追記: 10/31現在、karmic-proposedにgzopenがあるバージョン(5.2.10.dfsg.1-2ubuntu6.1)ができています。apt lineは以下のような感じです。)

deb http://jp.archive.ubuntu.com/ubuntu/ karmic-proposed restricted main multiverse universe


現在、karmic版のphp5(5.2.10.dfsg.1-2ubuntu6)全般で、"gzopen"関数が使えないという問題があります。

これによってこのphp5では、たとえばwordpresspukiwikiなどが動かなくなっています。

Fatal error: Call to undefined function gzopen()

というエラーがでます。自分も9月中ごろあたりのバージョンからこの問題が起こっており、レポートを投稿してました。

このphp5バイナリでは、gzopenはないけど"gzopen64"関数は入っています。ちなみに利用コード側でgzopenをgzopen64に置き換えると動くようにはなるようです。


一応php5バイナリにgzopenを復活させる方法は、

のようにリビルドすることですが、今度はgzopen64が消えるようです。
自分はjaunty版の5.2.6を入れて、php5-commonをholdして使ってました(こちらもgzopen64は入ってない)。


phpソースコードや仕様を調べた結果、gzopen64はgzopenが/usr/include/zlib.hヘッダ中のマクロで不必要に変化してしまったというバグであり、直されるべきものと判断しました。運用上は、直った跡でも動くようなユーザーコード上のワークアラウンドで対応し、php5パッケージの修正リリースを待つことに決めました。

以下は、pukiwikiなどで、gzopenなしに対応するためのワークアラウンドコードです(lib/pukiwiki.phpの先頭に埋め込む)


もうkarmicのリリースしてしまったわけで、そしてgzopenなし問題も改めて対応する問題になったようですが、今後どう対応するのか気になります。

まとめ

  • gzopen64なんて標準関数はPHPにはない
  • /usr/include/zlib.hに以下のマクロがある。これがPHP関数gzopenの定義にまで利きすぎたのではないか。
#define gzopen gzopen64
  • 019-z_off_t_as_long.patch はext/zlibに古めのzlibのヘッダファイルを追加するだけのpatch。そのzlib.hには上記マクロが存在しない
    • includeでそれが優先されるためgzopenが存在し、gzopen64は存在しないphpバイナリができあがる
  • gzopen64が存在すること自体がバグ。PHPソースコードをgzopen64に変えてはいけない
  • この結末を見ると、同じバグでもみんなでバグレポートしたほうがいいかも。

実験

システムのzlib.h含む、phpのマクロ構造を単純化したコードが以下のようになります。

/* print result: gcc -E sim_php_zlib_ubuntu.c */

/* Zend/zend_API.h */
#define ZEND_NAMED_FUNCTION(funcname) PHP_##funcname
#define ZEND_FUNCTION(funcname) void ZEND_NAMED_FUNCTION(funcname) ()
/* main/php.h */
#define PHP_FUNCTION ZEND_FUNCTION
/* /usr/include/zlib.h */
#define gzopen gzopen64

/* ext/zlib/zlib.c */
PHP_FUNCTION(gzopen);

PHP_FUNCTION(gzopen)
{
  return;
}

int main() {
  return 0;
}

実行結果は、

# 1 "sim_php_zlib_ubuntu.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "sim_php_zlib_ubuntu.c"
# 12 "sim_php_zlib_ubuntu.c"
void PHP_gzopen64 ();

void PHP_gzopen64 ()
{
  return;
}

int main() {
  return 0;
}

となり、たしかにリネームされることが確認できます。