Vimとボクと、ときどき、PHP

Vim好きなエンジニア(主にPHP開発)が不定期に技術情報やネタを書いていくブログです

Home » さくらのスタンダードプランでPHPのNet_FTPが動くようにする

さくらのスタンダードプランでPHPのNet_FTPが動くようにする

PHPでFTP操作をするにはPEARのNet_FTPを使うのが簡単なわけですが、さくらのレンタルサーバ(スタンダードプラン)ではただインストールしただけでは動きませんでした。それを何とかしようとしたら解決できたので、ここに書かせていただく次第。

スタンダードプランで出来たわけなので、それより上のプランでも大丈夫だと思います。SSH接続できる必要があるので、ライトプランでは無理です。

軽く経緯を

さくらのスタンダードプランのサーバで、他のサーバからファイルをダウンロードする必要に迫られました。SSHが使えれば

scp -r remote:/path/to/remote/dir /path/to/local/dir

を実行してやればいいのですが、相手のサーバにSSH接続することができないので、必然的にFTPを使うことになります。
そこで、PHPでNet_FTPを使えば楽勝だなと思いきや、いざインストールして実行してみてもうまくいかない。そもそも接続ができない始末。

さてどういうことかとソースを追いました。私が見てたのはバージョン1.4.0a3時点のソースです。Githubにあります
まず見つけた原因は、ftp_connect関数でエラーが発生していた。スタンダードプランではPHPのFTP拡張が無効になっているらしく、ftp_connect関数が存在しないのです。

この時点でそこそこ困り果てていたのですが、よくよくNet_FTPのソースを見てみたら、FTP/Socket.phpftp_connect関数の実装がありました。他にもftp_xxxxx関数が無かった場合は独自で定義してくれています。これは有り難いと

require_once 'FTP/Socket.php';

を記述して再度実行。接続もできた。これは勝ったと思い意気揚々と

$ftp->getRecursive($remote, $local, true, FTP_BINARY);

と書いて実行したものの、今度はファイルをダウンロードできない・・・。

この時点でかなり萎えてはいましたが、再度ソースをチェック。すると、socket_create関数でエラーが出ています。まさかと思ってphpinfo()で見てみたら、案の定です。スタンダードプランはSocket拡張も無いのですね。
つーか、@ftp_connect(...)の形で呼んでるせいで、エラーが抑制されて原因が非常に分かりづらかった。相手サーバが原因かと思って.ftpaccessまで調べちゃったよ。もぅ。

これは完全に詰んだ。PHPに拡張機能を追加するときって、configureにオプション追加して再ビルドだった気がする。レンタルサーバでそんなことやりたくない。こうなったらPHPからftpコマンドを直接実行して力技で何とかするしかないか。でもSocketが使えないからどうしよう・・・と、いろいろと考えながら白目でGoogle先生にすがったら、こんなページを発見。

PHPを再コンパイルせずに拡張モジュールだけビルドできる・・・だと・・・!?

というわけでございます。

あ、ちなみに、上で書いた経緯はかなり曖昧な記憶に頼っているので、FTP/Socket.phpを使ったタイミングでもまだ接続できなかったかもしれない。けどいいんだ、もう過去のことだから!

Socket拡張をビルドする

調査した結果、ftp_xxxx関数に関しては実装があるわけなので、Socket拡張だけあればいける。ということで、この素晴らしいページにまんま書かれているので、実行してやればよい。
phpizeautoconfが必要ですが、どちらもスタンダードプランで使えます。

PHPのソースをダウンロードする

PHPのバージョンを確認して、ここから同じバージョンのソースを取得する。現時点(2016/05/12)では、さくらのスタンダードプランでは5.6.18が標準ですが、他のバージョンが選択されていることもあるのできちんと確認すること。
ダウンロードしたらさくらのサーバにアップロードして、展開。・・・そんなことするならサーバでダウンロードすればよかったなと思ったのは全てが終わった後です。笑えよ。

展開先を/path/to/php_srcとして話を進めます。

phpizeしてconfigureしてmakeしてインストール

もう一気にコマンドを打っていくだけ。

$ cd /path/to/php_src/php-5.6.18/ext/sockets
$ phpize
$ ./configure
$ make

参考にしたページでは最後にmake installしてるのですが、今回はスタンダードプランなのでインストールすることはできず。なので置ける場所に手動でコピーします。

$ mkdir ~/php_modules
$ mv /path/to/php_src/php-5.6.18/ext/sockets/modules/socket.so ~/php_modules

こんな感じでいいんじゃないかな。ファイル1つ移動するだけです。

php.ini に追加

インストールしたsocket.soをPHPさんが見つけられるようにしてあげなければいけない。php.iniに

; [user]の部分は自分ので埋めてください
extension_dir=/home/[user]/php_modules
extension=socket.so

を追記しませう。これで完了。phpinfoで見て、「Sockets support」が「enabled」になっていれば大丈夫。

まだだ、まだ終わらんよ

Socketも使えるようになったし、これで勝ったなと思ったのに、まだダメだったんですよ。
何がダメって、Net_FTPのgetRecursive関数でエラーが出る。リモートのパスで指定しているのが/remote/dirだとすると、なんか「/remote/dir/.はムリよ」みたいなメッセージ出してる。

またソースをチェック・・・したんですが、特におかしそうなところはない。でもディレクトリに再帰的に入っていこうとしてエラーが出てるぽかったので、ls()関数が何を返してるか見てみたら、なんかディレクトリパスの最後に改行が入ってる。
分かりにくいですよね。つまり

"/remote/dir/dir_a"    ←""で囲んだ中の文字列が取得できているとする

と取得してほしいのに

"/remote/dir/dir_a
"

みたいに取れてるの。最後に改行が付いてる。ソース上では .(カレントディレクトリを示すやつ)と..(1つ上の階層を示すやつ)は無視するようになってるけど、改行がついてるから意図したように判定されておらず、「/remote/dir/.[改行]」ていうディレクトリに入ろうとしてエラーが出てる。どうやら独自定義のftp_xxxxのせいみたいです。

なんかNet_FTPのバグのような気もするし、trim関数で改行を消してやるだけで解決できそうだったんですが、他に妙なことが起きてももう調査する気力が無いので、いっそのことFTP拡張もビルドしてやることにしました。

FTP拡張をビルドする

Socket拡張のビルドのときにPHPのソースはダウンロードしてあるし、手順も同じなので楽勝。

phpizeしてconfigureしてmakeしてインストール

また一気にコマンドを打っていくだけ。

$ cd /path/to/php_src/php-5.6.18/ext/ftp
$ phpize
$ ./configure
$ make
$ mv /path/to/php_src/php-5.6.18/ext/ftp/modules/ftp.so ~/php_modules

なんかftp.laってのも一緒に出来たけど、ftp.soだけコピーで動きました。

php.ini に追加

インストールしたftp.soをphp.iniに追記。extension_dirはさっき書いたのでextensionだけでおk。

extension=ftp.so

phpinfoで見て、「FTP support」が「enabled」になっていれば大丈夫。

ここまでやれば、FTP/Socket.phpをrequireする必要はなくなります。

注意:コマンドラインとかで実行する場合

今回追加した拡張モジュールは、php.iniに追記してるのでブラウザから実行された場合は読み込めるんですが、コマンド手打ちやCronで実行するような場合には無効になってるままです。なので、以下のどちらかの方法で実行すること。

1. コマンドラインで設定を追加する

こんな感じ。[user]の部分は置き換えで。

$ php -d extension_dir=/home/[user]/php_modules \
      -d extension=ftp.so \
      -d extension=sockets.so \
      -d include_path=".:/path/to/PEAR" \
      /path/to/script.php

-dオプションで、php.iniに記述する設定をコマンドラインで指定できます。Net_FTPも見えないといけないので、上の例ではPEARの場所も指定。

2. php.iniを指定する

いちいちコマンドラインで指定するのが面倒だったり、ブラウザで実行されたときと同じ設定を適用したい場合は、-cでphp.iniを指定してやればいいです。

$ php -c /path/to/php.ini /path/to/script.php

こっちの方がいろいろ楽かもしれません。

必ず最後にIは勝つ

Iってもちろん「私」って意味です。「I win!!」ね。つまらんことを解説しちゃうと更につまんなくなるね・・・。

phpizeって知らなかったから勉強になったし、レンタルサーバでも結構自由に拡張とかできるんですね。今後の役に立ちそうな予感がしてならない。
あと、ライブラリといえど所詮はPHPソースなんで、しっかり調査すれば解決できることがほとんどです。いざとなったら自分でソースを書き換えて何とかすればよい。でもかなり疲れるので、覚悟のうえで臨みましょう・・・。

Name of author

Name: よーすけ

Short Bio:

主にPHP開発をやってる社内PGです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です