PHP项目目录同步如何编写PHP脚本

wen PHP项目 30

本文目录导读:

PHP项目目录同步如何编写PHP脚本

  1. 基础文件同步脚本
  2. 实时监控同步脚本(使用inotify)
  3. 基于MD5校验的增量同步脚本
  4. 简单的命令行同步工具
  5. 使用Rsync的PHP封装
  6. 使用建议

我来为您介绍几种PHP项目目录同步的脚本编写方法:

基础文件同步脚本

<?php
class DirectorySync {
    private $sourceDir;
    private $targetDir;
    private $logFile;
    public function __construct($source, $target, $log = 'sync.log') {
        $this->sourceDir = rtrim($source, '/') . '/';
        $this->targetDir = rtrim($target, '/') . '/';
        $this->logFile = $log;
    }
    public function sync() {
        $this->log("开始同步: {$this->sourceDir} -> {$this->targetDir}");
        $this->syncDirectory($this->sourceDir, $this->targetDir);
        $this->log("同步完成");
    }
    private function syncDirectory($source, $target) {
        if (!is_dir($target)) {
            mkdir($target, 0755, true);
            $this->log("创建目录: {$target}");
        }
        $items = scandir($source);
        foreach ($items as $item) {
            if ($item == '.' || $item == '..') continue;
            $sourcePath = $source . $item;
            $targetPath = $target . $item;
            if (is_file($sourcePath)) {
                $this->syncFile($sourcePath, $targetPath);
            } elseif (is_dir($sourcePath)) {
                if (!$this->shouldExclude($item)) {
                    $this->syncDirectory($sourcePath, $targetPath);
                }
            }
        }
    }
    private function syncFile($source, $target) {
        // 检查文件是否存在或是否需要更新
        if (!file_exists($target) || 
            md5_file($source) !== md5_file($target)) {
            copy($source, $target);
            $this->log("同步文件: {$source}");
        }
    }
    private function shouldExclude($name) {
        $exclude = ['.git', 'node_modules', '.idea', 'vendor'];
        return in_array($name, $exclude);
    }
    private function log($message) {
        $time = date('Y-m-d H:i:s');
        file_put_contents($this->logFile, "[{$time}] {$message}\n", FILE_APPEND);
        echo "[{$time}] {$message}\n";
    }
}
// 使用示例
$sync = new DirectorySync('/path/to/source', '/path/to/target');
$sync->sync();
?>

实时监控同步脚本(使用inotify)

<?php
class RealtimeSync {
    private $sourceDir;
    private $targetDir;
    private $watchProcess;
    public function __construct($source, $target) {
        $this->sourceDir = $source;
        $this->targetDir = $target;
    }
    public function watch() {
        // 使用 inotifywait (需要安装 inotify-tools)
        $command = sprintf(
            'inotifywait -m -r -e modify,create,delete,move %s --format "%%w%%e%%f"',
            escapeshellarg($this->sourceDir)
        );
        $descriptors = [
            0 => ['pipe', 'r'],
            1 => ['pipe', 'w'],
            2 => ['pipe', 'w']
        ];
        $this->watchProcess = proc_open($command, $descriptors, $pipes);
        if (is_resource($this->watchProcess)) {
            while ($line = fgets($pipes[1])) {
                $this->handleEvent(trim($line));
            }
        }
    }
    private function handleEvent($event) {
        // 解析事件并执行同步
        $parts = explode(' ', $event);
        $action = $parts[0] ?? '';
        $file = $parts[1] ?? '';
        switch ($action) {
            case 'CREATE':
            case 'MODIFY':
                $this->copyFile($file);
                break;
            case 'DELETE':
                $this->deleteFile($file);
                break;
            case 'MOVED_FROM':
            case 'MOVED_TO':
                $this->moveFile($file);
                break;
        }
    }
    private function copyFile($file) {
        $sourcePath = $this->sourceDir . '/' . $file;
        $targetPath = $this->targetDir . '/' . $file;
        $targetDir = dirname($targetPath);
        if (!is_dir($targetDir)) {
            mkdir($targetDir, 0755, true);
        }
        if (is_file($sourcePath)) {
            copy($sourcePath, $targetPath);
            echo "已同步: {$file}\n";
        }
    }
}
?>

基于MD5校验的增量同步脚本

<?php
class IncrementalSync {
    private $source;
    private $target;
    private $checksumFile = '.sync_checksum.json';
    public function __construct($source, $target) {
        $this->source = rtrim($source, '/');
        $this->target = rtrim($target, '/');
    }
    public function smartSync() {
        // 读取上一次的校验和
        $oldChecksums = $this->loadChecksums();
        $newChecksums = [];
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($this->source)
        );
        foreach ($iterator as $file) {
            if ($file->isFile()) {
                $relativePath = str_replace($this->source . '/', '', $file->getPathname());
                $md5 = md5_file($file->getPathname());
                $newChecksums[$relativePath] = $md5;
                // 检查是否需要同步
                if (!isset($oldChecksums[$relativePath]) || 
                    $oldChecksums[$relativePath] !== $md5) {
                    $this->syncFile($relativePath);
                }
            }
        }
        // 检测删除的文件
        foreach ($oldChecksums as $file => $checksum) {
            if (!isset($newChecksums[$file])) {
                $this->deleteFile($file);
            }
        }
        // 保存新的校验和
        $this->saveChecksums($newChecksums);
    }
    private function syncFile($relativePath) {
        $sourcePath = $this->source . '/' . $relativePath;
        $targetPath = $this->target . '/' . $relativePath;
        $targetDir = dirname($targetPath);
        if (!is_dir($targetDir)) {
            mkdir($targetDir, 0755, true);
        }
        copy($sourcePath, $targetPath);
        echo "同步: {$relativePath}\n";
    }
    private function deleteFile($relativePath) {
        $targetPath = $this->target . '/' . $relativePath;
        if (file_exists($targetPath)) {
            unlink($targetPath);
            echo "删除: {$relativePath}\n";
        }
    }
    private function loadChecksums() {
        $file = $this->target . '/' . $this->checksumFile;
        if (file_exists($file)) {
            return json_decode(file_get_contents($file), true) ?: [];
        }
        return [];
    }
    private function saveChecksums($checksums) {
        $file = $this->target . '/' . $this->checksumFile;
        file_put_contents($file, json_encode($checksums, JSON_PRETTY_PRINT));
    }
}
?>

简单的命令行同步工具

#!/usr/bin/env php
<?php
// sync.php - 命令行同步工具
if ($argc < 3) {
    echo "使用方法: php sync.php <源目录> <目标目录> [选项]\n";
    echo "选项:\n";
    echo "  --exclude=dir1,dir2  排除目录\n";
    echo "  --delete             删除目标中不存在的文件\n";
    echo "  --dry-run           模拟运行\n";
    exit(1);
}
$source = $argv[1];
$target = $argv[2];
// 解析选项
$options = [
    'exclude' => [],
    'delete' => false,
    'dry-run' => false
];
for ($i = 3; $i < $argc; $i++) {
    $arg = $argv[$i];
    if (strpos($arg, '--exclude=') === 0) {
        $options['exclude'] = explode(',', substr($arg, 10));
    } elseif ($arg == '--delete') {
        $options['delete'] = true;
    } elseif ($arg == '--dry-run') {
        $options['dry-run'] = true;
    }
}
// 执行同步
syncDirectories($source, $target, $options);
function syncDirectories($source, $target, $options) {
    if (!is_dir($source)) {
        echo "错误: 源目录不存在\n";
        exit(1);
    }
    if (!$options['dry-run'] && !is_dir($target)) {
        mkdir($target, 0755, true);
    }
    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ($files as $file) {
        $relativePath = str_replace($source . '/', '', $file->getPathname());
        // 检查排除
        foreach ($options['exclude'] as $exclude) {
            if (strpos($relativePath, $exclude) === 0) {
                continue 2;
            }
        }
        $targetPath = $target . '/' . $relativePath;
        if ($file->isDir()) {
            if (!$options['dry-run'] && !is_dir($targetPath)) {
                mkdir($targetPath, 0755, true);
                echo "创建目录: {$relativePath}\n";
            }
        } else {
            if (!file_exists($targetPath) || 
                filemtime($file) > filemtime($targetPath)) {
                if ($options['dry-run']) {
                    echo "[模拟] 同步: {$relativePath}\n";
                } else {
                    copy($file, $targetPath);
                    echo "同步: {$relativePath}\n";
                }
            }
        }
    }
    // 处理删除
    if ($options['delete']) {
        deleteRemovedFiles($source, $target, $options);
    }
}
?>

使用Rsync的PHP封装

<?php
class RsyncSync {
    private $source;
    private $target;
    private $options = '-avz --delete';
    public function __construct($source, $target) {
        $this->source = $source;
        $this->target = $target;
    }
    public function sync() {
        $command = sprintf(
            'rsync %s %s/ %s/',
            $this->options,
            escapeshellarg($this->source),
            escapeshellarg($this->target)
        );
        $output = [];
        $returnVar = 0;
        exec($command, $output, $returnVar);
        if ($returnVar === 0) {
            echo "同步成功完成\n";
            return $output;
        } else {
            echo "同步失败\n";
            return false;
        }
    }
}
?>

使用建议

  1. 选择合适的同步方式

    • 小项目:使用基础的PHP文件操作
    • 大项目:使用rsync或inotify
    • 需要实时同步:使用inotify监控
  2. 安全考虑

    • 添加错误处理
    • 设置适当的文件权限
    • 记录日志
  3. 性能优化

    • 使用文件时间戳或MD5校验
    • 排除不需要同步的目录
    • 考虑使用多线程

根据您的具体需求选择合适的脚本,并根据实际情况进行调整。

抱歉,评论功能暂时关闭!