uzullaがブログ

uzullaがブログです。

Apache+mod_phpをOSXで野良ビルドし、phpenvでPHPを入れるのに挫折しかけたが諦めなかった話

一つ前のエントリの話から繋がるんですが、OSXApachephpの環境を用意したいんですよ。

uzulla.hateblo.jp

三行で

  • Apacheは手でいれる
  • phpenv(php-build)で、apxs有効にすればすぐとおもいきや
  • sslの依存関係の闇が深くて、手パッチ
  • 「…mod_phpあきらめれば?」「それな」

さておき

OSXApache+phpの環境を用意するには色々な方法がありますが、

  • XAMPPで入れる
  • 最初からはいってるやつをつかう
  • Homebrewをつかう

ここらへんが普通でしょう。

どれが良いか一長一短ありますが…

  • XAMPPは入れるの楽だが切り替えが面倒ですし
  • 最初から入ってるやつは構成やバージョンの変更はできませんし
  • Homebrewは提供版だけ使うならよさそうだけど、マルチバージョン等、構成を色々変えるのは難しい

ということで

今回それらは無視して、以下で行きました。

本来tar ballからのビルドで育った人間としては難しい所はないんですが、今回はphpenvをつかいたい。Apache自体はそんなにバージョンアップしなくてもいい。

うまくいけば、OSXネイティブのApache+mod_phpで複数の環境をうまくきりかえて使えるハズ…(続編で紹介)!という感じです。

最初にオチをいいますが、El capitanだとmod_phpのビルドが大変なので、Apache+CGIPHP、あるいはApache+mod_fcgi+php-fpmの構成を検討した方が良いです。

今回なかば意地になっているのを最初にお伝えしておきます。

(開発環境ではmod_phpほどの速度は必要ないし、cgiphpで十分でしょう)

したごしらえ

phpenvとhomebrewをセットアップし(省略します)、まずはphpenvでPHPがビルド出来る程度の用意をします。

$ phpenv install 7.0.0

これが通るくらいにはインストールマラソンをこなしておきましょう。まあここらへんはネットでググればすぐ解決します。

後、以下を実行しておきます。多分必要です。*1

$ brew link libxml2 --force
$ brew link libressl --force

まずはApacheのビルド

本家からtar.gzなsourceを落としてきて、configure、make all、make allでビルド&インストールします。

先に書きますがコンパイラがみつからない場合があります。ググれば解決策もみつかりますけど以下みたいにすれば解決できます。(Makefileを直してもよい)

sudo ln -s /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/ /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.11.xctoolchain
# 10.11はElcapitanなので、Mavericksだと10.10かも

そしてconfigureは注意が必要です。

$ ./configure --prefix /Users/uzulla/bin/apache --enable-load-all-modules --with-libxml2=/usr/local/Cellar/libxml2/2.9.2 --with-ssl=/usr/local/Cellar/libressl/2.3.0/
$ make all
$ make install

キモはlibxml2とopensslをHomeBrewのものを指定し、with-sslにはLibreSSLを設定していることです。

普通はOpenSSLでいいんですが、homebrewのlibはLibreSSLを前提にしているっぽいので今回はLibreSSLに寄せました。OpenSSLとLibreSSLをまぜるとこの後のPHPのビルドで大変になる様です(後述します)。

prefix*2については、システム(/usr/local以下)にいれる意味がないので*3自分のhome以下に変えています*4、ここは各自適当に。あとは開発環境なんでモジュールは全入れです。

ビルドとインストールが成功し、エラーなくApacheがあがればとりあえずOKです。*5

$ sudo ~/bin/apache/bin/httpd  -DFOREGROUND

(一つ前のエントリで説明していますが、上はデーモン化しない起動方法で、^Cで終了できます)

PHPのビルド

phpのビルドには今回phpenvをつかいます、やっぱりphpenv以下にまとまってると楽ですからね。しかし、冒頭にも書きましたが結構大変ですし成功しませんでした。

この後は闇あふれる手パッチです。どうしてもmod_phpをいれたい人以外はcgiPHPを動かすといいんじゃないですかね…(私も次はそうするかもしれません)。

本来、以下一発でできるはず…だと思います。(もしこれでビルド一発通ったら教えて下さい、なんか私のHomeBrewがおかしいのかもしれない)

重要なのはwith-apxs2を指定していることで、この指定でmod_phpが作成できます。当然ですがこのapxsには先程インストールしたApacheのものをつかいます。

ライブラリのopensslとlibxmlの指定はシステムではなく、Homebrewのライブラリをつかうように指定しています。

$ PHP_BUILD_CONFIGURE_OPTS='--with-apxs2=/Users/uzulla/bin/apache/bin/apxs --with-openssl=/usr/local/Cellar/libressl/2.3.0 --with-libxml-dir=/usr/local/Cellar/libxml2/2.9.2/bin/xml2-config' phpenv install 7.0.0

あるいは、~/.phpenv/plugins/php-build/share/php-build/default_configure_optionsに以下のような定義を追加することでもかまいません。こっちのほうが確実かもしれません。

# with-opensslとかは元々があるとおもうので、その行を書き換えて下さい。
--with-openssl=/usr/local/Cellar/libressl/2.3.0
--with-curl=/usr/local/Cellar/curl/7.45.0
--with-apxs2=/Users/uzulla/bin/apache/bin/apxs

さて、これでビルドしてみると…?うまくいきました?私は次のエラーメッセージがでて、これに苦しめられました。

Undefined symbols for architecture x86_64:
  "_PKCS5_PBKDF2_HMAC", referenced from:
      _zif_openssl_pbkdf2 in openssl.o
  "_SSL_CTX_set_alpn_protos", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_SSL_CTX_set_alpn_select_cb", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_SSL_get0_alpn_selected", referenced from:
      _php_openssl_sockop_set_option in xp_ssl.o
  "_SSL_select_next_proto", referenced from:
      _server_alpn_callback in xp_ssl.o
  "_TLSv1_1_client_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_TLSv1_1_server_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_TLSv1_2_client_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
  "_TLSv1_2_server_method", referenced from:
      _php_openssl_setup_crypto in xp_ssl.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [libs/libphp7.bundle] Error 1

これはsslのライブラリのリンクがうまくいってないってことですね。私はこれにめっちゃハマりました。というのも、このエラーってapxsを抜くと出ないんですよ、めっちゃ罠じゃん。

さて、これの原因は、OpenSSLとLibreSSL(あるいはそれらのバージョン差異)の食い合わせが悪くてリンクに失敗してしまうという事なんですが(だとおもいますが)、この処理って最後のリンクなので、ここに到達するのにすげー時間がかかるので、試行がめっちゃ大変でしたね…。

さておき、解決策なんですが、これ本当にきびしい手パッチです。

phpenv install 7.0.0の裏でうごくphp-build/var/tmp/php-build/source/7.0.0ここらへんにtarを展開して、そこでbuildとかしています。なのでそこに移動してMakefileを編集します。

EXTRA_LIBSの行に-lsslがいくつか(複数はいっている可能性が高いです)あるのですが、それを全部消して、そこに直接Homebrewでいれたlibresslのdylibを指定してしまいます。

EXTRA_LIBS = -lz -lexslt -ltidy -lresolv -ledit -lncurses -lmcrypt -lltdl -liconv -liconv -lpng -lz -ljpeg -lcurl -lz /usr/local/Cellar/libressl/2.3.0/lib/libssl.dylib /usr/local/Cellar/libressl/2.3.0/lib/libcrypto.dylib -lm -lxml2 -lz -liconv -lm -lkrb5 -lcurl -lldap -lz -lxml2 -lz -liconv -lm -lxml2 -lz -liconv -lm -lxml2 -lz -liconv -lm -lxml2 -lz -liconv -lm -lxml2 -lz -liconv -lm -lxml2 -lz -liconv -lm -lxml2 -lz -liconv -lm -lxml2 -lxslt

たとえばこんな感じになりますね。この修正をしたらmake allが通るはず…です。

私がなんかミスってるのかもしれないのですが、特にEl capitanになってからOpenSSL、LibreSSLまわりが一筋縄でいかなくて本当にきびしいですね。かといってOpenSSLを野良で入れるのもビミョウ。

さておき、makeが成功したら、make installしましょう。

(追記:後述しますが、一度でもapxs抜きでphpenv installを行っていて、既存のバージョンを上書きするなら、.phpenv/versions/7.0.0以下のファイルを一回消してからやらないとハマります)

すると、いつも通りのphpenvのinstallのようにPHPが~/.phpenv/versions以下にはいりつつ、Apachebin/apache/modules/以下にlibphp7.soがコピーされ、設定ファイルが自動的に修正されてインストールされるはず、です。

ただ、ここもうまくいきませんでした(sedがエラーを吐いた)。なのでhttpdのconfを修正する必要があります。(勿論エラーなくうまくいったらこの作業は不要です)

apache/conf/extrahttpd-php7.confというファイルを以下の内容でつくり、以下のように記述します。

LoadModule php7_module modules/libphp70.so
AddType text/html .php
<FilesMatch \.php$>
    SetHandler application/x-httpd-php
</FilesMatch>

(そういえば、SetHandlerまわり、セキュリティのアレでファイル名マッチからデフォルトがこの記法にかわりましたね。)

その上で、httpd.confの方に以下のように追記して、コンフィグをロードします。(あるいは、直接httpd.confに上のconfをかいてしまってもよいですけど)

include conf/extra/httpd-php7.conf

あとは、必要に応じて以下を書いたりしてもよいでしょう。

DirectoryIndex index.php

これでPHPがうごくはずです!多分!

まあ、Linux野良ビルドやってきた人にはなれたもんですね。

余談:途中でとまるので、xdebugがはいらない

phpenv installは最後まで通らないので、xdebugがはいりません(opcacheまでは入るけど)。なので手でいれましょう。

$ cd /tmp
$ git clone git://github.com/xdebug/xdebug.git
$ cd xdebug
$ phpize
$ make all
$ make install
$ rm -r xdebug
# .phpenv/versions/7.0.0/etc/conf.d/xdebug.ini を作成
zend_extension="/Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-zts-20151012/xdebug.so"
html_errors=on

php -i |grep xdebugでエラーなくロードできていればOKです

ここで一個注意があるんですが、試行錯誤してると以下みたいなエラーがでることがあります。

$ php -v
Failed loading /Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/opcache.so:  dlopen(/Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/opcache.so, 9): Symbol not found: _compiler_globals
  Referenced from: /Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/opcache.so
  Expected in: flat namespace
 in /Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/opcache.so
Failed loading /Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so:  dlopen(/Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so, 9): Symbol not found: _basic_globals
  Referenced from: /Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so
  Expected in: flat namespace
 in /Users/uzulla/.phpenv/versions/7.0.0/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so
PHP 7.0.0 (cli) (built: Dec 15 2015 14:27:15) ( ZTS )
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2015 Zend Technologies

こちら、opcacheやxdebugなどのextentionがうまくうごいてないのは一目瞭然なのですが、ちょっとハマりました。

原因はphpenv installをつかってすでにあるバージョンのphpを再度インストールしたら、既存のファイルとの競合が発生しました。

具体的には、過去にapxs抜きでビルドするとnon-ztsなファイル群ができますが、その後でapxs入りでビルドするとztsのファイル群ができてきて、既存のファイルは削除されずにマージされる格好となります。

その時、既存の設定ファイルは「上書きされない」ので、「過去のapxs抜きのnon-ztsの拡張をつかいつづけようとする設定」になり、ビルドしたPHPとマッチしない拡張をロードしようとしてしまいます。

後でphp.inixdebug.iniなどを書き換える。あるいは、apxs付きでビルドする前に既存の.phpenv/versions/7.0.0をどけましょう。(あるいは、7.0.0_apacheとか別名を)

余談:apxsがうまくうごかない

これは一部の人だけになりますが、plenv環境でApacheをビルドするとこんな感じのエラーが出るかもしれません。

/Users/uzulla/bin/apache/bin/apxs: line 18: require: command not found
/Users/uzulla/bin/apache/bin/apxs: line 19: use: command not found

これはapxsはPerlでかかれているんですが、うまくそれがうごいてない場合になります。(ShebangにかかれているPerlをうまく認識できず、シェルスクリプトとして実行してしまっている)

手パッチ面倒くさいですけど、/Users/uzulla/bin/apache/bin/apxsの一行目を修正し、たとえば以下のように書き直せば動くはずです。

#!/usr/bin/env perl -w

これ、どうにかならんのですかね?(plenvつかってると、希によくある)

未完!

ここからがやりたい事なんですけど(PHPのBuiltin ServerみたいにApache+mod_php環境をシュッ起動できるように用意し、プロジェクト毎に設定を別ける)、PHPのビルド一回に10分くらいかかるので、大分HPをけずられました。今日はここまでです。

次回につづく。

*1:OSXは環境のガラガラポンが面倒なので、まっさらな最短手順を試行するのがむずかしいですね…

*2:インストール先のBase Directory

*3:というか、Homebrewとぶつかりそうなので

*4:El capitanだと色々ありますしね

*5:sudoしてるのはデフォルトで80番を使うからですね