PHPからFTPをたたこうとすると、「中身入りのディレクトリを削除(or空にする)」処理と「サブディレクトリひっくるめてアップロード」が無いという、ホントFTPコマンド投げてるだけかよっちゅうアレでもう、ね。
とりあえず、コネクションの管理ひっくるめて全部マネージャクラスに押し込めましたよ、ええ。
<?php class ftpManager { const FTPERR_CANT_CONNECT = -1; // FTPサーバへの接続に失敗 const FTPERR_LOGIN_FAILD = -2; // FTPサーバへのログインに失敗 const FTPERR_SUCCESS_BUT_DO_NOT_CHANGE_DIRECTORY = -3; // 接続とログインに成功したが、設定ファイルのdirectoryに移動できなかった private $_connected = false; private $_connect_id = null; private $_ftpconfig = null; /** * 生成子 * * @param array $config application.iniの外部サーバ設定セクション */ public function __construct($config) { $this->_ftpconfig = new Zend_Config_xml($config['path'], $config['ftp']['configsection']); } function __destruct() { // デストラクタを呼ばれる保証はない。disconnectされない場合、サーバ側からタイムアウトで切断される。 $this->disconnect(); } /** * 接続済みフラグプロパティ * */ public function getConnected() { return $this->_connected; } /** * コネクションIDプロパティ * */ public function getConnectID() { return $this->_connect_id; } /** * FTP接続実行 * * @return boolean/integer * true: 接続とログイン成功 * FTPERR_CANT_CONNECT: 接続に失敗 * FTPERR_LOGIN_FAILD : 接続は出来たがログインに失敗 * FTPERR_SUCCESS_BUT_DO_NOT_CHANGE_DIRECTORY : 接続とログインには成功したが、ftp設定情報のdirectoryに移動できなかった。 * (このエラーが返った時は、接続自体は生きている) */ public function connect() { $this->_connected = false; $this->_connect_id = ftp_connect($this->_ftpconfig->host); if ($this->_connect_id == false) { $this->_connect_id = null; return self::FTPERR_CANT_CONNECT; } else { if (ftp_login($this->_connect_id, $this->_ftpconfig->username, $this->_ftpconfig->password)) { $this->_connected = true; if (ftp_chdir($this->_connect_id, $this->_ftpconfig->directory)) { return true; } else { return self::FTPERR_SUCCESS_BUT_DO_NOT_CHANGE_DIRECTORY; } } else { $this->disconnect(); return self::FTPERR_LOGIN_FAILD; } } } /** * FTP接続を切断 * */ public function disconnect() { if (!is_null($this->_connect_id)) { ftp_close($this->_connect_id); $this->_connect_id = null; } $this->_connected = false; } /** * 指定したsource_pathにあるすべてのファイルとフォルダとサブフォルダをdest_pathにアップロードする。 * dest_path内のクリーニングはここではやらない。必要なら呼び出す前にやっておくこと。 * * @param unknown_type $source_path * @param unknown_type $dest_path * @return boolean */ public function uploadpath($source_path, $dest_path, $logging = false) { if ($this->_connected == false) { return false; } $con_id = $this->_connect_id; $current_dir = ftp_pwd($con_id); if (ftp_chdir($con_id, $dest_path) == false) { return false; } // ftpサーバ側の現在パスをアップロードのルートパスにしてからbodyを呼び出す // 初回の呼び出し時は$dest_path=''にする $this->uploadpath_body($con_id, $source_path, '', $logging); ftp_chdir($con_id, $current_dir); return true; } // ファイルアップロード本体 private function uploadpath_body($con_id, $source_path, $dest_path, $logging) { $current_dir = ftp_pwd($con_id); if ($dest_path <> '') { if (ftp_chdir($con_id, $dest_path) == false) { return false; } } if ($handle = opendir($source_path)) { while (false !== ($entry = readdir($handle))) { // カレントとパレントは飛ばす if (($entry == '.') || ($entry == '..')) { continue; } $filename = PathHelper::Combine($source_path, $entry); if (is_dir($filename)) { // 検索結果がディレクトリの場合、dest_pathに同じ名前のディレクトリを作成し、再帰処理する $newDir = PathHelper::Combine($dest_path, $entry); if (ftp_mkdir($con_id, $newDir)) { $this->uploadpath_body($con_id, $filename, $newDir, $logging); } } else { ftp_put($con_id, $entry, $filename, FTP_BINARY)); } } closedir($handle); } ftp_chdir($con_id, $current_dir); } /** * 指定したディレクトリを空にする(public呼び出し用) * * @param string $directory * @return boolean */ public function force_clear($directory) { if ($this->_connected == false) { return false; } return $this->ftp_force_clear($this->_connect_id, $directory); } // 指定したftpディレクトリを空にする-処理本体 private function ftp_force_clear($con_id, $directory) { // 現在のパスを保存しておいて、フォルダを戻す時にここに戻す $current_dir = ftp_pwd($con_id); $file_list = ftp_nlist($con_id, $directory); foreach ($file_list as $filename) { // warningを出したくないので@で抑制している。 // ftp_deleteで削除出来たらファイルだった if (@ftp_delete($con_id, $filename) == true) { continue; } // ftp_rmdirを試してみる。削除出来たら続行。 if (@ftp_rmdir($con_id, $filename) == true) { continue; } // ftp_rmdirで削除出来なかったら、その名前のフォルダに移動してみる // 移動できなかったら、$filenameがファイルでかつ削除失敗しているのでfalseを返す if (@ftp_chdir($con_id, $filename) == false) { return false; } // フォルダを戻す。失敗したら副作用が怖いのでfalseを戻して終了 if (@ftp_chdir($con_id, $current_dir) == false) { return false; } // 消せなかったフォルダに対して再帰処理 // falseが戻ってきたらfalseを戻して終了 if ($this->ftp_force_clear($con_id, $filename) == false) { return false; } // $filenameフォルダは空のはずなので削除。削除に失敗したらfalseを戻す if (@ftp_rmdir($con_id, $filename) == false) { return false; } // 削除に成功したら続行 } // ここまで来たらディレクトリ内部が空になっているハズ return true; } }
設定定義ファイルについては詳細はヒミツです。
Zend Frameworkのapplication.iniと、外部サーバの設定が書かれたxmlファイルがある、ということで。
後は、connect()してから実際のファイル操作までにタイムアウトした場合、クラス内部からソレを察知する手段が無いのがちと困る。
まぁ、システム内で実際に使う時は、connectから転送からdisconnectまで一気にやるので、そこまで手間かけて実装する意味ないからいいですケド。