PHPでFTPクライアント
PHPでFTPクライアントを稼働させたい案件がありました。PHPには拡張ライブラリとしてFTPが存在します。今回はそれを使ってみます。
拡張ライブラリの確認
php -mコマンドでライブラリが存在するか確認できます。
> php -m [PHP Modules] calendar Core ctype date dom ... ftp ...
またPHPコードにおいて、FTP拡張ライブラリの有無を確認したければextension_loaded関数を使います。
if (extension_loaded('ftp')==false) {
die('require ftp extension');
}
Debian11においてはphp7.4をインストールすればそのままFTP拡張ライブラリは利用できるようですが、Windows11の場合は、php.iniでFTP拡張を有効化する必要があります。
php.ini(Windows)
... extension=curl ;extension=ffi # コメントアウトを解除します。 extension=ftp extension=fileinfo ...
ライブラリの実装
基本的な使い方は次のようになります。コード中ftp_connectの戻り値を格納する$ftpはPHPのバージョン8.1以上ではFTP\Connectionのインスタンス、それ以前はリソースですが、コードの互換性は保持されているようです。
ftp_getやftp_putでファイルを送受信しますが、その際にそれぞれの第4引数でアスキーモード(FTP_ASCII)かバイナリモード(FTP_BINARY)か指定する構造になっています。また、第5引数ではダウンロードまたはアップロードの開始位置を指定するオフセットを指定できます。
ftp-client.php
<?php
// FTPサーバーの設定
define('FTP_SERVER','192.168.1.1');
define('FTP_USER','user');
define('FTP_PASS','password');
// 接続
$ftp = ftp_connect(FTP_SERVER);
// ログイン
$login = ftp_login($ftp,FTP_USER,FTP_PASS);
// ログイン結果の確認
if((!$ftp) || (!$login)) {
die('ftp connection has failed');
}
// 受信
$receiveFile="./receive.dat";//既存時上書き
$serverFile="/path/test.dat";
if (ftp_get($ftp, $receiveFile,$serverFile, FTP_ASCII)==false) {
echo "ftp_get has failed";
}
// 送信
$sendFile="./send.dat";
$serverFile="/path/test.dat";//既存時上書き appendしたい場合は ftp_appendを使う
if (ftp_put($ftp, $serverFile, $sendFile, FTP_BINARY)==false) {
echo "ftp_put has failed";
}
// 切断
ftp_close($ftp);
主なメソッド
PHP FTP拡張モジュールでは先に登場したメソッドの他にも多くのものがありますが、主なものを紹介していきます。
例示中の$ftpはftp_connectで得られる戻り値、$modeにはFTP_ASCIIまたはFTP_BINARYのいずれかの値が入る前提です。
その他の引数は日本語の説明に$を付けています。
特に記述のない限り、戻り値はコマンド実行が正常に終わったかどうかがboolean値で返ります。
- 接続
ftp_connect($ホスト,$ポート,$タイムアウト)
ホストはドメインまたはIPアドレス、ポートはFTPのコントロールポートを数値で指定します。タイムアウトは秒数を数値で指定します。
戻り値は、成功時はリソースまたはFTP\Connectionのインスタンス(PHPのバージョンにより変わります)、失敗時はfalseです。
SSL接続を指定したい場合はコマンドを「 ftp_ssl_connect 」に差し替えるだけです。
相性の問題もあるのかもしれませんが、以前このブログで紹介したvsftpで構築したFTP SSLサーバには問題なく接続できました。
- 切断
ftp_cloe($ftp)
- ログイン
ftp_login($ftp, $ユーザー名, $パスワード)
- 一覧取得
ftp_nlist($ftp, $ディレクトリ)
成功時はファイルとディレクトリのリストが配列で返ります。失敗時はfalseが返ります。
ディレクトリ名の前にlsに対するオプションが指定できます。
次のようにすると、隠しファイルの含んだ詳細情報の配列が戻ります。
ftp_nlist($ftp, "-la target-dir")この戻り値は次の出力を行毎に配列化した結果になります。
drwxr-xr-x 4 0 0 4096 Sep 24 21:20 . drwxr-xr-x 4 0 0 4096 Sep 24 21:20 .. -rw-r--r-- 1 0 0 5 Sep 26 12:43 .secret-file drwxrwxrwx 2 1007 1007 4096 Sep 26 11:36 dir-sample1 drwxrwxrwx 2 1007 1007 4096 Sep 26 11:37 dir-sample2
環境によるのかもしれまんせんが、pオプションは使えませんでした。なのでリストで取得したものがファイルかディレクトリの判別するにはlオプションの先頭のdを頼るしかなさそうです。
- ファイル受信
ftp_get($ftp, $ローカル保存ファイル,$サーバーファイル, $mode ,$オフセット)
ftp_getでは保存先のファイルパスを指定しますが、保存先をストリームにしたい場合はftp_fgetメソッドを用います。この時、引数は保存先ファイルパスがストリームに変わる以外はftp_getと同じです。
- ファイル送信
ftp_put($ftp, $サーバーファイル,$ローカル送信ファイル, $mode ,$オフセット)
ftp_fget同様に、送信ファイルのファイルパスのかわりにストリームを指定できるftp_fputメソッドがあります。
- 追記
ftp_append($ftp,$サーバーファイル,$送信ファイル,$mode)
- カレントディレクトリ移動
ftp_chdir($ftp,$パス)
- 権限変更
ftp_chmod($ftp,$権限,$ファイル)
$権限は8進数で指定します。たとえば権限を644にしたい場合は$権限には0644とします。
戻り値は、成功時に変更後のパーミッションが返ります。失敗時はfalseが返ります。
- 削除
ftp_delete($ftp,$ファイル)
- ディレクトリ作成
ftp_mkdir($ftp,$ディレクトリ)
成功時は作成したディレクトリ名が返ります。失敗時はfalseが返ります。また、ディレクトリ既存や権限エラー時はE_WARNINGレベルの警告が出力されます。
- 名前の変更
ftp_rename($ftp, $変更前ファイル名,$変更後ファイル名)
- 任意のコマンドの実行
ftp_exec($ftp,$コマンド文字列)
任意のOSコマンドを実行したい場合に用います。戻り値はコマンドが成功したらtrue、失敗したらfalseです。
FTPサーバーで本来利用できるコマンドはFTP接続時にhelpコマンドを実行すると出力されます。ここには「pwd」がありますが「ls」は存在しません。
通常FTPクライアントからlsコマンドを利用する際は、クライアントがそれらのコマンドに変換して処理をしています。
そのようなサーバーコマンドのひとつにOSコマンドを実行するというSITEコマンドがあり、ftp_execはそのsiteコマンドを通じてOSコマンドを実行しようとします。
ただ、PHP:ftp_site関数のページにもあるように、このSITEコマンドは規格化されておらず、筆者の環境ではこのコマンドの実行は「 ftp_exec(): Unknown SITE command 」というエラーがでてほとんど実行することができませんでした。唯一実行が確認できたのは「chmod」コマンドでした。
- ftp_raw
ftp_raw($ftp,$コマンド文字列)
ftp_execは戻り値がbooleanでしたが、こちらはサーバーのメッセージがそのまま返ります。ftp_execは引数に渡したコマンドがすべてSITEに渡されるようでしたが、ftp_rawの場合はFTPコマンド(HELPやSYST、PWDなど)はそのまま実行され結果が返ってきました。
- パッシブモードの設定
ftp_pasv($ftp, $boolean)
パッシブモードの有効化と無効化をboolean値で指定します。ログイン後に実行する必要があります。
- ファイルの更新日取得
ftp_mdtm($ftp, $ファイル名)
対応しているFTPサーバーでファイルの更新日時のタイプスタンプが返ります。存在しない場合ややディレクトリの場合は-1が返ります。