Files
SFTPsync/README.md

4.5 KiB

SFTPsync

Small PHP CLI utility to synchronize directories and manage files on a remote server over SFTP.

What It Does

  • Sync local directory to remote (--sync)
  • Sync remote directory to local (--sync-down)
  • Delete one remote file (--delete)
  • Delete remote directory recursively (--delete-dir)
  • Combine multiple actions in one run (executed in CLI order)
  • Skip selected paths during sync or delete using repeatable rules

Requirements

  • PHP 8+ (CLI)
  • PHP ssh2 extension enabled (ssh2_connect, ssh2_sftp, ...)
  • Network access from your machine to the target SFTP host

Quick Start

From repository root:

php src/SFTPsync.php --host example.com --user myuser --password mypass --sync ./local /var/www/app

If --host, --user, or --password is missing, the script asks for it interactively (TTY only).

CLI Usage

php src/SFTPsync.php --host <host> --user <user> --password <password> [--port <port>] <actions...>

Actions (repeatable)

  • --sync <local_dir> <remote_dir>: upload local changes to remote
  • --sync-down <remote_dir> <local_dir>: download remote changes to local
  • --delete <remote_file>: delete one remote file
  • --delete-dir <remote_dir>: delete remote directory recursively (with safety checks)

Options

  • --host <host>: required (or prompted)
  • --user <user>: required (or prompted)
  • --password <password>: required (or prompted)
  • --port <port>: optional, default 22
  • --print-relative: show paths relative to action root in logs
  • --no-print-skip: suppress SKIP status lines during execution
  • --skip <pattern>: repeatable, exact names/paths or glob patterns (*, ?), applied to --sync and --sync-down
  • --skip-delete <pattern>: repeatable, exact names/paths or glob patterns (*, ?), applied to --delete and --delete-dir
  • -h, --help: show help

Examples

# Upload local -> remote
php src/SFTPsync.php --host example.com --user u --password p --sync ./app /srv/app

# Download remote -> local
php src/SFTPsync.php --host example.com --user u --password p --sync-down /srv/backups ./backups

# Multiple actions in one run (executed left-to-right)
php src/SFTPsync.php --host example.com --user u --password p \
  --sync ./a /remote/a \
  --delete /remote/a/old.zip \
  --sync-down /remote/logs ./logs

# Skip selected entries during sync
php src/SFTPsync.php --host example.com --user u --password p \
  --skip .git --skip node_modules --skip "*.log" --skip "cache/*" \
  --sync ./app /srv/app

# Delete remote directory but keep selected subpaths
php src/SFTPsync.php --host example.com --user u --password p \
  --skip-delete .well-known --skip-delete uploads/keep \
  --delete-dir /srv/app

Sync Behavior

For each file pair, transfer happens when:

  • target file does not exist, or
  • file size differs, or
  • source mtime is newer than target mtime

After upload/download, mtime is propagated to the target when possible.

Skip Rule Matching

  • Rule without wildcard characters (example: node_modules) keeps exact matching.
  • Exact rule without slash (example: node_modules) matches any path segment with that name.
  • Exact rule with slash (example: cache/tmp) matches that subpath within a relative path.
  • Rule containing * or ? is treated as a glob pattern. * matches any characters, and ? matches one character.
  • Glob rule without slash (example: *.log) can match file or directory names at any depth.
  • Glob rule with slash (example: src/temp/*.log or cache/*) is matched against relative paths.
  • Rules are normalized to forward slashes.

Examples:

  • --skip=*.bat skips test.bat and tools/deploy.bat, but not test.bat.txt.
  • --skip=*.log skips app.log and src/temp/test.log, but not app.log.1.
  • --skip=backup-* skips backup-2025, backup-old, and backup-test.
  • --skip=cache/* skips content under cache.
  • --skip=node_modules and --skip=.git keep the original exact-name behavior.

Safety Notes

  • --delete-dir refuses dangerous roots such as empty path, /, ., .., and similar dot paths.
  • Delete operations run only on the remote side.
  • Path handling normalizes slashes and trims duplicate separators.

Exit Codes

  • 0 success
  • 1 runtime/SFTP error
  • 2 invalid CLI arguments

Output

The script prints status lines (MKDIR, UPLOAD, DOWNLOAD, DELETE, RMDIR, SKIP, ERROR) and a final summary with operation counters.

Use --no-print-skip if you want to keep skipped-item accounting in the final summary but hide individual SKIP log lines during the run.