導入
業務システムにおいて、外部サーバーとの連携や定時バックアップ、古いレガシーシステムとのファイル同期を行う際、PHPからFTPプロトコルを操作する機会は依然として多いです。PHP標準のFTP関数は非常に強力ですが、毎回直接呼び出すと処理が煩雑になり、接続管理やエラーハンドリングの漏れが発生しやすくなります。今回は、FTP操作をクラス化し、実務で使いやすくするための設計と実装のポイントを解説します。
基礎知識
PHPでFTPを扱うには、主に「FTP拡張モジュール」を使用します。FTP通信には「アクティブモード」と「パッシブモード(PASVモード)」の2種類がありますが、現代のファイアウォール環境では、通信経路を制御しやすいPASVモードが一般的です。また、ファイル転送には「ASCIIモード(テキスト用)」と「BINARYモード(画像やアーカイブ用)」があり、これを適切に選択しないと、アップロード後にファイルが破損する原因となります。
実装/解決策
実務でFTPクラスを扱う際は、以下の3点を意識した設計が重要です。
1. 接続管理の抽象化: 接続・認証・切断の流れをメソッドに集約し、例外処理を適切に行うこと。
2. モードの自動判定: 拡張子に基づき転送モードを自動選択するロジックを組み込み、ミスを減らすこと。
3. パス操作の堅牢性: ファイルパスの結合時にスラッシュの有無を確認し、パスのズレによるエラーを未然に防ぐこと。
サンプルプログラム
以下は、現場で必要となる基本的な操作をパッケージ化したFTPクラスのサンプルです。
/
class FtpClient {
private $conn;
// 接続とログイン
public function connect($host, $user, $pass, $port = 21, $timeout = 30) {
$this->conn = @ftp_connect($host, $port, $timeout);
if (!$this->conn) return false;
$login = @ftp_login($this->conn, $user, $pass);
if (!$login) return false;
// 実務ではPASVモードを有効にするのが定石
ftp_pasv($this->conn, true);
return true;
}
// 転送モードの自動判定
private function getTransferMode($filename) {
$asciiExt = [‘txt’, ‘csv’, ‘html’, ‘php’, ‘js’, ‘css’, ‘json’, ‘xml’];
$ext = pathinfo($filename, PATHINFO_EXTENSION);
return in_array(strtolower($ext), $asciiExt) ? FTP_ASCII : FTP_BINARY;
}
// アップロード
public function upload($localFile, $remotePath) {
$mode = $this->getTransferMode($localFile);
return ftp_put($this->conn, $remotePath, $localFile, $mode);
}
// 接続解除
public function close() {
if ($this->conn) {
ftp_close($this->conn);
}
}
}
// 実行例
$ftp = new FtpClient();
if ($ftp->connect(‘ftp.example.com’, ‘user’, ‘password’)) {
$ftp->upload(‘/var/www/data.csv’, ‘/remote/data.csv’);
$ftp->close();
}
?>
応用・注意点
実務で運用する上で、以下の点に注意してください。
1. セキュリティの確保
FTPは通信内容が平文で送られるため、機密情報を扱う場合はSFTP(SSH File Transfer Protocol)の利用を強く推奨します。PHPでは「ssh2_sftp」拡張を使用することで、同様の操作が可能です。
2. エラーログの記録
サンプルでは単純な真偽値を返していますが、本番環境では「どのファイルで失敗したか」「FTPサーバーからどのようなレスポンスが返ってきたか」をログ出力するようにしてください。`ftp_raw()`関数を使うと、サーバーからの生の応答を取得でき、詳細なデバッグに役立ちます。
3. タイムアウト設定
cronなどで大規模なファイルを転送する場合、デフォルトのタイムアウト設定では転送が途中で切れることがあります。`set_time_limit()`やFTPのタイムアウトオプションを適切に調整してください。