1. はじめに
先日、とある案件でCent OS 7.2 に PHP 5.6をにインストールする際、がっつりはまってしまいました。ネットで探してもなかなか情報がなかったので、この記事が誰かのお役に立てればと思います。
2. 環境
OS: Cent OS 7.2
DB: MariaDB 10.2
PHP: 5.6
※さらに、PHP 7.2が既にインストールされている状態
同一apache内でモジュール版phpとcgi版phpを共存させる
3. 背景
もともと、Cent OS7.2のVMの環境を持っていました。VMwareです。
Cent OS 7.2の標準リポジトリからyumでLAMP環境を構築すると、
普通にやる分には恐らくPHP 5.4 + MariaDB 5.5系がインストールされると思います。
私は開発環境にはあまりこだわりはないので、もっぱらこの構成で開発をしていました。
しかし、先日、とある案件でPHP7.x、MariaDB 10.xでなければならない、という要件が出てきました。
納期が短かったこともあり、環境構築に割ける時間がなかったので、
そのVMのPHPとMariaDBをえいやで最新の状態に上げることで対応しました。
つまり、既存のPHP 5.4と MariaDB 5.5をアンインストールし、
当時(2018/11月)で最新である PHP 7.2, MariaDB 10.2をインストールしたことになります。
(このアップグレードもなかなか大変でしたが、、、)
かくして、最新のPHPとMariaDB(mysql)を手に入れたわけですが、
当たり前な話で、それまで開発してきたPHPのプロジェクトが軒並み動かなくなってしまいました。
そこで、古いPHPと新しいPHPを共存させるための方法を探していたら、
PHPをソースからビルドして、CGIモードで動かせば良い、ということが分かりました。
(複数バージョンのPHPを共存させる方法は他にもありますが、今回は色々悩んでこの方法にしました)
そして、PHPをCGIモードで動かすためには、PHPをソースからビルドする必要がある、と。
そんな背景から、CentOS 7.2 + MariaDB 10.2 にPHP 5.6を入れるという、
普通の人はたぶん絶対やらない道に進むことになったのです。
4. ハマりポイントerror: ‘MYSQL_UNIX_ADDR’ undeclared (first use in this function)
私自身、PHPをソースからビルドするなんて初めての経験だったのですが、
これは何というか、多くの人が口にするように地味に苦痛でした。
どうにかこうにか ./configureコマンドが正常に終わるよう色々なパッケージをインストールして、
やっとのことでmakeまでたどり着いた、と思ったらmakeでこの結果、、、
error: ‘MYSQL_UNIX_ADDR’ undeclared (first use in this function)
文字通り、MYSQL_UNIX_ADDRという定義がされていない、という事のようです。
同時に以下のようなエラーも出ています。
error: ‘MYSQL_PORT’ undeclared (first use in this function)
正直、さすがにこれはお手上げ、と直感しました。
がしかし、ちょっとがんばってみました。
やればできるもんです。
頑張った途中経過は後で説明するとして、最終的には以下のようにC言語のソースコードをいじることで対応しました。
/usr/include/mysql/mariadb_version.h
1 2 3 4 5 6 7 8 9 10 11 |
#define MARIADB_PORT 3306 #define MARIADB_UNIX_ADDR "/var/lib/mysql/mysql.sock" #define MYSQL_CONFIG_NAME "my" /* ここから */ #ifndef MYSQL_PORT #define MYSQL_PORT MARIADB_PORT #endif #ifndef MYSQL_UNIX_ADDR #define MYSQL_UNIX_ADDR MARIADB_UNIX_ADDR #endif /* ここまで追加 */ |
何をしているかというと、MYSQL_PORTという定義がまだされていなかったら、
MYSQL_PORTをMARIADB_UNIX_ADDRと定義せよ、としています。
MARIADB_UNIX_ADDRは1行目で3306と定義されているため、MYSQL_PORTは最終的に3306に変換されます。
これで無事にmakeが完了しました。
5. 解説
それでは、できる限り詳細に解説を試みます。
まず、エラーの内容をよく見てみます。
エラーメッセージの前に、何やらすごく長いコマンドみたいなものが書かれています。
/bin/sh //usr/local/src/php-5.6.32/php-5.6.32/libtool --silent --preserve-dup-deps --mode=compile cc -Iext/mysql/ -I//usr/local/src/php-5.6.32/php-5.6.32/ext/mysql/ -DPHP_ATOM_INC -I//usr/local/src/php-5.6.32/php-5.6.32/include -I//usr/local/src/php-5.6.32/php-5.6.32/main -I//usr/local/src/php-5.6.32/php-5.6.32 -I//usr/local/src/php-5.6.32/php-5.6.32/ext/date/lib -I//usr/local/src/php-5.6.32/php-5.6.32/ext/ereg/regex -I/usr/include/libxml2 -I/usr/X11 -I/usr/include/freetype2 -I/usr/include/libpng15 -I//usr/local/src/php-5.6.32/php-5.6.32/ext/mbstring/oniguruma -I//usr/local/src/php-5.6.32/php-5.6.32/ext/mbstring/libmbfl -I//usr/local/src/php-5.6.32/php-5.6.32/ext/mbstring/libmbfl/mbfl -I/usr/include/mysql -I/usr/include/mysql/mysql -I//usr/local/src/php-5.6.32/php-5.6.32/ext/sqlite3/libsqlite -I/usr/include/pspell -I//usr/local/src/php-5.6.32/php-5.6.32/ext/zip/lib -I//usr/local/src/php-5.6.32/php-5.6.32/TSRM -I//usr/local/src/php-5.6.32/php-5.6.32/Zend -I/usr/include/mysql/server -I/usr/include -g -O2 -fvisibility=hidden -c //usr/local/src/php-5.6.32/php-5.6.32/ext/mysql/php_mysql.c -o ext/mysql/php_mysql.lo
よく見ると、途中に「cc」とか「php_mysql.c」とか「-o」とか書かれています。
なるほど、これはC言語のソースコードをコンパイルするときのコマンドパターンです。
つまり、/usr/local/src/php-5.6.32/php-5.6.32/ext/mysql/php_mysql.cというC言語のコードをコンパイルしている時にエラーが出ていることが分かります。
もう一度エラーメッセージを見てみます。
error: ‘MYSQL_UNIX_ADDR’ undeclared (first use in this function)
これはつまり、php_mysql.cをコンパイルするにあたって、MYSQL_UNIX_ADDRが定義されていないよ、と怒られているのですね。
一見、そんなのどうしようもないじゃないか、とも思います。
確かに。
ダメもとでGoogle先生に聞いてみます。
すると、以下のような記事が見つかりました。
PHP mysqli extension cannot be build with MariaDB >= 10.2.10
この記事の内容を見てみると、どうも今回のケースとは少し違う現象のようですが、原因は恐らく同じです。
試しに、この記事に書かれている通り、./configure時に以下のオプションを付けてみます。
CPPFLAGS="-I/usr/include/mysql/server"
でもダメでした。
エラーの内容が変わりません。
もう本当にダメだと思いましたが、もう少し粘ってみることに。
上の記事に書かれているように、関係する定義は/usr/include/mysql/配下にありそうです。
そこで、このフォルダ以下でgrepしてみると、以下の定義が見つかります。
server/mysql_version.h:#define MYSQL_UNIX_ADDR "/var/lib/mysql/mysql.sock"
server/mysql_version.h:#define MYSQL_PORT 3306
うーん。
ここまでわかったのなら、とりあえずはこの二つの定義をphp_mysql.cに直書きしてやれば、
少なくともエラーが出ている件に関してはクリアできそうですが、そんな無茶をしてビルドしたPHPなんて信用できます?
ちょっとできません、、、
せめて、どうしてコンパイルが失敗しているのか突き止めたい所です。
今回のケースの問題は、つまり、mysql_version.hというヘッダファイルを読み込めていない事と言えます。
上であげた記事で説明されていますが、いくつかのヘッダファイルは/usr/include/mysql/から/usr/include/mysql/serverに移動したようですね。
そのあたりの構成の変更のせいで、インクルードファイルの構成にも影響が出てしまったと思われます。
C言語プロジェクトの開発ではよくある事です。
仕方がないのでphp_mysql.cの中身を覗いてみることにします。
解析してみると、どうもmysql.hをインクルードする時に、MYSQL_UNIX_ADDRが定義されていてほしいはずが、そうなっていないものと思われます。
その原因は、mysql.hが二つ存在するからです。
一つ目は
/usr/include/mysql/mysql.h
二つ目は
/usr/include/mysql/server/mysql.h
です。
一つ目のmysql.hをインクルードした場合は、以下のようにインクルードされ、定義がない状態となる。
/usr/include/mysql/mysql.h
-> mariadb_version.hをインクルード
-> MYSQL_UNIX_ADDRがない!
二つ目のmysql.hをインクルードした場合は、以下のようインクルードされ、定義がある状態となる(結果コンパイルできるはず)
/usr/include/mysql/server/mysql.h
-> mysql_version.hをインクルード
-> MYSQL_UNIX_ADDRがある!
そして、今回の現象では一つ目のmysql.hをインクルードしています。
なぜかというと、あの長いコマンドをよくよく見てみると、中に-I/usr/include/mysqlと書いてあるじゃありませんか。
-I/usr/include/mysql/serverも書いてありますが、-I/usr/include/mysqlが先に書かれているので
こちらが優先されるのでしょう(-Iはインクルードパスを追加するオプション)。
というわけで、うまくインクルードできていない原因が分かりました。
それでは次はどう直そうか、という問題です。
色々選択肢はあります。
i. php_mysql.cにそれぞれのdefineを直書きする
ii. インクルードしてほしくない方のmysql.hの名前を変える
→すると自動的に/usr/include/mysql/serverの方のmysql.hを見に行く
iii. mariadb_version.hにdefine定義を追加する
どの方法でもビルドは成功するでしょうし、ビルドされた結果も全く同一になるでしょう。
ここではできるだけ影響を小さくし、.cファイルを触りたくないという理由から、iiiを採用しました。
6. 終わりに
ワラゴンは、今はもっぱらWeb系のプログラマですが、昔はCもかじっていました。
今回は、そんな経験が少し役に立つ形となりました。
本当、色々とやってみるもんです。
それにしても、改めてPHPはCで書かれているんだなぁと実感する出来事でした。
そして、こんなに苦戦するんだったら、最初からDockerでPHP 5.6の環境を作った方が早かっただろうな、と少し後悔しました、、、
おまけ
※makeが途中でコケた場合、念のためmake cleanコマンドを使ってゴミを掃除してからもう一度makeしましょう。念のため。