首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP FTP 自動化部署實戰:增量同步、目錄遞歸與 CI/CD 集成方案

     編輯:PHP 綜合知識

在容器化、K8s 普及前,FTP 仍是許多虛擬主機、老舊項目、嵌入式設備的唯一發布通道。本文實戰拆解一套生產級 PHP FTP 同步工具,核心亮點:基於 mtime 的增量檢測、遞歸目錄遍歷、斷點續傳雛形、CodeIgniter 風格的 FTP 封裝類。

整體架構:兩大核心類協作

  • FilerFile:本地文件掃描器,負責遞歸遍歷、排除規則、mtime 緩存對比、生成增量文件列表。
  • Ftp:FTP 客戶端封裝,提供連接、上傳/下載、目錄創建/刪除、權限修改、被動模式、錯誤日誌等完整操作集。

核心模塊深度解析

1. 增量檢測機制:FilerFile::Zip()

策略:維護一個 .~~~time.php 緩存文件,存儲 [文件路徑 => mtime] 映射。每次掃描對比當前 mtime,只返回變更文件。

<?php
class FilerFile {
    private $time_path;
    private $fctimes = [];

    public function Zip($dir) {
        $this->time_path = rtrim($dir, "/") . "/.~~~time.php";
        $filelist = $this->GetFileList($dir);
        // 保存最新 mtime 映射
        file_put_contents($this->time_path, "fctimes, true) . ";");
        return $filelist;
    }

    private function GetFileList($dir, $path = "") {
        static $tmpp = "";
        if (!$tmpp) $tmpp = $dir;
        $d = dir($dir);
        $files = [];
        while ($f = $d->read()) {
            // 排除規則:隱藏文件、.svn、配置排除列表
            if ($f == '.' || $f == '..' || $f[0] == '.' || in_array($f, $_GET['exclude'])) continue;
            
            $newdir = rtrim($dir, "/") . "/" . $f;
            $abspath = $newdir;
            
            if (is_dir($newdir)) {
                $files = array_merge($files, $this->GetFileList($newdir, $newdir));
            } else {
                // 記錄 mtime
                $this->fctimes[$abspath] = filemtime($abspath);
                // 對比緩存
                if (!$this->getFileByFilectime($abspath)) continue; // 未變更跳過
                // 返回相對路徑
                $relPath = str_replace($tmpp, "", $dir) . "/" . $f;
                $files[] = ltrim($relPath, "/");
            }
        }
        return $files;
    }

    private function getFileByFilectime($file) {
        static $time_data;
        if (!$time_data) $time_data = @include_once($this->time_path) ?: [];
        // 關鍵:對比 mtime 而非 ctime,避免權限變更觸發誤傳
        return ($time_data[$file] ?? null) != filemtime($file) ? $file : false;
    }
}
?>

優化建議:大項目掃描耗時,建議引入 inotify 監聽或數據庫記錄文件哈希(MD5/SHA1)實現內容級去重。

2. FTP 客戶端封裝:Ftp 類

參考 CodeIgniter FTP 庫設計,統一錯誤處理、被動模式、調試日誌、資源管理。

<?php
class Ftp {
    private $conn_id = false;
    private $debug = true;
    private $passive = true;

    public function connect($config) {
        $this->_init($config);
        $this->conn_id = @ftp_connect($this->hostname, $this->port);
        $this->_login() && ftp_pasv($this->conn_id, true);
        return true;
    }

    // 上傳:自動判斷 ASCII/BINARY 模式
    public function upload($local, $remote, $mode = 'auto', $perm = null) {
        $mode = ($mode == 'auto') ? $this->_settype($this->_getext($local)) : $mode;
        $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
        $res = @ftp_put($this->conn_id, $remote, $local, $mode);
        if ($res && $perm) $this->chmod($remote, $perm);
        return $res;
    }

    // 遞歸創建遠程目錄(FTP 無 mkdir -p)
    public function mkdir($path, $perm = null) {
        $dirs = explode('/', $path);
        $current = '';
        foreach ($dirs as $dir) {
            $current .= $dir . '/';
            if (!$this->chgdir($current, true)) { // 抑制錯誤嘗試進入
                @ftp_mkdir($this->conn_id, $dir);
                $this->chgdir($current);
            }
        }
        return true;
    }

    // 遞歸刪除目錄
    public function delete_dir($path) {
        $list = $this->filelist($path);
        foreach ($list as $item) {
            if (!$this->delete_file($item)) $this->delete_dir($item); // 可能是子目錄
        }
        return @ftp_rmdir($this->conn_id, $path);
    }

    // 錯誤日誌統一入口
    private function _error($msg) {
        file_put_contents('/tmp/ftp_err.log', 
            "[".date('Y-m-d H:i:s')."] host:{$this->hostname} user:{$this->username} msg:{$msg}\n", 
            FILE_APPEND
        );
    }
}
?>

實戰部署腳本示例

<?php
// deploy.php - 命令行執行: php deploy.php
set_time_limit(0);
ini_set('memory_limit', '512M');

$config = [
    'hostname' => 'ftp.example.com',
    'username' => 'deploy_user',
    'password' => 'strong_password',
    'port'     => 21,
];

$localRoot = '/var/www/myproject';
$remoteRoot = '/htdocs';

// 1. 掃描增量
$scanner = new FilerFile();
$_GET['exclude'] = ['.git', 'vendor', 'node_modules', 'storage/logs', '.env'];
$changedFiles = $scanner->Zip($localRoot);

if (empty($changedFiles)) {
    echo "無文件變更,退出部署\n";
    exit(0);
}

echo "檢測到 " . count($changedFiles) . " 個文件變更,開始上傳...\n";

// 2. 連接 FTP
$ftp = new Ftp();
$ftp->connect($config);

// 3. 確保遠程根目錄存在
$ftp->mkdir($remoteRoot);

// 4. 批量上傳
$success = $failed = 0;
foreach ($changedFiles as $relFile) {
    $local = $localRoot . '/' . $relFile;
    $remote = $remoteRoot . '/' . $relFile;
    
    // 確保遠程目錄存在
    $remoteDir = dirname($remote);
    $ftp->mkdir($remoteDir);
    
    if ($ftp->upload($local, $remote)) {
        echo "[OK] $relFile\n";
        $success++;
    } else {
        echo "[FAIL] $relFile\n";
        $failed++;
    }
}

$ftp->close();
echo "部署完成:成功 $success,失敗 $failed\n";
exit($failed > 0 ? 1 : 0);
?>

CI/CD 集成方案

  1. GitLab CI:deploy 階段調用 php deploy.php,憑證存儲在 CI Variables。
  2. Jenkins:構建後步驟執行 Shell 腳本,支持參數化構建選擇環境。
  3. GitHub Actions:使用 ftp-deploy-action 或自定義 Docker 容器運行 PHP 腳本。

安全與可靠性加固

  • FTPS/SSH:生產環境強制使用 ftp_ssl_connect (FTPS) 或改用 phpseclib 實現 SFTP,明文 FTP 極不安全。
  • 冪等性:上傳前校驗遠程文件大小/哈希,避免網絡抖動導致文件截斷。
  • 回滾機制:部署前備份遠程版本至 /backup/YYYYMMDD_HHIISS/,失敗時一鍵恢復。
  • 並發控制:大量小文件上傳時,使用 pcntl_fork 或 parallel 扩展並發上傳,加速部署。

總結

這套 FTP 自動化方案雖屬「傳統技術棧」,但其增量掃描緩存設計、FTP 協議完整封裝、錯誤處理規範化的工程思想值得借鑒。現代項目建議優先選擇 Rsync over SSH、Docker Registry、K8s RollingUpdate,若受限於 FTP 環境,請務必升級為 FTPS/SFTP 並引入上述安全加固措施。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved