initialize commit
This commit is contained in:
38
AGENTS.md
Normal file
38
AGENTS.md
Normal file
@ -0,0 +1,38 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Project Scope
|
||||
- Repository contains a small PHP CLI utility for SFTP-based sync/delete operations.
|
||||
- Main executable: `src/SFTPsync.php`
|
||||
- Connection layer: `src/SFTPconnection.php`
|
||||
|
||||
## Goals When Editing
|
||||
- Preserve backward-compatible CLI behavior unless the user explicitly requests a breaking change.
|
||||
- Keep the tool dependency-free (only PHP core + `ext-ssh2`).
|
||||
- Prefer minimal, readable changes.
|
||||
|
||||
## Important Behavior to Preserve
|
||||
- Actions are repeatable and executed in the same order as provided on CLI.
|
||||
- Exit codes:
|
||||
- `0` success
|
||||
- `1` runtime/SFTP error
|
||||
- `2` argument/usage error
|
||||
- Missing `--host`, `--user`, `--password` should still support interactive prompt mode.
|
||||
- `--skip` and `--skip-delete` matching semantics should remain stable.
|
||||
- `--delete-dir` safety guard against dangerous paths (`/`, empty path, dot paths) must remain intact.
|
||||
|
||||
## Coding Conventions
|
||||
- Target PHP 8+ compatibility.
|
||||
- Keep strict types in `src/SFTPsync.php`.
|
||||
- Avoid adding external libraries or framework structure.
|
||||
- Use clear runtime exceptions for operational failures.
|
||||
|
||||
## Validation Checklist
|
||||
- Lint changed PHP files:
|
||||
- `php -l src/SFTPsync.php`
|
||||
- `php -l src/SFTPconnection.php`
|
||||
- If CLI options are changed, update `README.md` in the same change.
|
||||
- Ensure examples in `README.md` remain executable from repository root.
|
||||
|
||||
## Documentation Rules
|
||||
- Keep `README.md` in English.
|
||||
- Document user-visible flags/behavior changes in README immediately.
|
||||
109
README.md
109
README.md
@ -1,3 +1,110 @@
|
||||
# SFTPsync
|
||||
|
||||
PHP utility for synchronize local and remote directories over SFTP.
|
||||
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:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```text
|
||||
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
|
||||
- `--skip <file_or_dir>`: repeatable, applied to `--sync` and `--sync-down`
|
||||
- `--skip-delete <file_or_dir>`: repeatable, applied to `--delete` and `--delete-dir`
|
||||
- `-h`, `--help`: show help
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# 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 cache/tmp \
|
||||
--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 slash (example: `node_modules`) matches any path segment with that name.
|
||||
- Rule with slash (example: `cache/tmp`) matches that subpath within a relative path.
|
||||
- Rules are normalized to forward slashes.
|
||||
|
||||
## 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.
|
||||
|
||||
106
doc/prompt.txt
Normal file
106
doc/prompt.txt
Normal file
@ -0,0 +1,106 @@
|
||||
----- 2026-02-15 14:10:18 -----------------------------------------------------
|
||||
**Úloha:** Vytvor CLI PHP skript `sftpsync2.php` na synchronizáciu cez SFTP. Skript musí používať triedu/vrstvu z `SFTPconnection.php` na pripojenie k SFTP (Secure FTP cez SSH). Neimplementuj pripojenie “od nuly”, iba ho používaj.
|
||||
|
||||
### 1) Požiadavky na CLI rozhranie
|
||||
|
||||
Skript musí spracovať argumenty z `argv` v štýle GNU:
|
||||
|
||||
**Povinné:**
|
||||
|
||||
* `--host <host>`
|
||||
* `--user <user>`
|
||||
* `--password <password>`
|
||||
|
||||
**Nepovinné:**
|
||||
|
||||
* `--port <port>` (default `22`)
|
||||
|
||||
**Opakovateľné akcie (môžu byť zadané viackrát, v ľubovoľnom poradí):**
|
||||
|
||||
* `--sync <local_dir> <remote_dir>`
|
||||
Synchronizácia **lokál → vzdialené** (upload).
|
||||
* `--sync-down <remote_dir> <local_dir>`
|
||||
Synchronizácia **vzdialené → lokál** (download).
|
||||
* `--delete <remote_file>`
|
||||
Zmaže vzdialený súbor.
|
||||
* `--delete-dir <remote_dir>`
|
||||
Zmaže vzdialený adresár (rekurzívne).
|
||||
|
||||
**Pravidlo:** Minimálne jedna akcia musí byť zadaná, inak vypíš help a skonči chybou.
|
||||
|
||||
### 2) Pomoc a validácia
|
||||
|
||||
* `--help` alebo `-h` vypíše použitie + príklady.
|
||||
* Validuj povinné parametre a formát vstupov.
|
||||
* Pri chybe argumentov vráť exit code `2`.
|
||||
* Pri zlyhaní pripojenia alebo SFTP operácie vráť exit code `1`.
|
||||
* Pri úspechu `0`.
|
||||
|
||||
### 3) Poradie spracovania akcií
|
||||
|
||||
* Spracuj akcie **presne v poradí**, v akom boli zadané v CLI.
|
||||
* Ak jedna akcia zlyhá, skonči a ďalšie už nespúšťaj (fail-fast), pokiaľ nie je explicitne uvedené inak.
|
||||
|
||||
### 4) Synchronizácia: špecifikácia správania
|
||||
|
||||
Pre `--sync` (upload) a `--sync-down` (download):
|
||||
|
||||
* Rekurzívne prechádzaj zdrojový adresár.
|
||||
* Prenášaj súbory, ktoré:
|
||||
|
||||
* neexistujú na cieli, alebo
|
||||
* majú odlišnú veľkosť, alebo
|
||||
* majú novší `mtime` na zdroji (ak sa dá zistiť na oboch stranách; ak nie, používaj veľkosť + checksum nie je povinný).
|
||||
* Vytváraj chýbajúce adresáre na cieli.
|
||||
* Zachovaj relatívnu štruktúru ciest.
|
||||
* Loguj pre každý súbor jednu z akcií: `SKIP`, `UPLOAD`, `DOWNLOAD`, `MKDIR`, `DELETE`, `RMDIR`, `ERROR`.
|
||||
|
||||
**Poznámka:** Nevyžadujem “mirror delete” (mazanie súborov, ktoré nie sú na zdroji) — sync je iba kopírovanie/aktualizácia. Mazanie sa robí výlučne cez `--delete` a `--delete-dir`.
|
||||
|
||||
### 5) Mazanie
|
||||
|
||||
* `--delete <remote_file>`:
|
||||
|
||||
* Skontroluj existenciu (ak sa dá); ak neexistuje, loguj `SKIP` a pokračuj.
|
||||
* `--delete-dir <remote_dir>`:
|
||||
|
||||
* Rekurzívne zmaž obsah (súbory aj podadresáre), potom samotný adresár.
|
||||
* Chráň sa pred nebezpečnými cestami: odmietni `""`, `/`, `.` a podobné “root-like” hodnoty (bezpečnostná poistka).
|
||||
|
||||
### 6) Integrácia so SFTPconnection.php
|
||||
|
||||
* Predpokladaj, že `SFTPconnection.php` poskytuje objekt na:
|
||||
|
||||
* pripojenie (`connect()`),
|
||||
* upload/download súboru,
|
||||
* listovanie adresára,
|
||||
* vytváranie adresára,
|
||||
* zisťovanie `stat` (min. veľkosť a mtime ak dostupné),
|
||||
* mazanie súboru a adresára.
|
||||
* Ak API nie je úplne jasné, sprav adaptér/obalové metódy v `sftpsync2.php`, ktoré volajú existujúce metódy a centralizujú rozdiely.
|
||||
|
||||
### 7) Kódové štandardy
|
||||
|
||||
* PHP 8+.
|
||||
* Žiadne externé balíčky.
|
||||
* Striktné typy (`declare(strict_types=1);`).
|
||||
* Prehľadná štruktúra: parser argumentov, runner akcií, sync funkcie, pomocné utily (cesty, logovanie).
|
||||
* Logovanie na STDOUT, chyby na STDERR.
|
||||
* Jasné komentáre tam, kde je to neintuitívne.
|
||||
|
||||
### 8) Príklady použitia (musíš ich uviesť v help)
|
||||
|
||||
* Upload sync:
|
||||
|
||||
* `php sftpsync2.php --host example.com --user u --password p --sync ./local /var/www`
|
||||
* Download sync:
|
||||
|
||||
* `php sftpsync2.php --host example.com --user u --password p --sync-down /var/backups ./backups`
|
||||
* Viac akcií:
|
||||
|
||||
* `php sftpsync2.php --host example.com --user u --password p --sync ./a /remote/a --delete /remote/a/old.zip --sync-down /remote/logs ./logs`
|
||||
* Delete dir:
|
||||
|
||||
* `php sftpsync2.php --host example.com --user u --password p --delete-dir /tmp/testdir`
|
||||
|
||||
**Výstup:** Vygeneruj kompletný obsah súboru `sftpsync2.php` (jeden súbor), pripravený na spustenie.
|
||||
276
src/SFTPconnection.php
Normal file
276
src/SFTPconnection.php
Normal file
@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
// https://www.php.net/manual/en/function.ssh2-sftp.php#120853
|
||||
class SFTPConnection
|
||||
{
|
||||
|
||||
private $connection;
|
||||
private $sftp;
|
||||
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function __construct($host, $port = 22)
|
||||
{
|
||||
$this->connection = @ssh2_connect($host, $port);
|
||||
if (! $this->connection)
|
||||
throw new Exception("Could not connect to SFTP host $host on port $port.");
|
||||
}
|
||||
|
||||
public function login($username, $password)
|
||||
{
|
||||
if (! @ssh2_auth_password($this->connection, $username, $password))
|
||||
throw new Exception("Could not authenticate with username $username " . "and password $password.");
|
||||
$this->sftp = ssh2_sftp($this->connection);
|
||||
return $this->sftp !== false;
|
||||
}
|
||||
|
||||
public function uploadFile($local_file, $remote_file)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
$remote_dir = dirname($remote_file);
|
||||
if (!file_exists("ssh2.sftp://$sftp$remote_dir")) {
|
||||
mkdir("ssh2.sftp://$sftp$remote_dir", 0777, true);
|
||||
}
|
||||
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'w');
|
||||
if (! $stream)
|
||||
throw new Exception("Could not open SFTP file for writing: $remote_file");
|
||||
$data_to_send = @file_get_contents($local_file);
|
||||
if ($data_to_send === false)
|
||||
throw new Exception("Could not open local file: $local_file.");
|
||||
if (@fwrite($stream, $data_to_send) === false)
|
||||
throw new Exception("Could not send data from file: $local_file.");
|
||||
@fclose($stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function sendFile($local_file, $remote_file)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
$remote_dir = dirname($remote_file);
|
||||
if (!file_exists("ssh2.sftp://$sftp$remote_dir")) {
|
||||
mkdir("ssh2.sftp://$sftp$remote_dir", 0777, true);
|
||||
}
|
||||
return ssh2_scp_send ($this->connection, $local_file, $remote_file);
|
||||
}
|
||||
|
||||
public function uploadFileContent($content, $remote_file)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
$remote_dir = dirname($remote_file);
|
||||
if (!file_exists("ssh2.sftp://$sftp$remote_dir")) {
|
||||
mkdir("ssh2.sftp://$sftp$remote_dir", 0777, true);
|
||||
}
|
||||
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'w');
|
||||
if (! $stream)
|
||||
throw new Exception("Could not open SFTP file for writing: $remote_file");
|
||||
if (@fwrite($stream, $content) === false)
|
||||
throw new Exception("Could not send data to file: $remote_file.");
|
||||
@fclose($stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function changeTimestamp($remote_file, $timestamp)
|
||||
{
|
||||
$date = date("YmdHi.s", is_numeric($timestamp) ? $timestamp : strtotime($timestamp));
|
||||
$cmd = "touch -t $date \"$remote_file\"";
|
||||
$stream = @ssh2_exec($this->connection, $cmd);
|
||||
if ($stream === false) return false;
|
||||
stream_set_blocking($stream, true);
|
||||
// $ret = stream_get_contents($stream); print_r($ret);
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function changeTimestamp2($remote_file, $timestamp)
|
||||
{
|
||||
$date = date("YmdHi.s", is_numeric($timestamp) ? $timestamp : strtotime($timestamp));
|
||||
return touch('ssh2://'.$this->sftp.$remote_file, $date);
|
||||
}
|
||||
|
||||
function scanFilesystem($remote_file)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
$dir = "ssh2.sftp://$sftp$remote_file";
|
||||
$tempArray = array();
|
||||
|
||||
if (is_dir($dir)) {
|
||||
if ($dh = opendir($dir)) {
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
$filetype = filetype($dir . $file);
|
||||
if ($filetype == "dir") {
|
||||
$tmp = $this->scanFilesystem($remote_file . $file . "/");
|
||||
foreach ($tmp as $t) {
|
||||
$tempArray[] = $file . "/" . $t;
|
||||
}
|
||||
} else {
|
||||
$tempArray[] = $file;
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
|
||||
return $tempArray;
|
||||
}
|
||||
|
||||
public function receiveFile($remote_file, $local_file)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'r');
|
||||
if (! $stream)
|
||||
throw new Exception("Could not open SFTP file for reading: $remote_file");
|
||||
$contents = fread($stream, filesize("ssh2.sftp://$sftp$remote_file"));
|
||||
file_put_contents($local_file, $contents);
|
||||
@fclose($stream);
|
||||
}
|
||||
|
||||
public function statFile($remote_file)
|
||||
{
|
||||
$statinfo = @ssh2_sftp_stat($this->sftp, $remote_file);
|
||||
return $statinfo;
|
||||
}
|
||||
|
||||
public function hashFile($remote_file)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
$hash = hash_file('sha256', "ssh2.sftp://$sftp$remote_file");
|
||||
return $hash;
|
||||
}
|
||||
|
||||
public function fileExists($remote_file)
|
||||
{
|
||||
$statinfo = @ssh2_sftp_stat($this->sftp, $remote_file);
|
||||
return $statinfo !== false;
|
||||
}
|
||||
|
||||
public function deleteFile($remote_file)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
unlink("ssh2.sftp://$sftp$remote_file");
|
||||
}
|
||||
|
||||
public function deleteDir($remote_dir)
|
||||
{
|
||||
$sftp = $this->sftp;
|
||||
$remote_dir = rtrim($remote_dir, "/");
|
||||
|
||||
if ($remote_dir === '' || $remote_dir === '.') {
|
||||
throw new Exception("Remote directory path is empty.");
|
||||
}
|
||||
|
||||
$dir = "ssh2.sftp://$sftp$remote_dir";
|
||||
$items = @scandir($dir);
|
||||
if ($items === false) {
|
||||
throw new Exception("Could not open remote directory: $remote_dir");
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item === '.' || $item === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$child_remote_path = $remote_dir . '/' . $item;
|
||||
$child = "ssh2.sftp://$sftp$child_remote_path";
|
||||
|
||||
if (is_dir($child)) {
|
||||
$this->deleteDir($child_remote_path);
|
||||
} else {
|
||||
if (! @unlink($child)) {
|
||||
throw new Exception("Could not delete remote file: $child_remote_path");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! @rmdir($dir)) {
|
||||
throw new Exception("Could not delete remote directory: $remote_dir");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function dirSync($local_dir, $remote_dir, $ignore = array(), $print = false, $print_only_upload = false)
|
||||
{
|
||||
$local_dir = rtrim($local_dir, "/");
|
||||
$remote_dir = rtrim($remote_dir, "/");
|
||||
$files = scandir($local_dir);
|
||||
$ignore_exts = array();
|
||||
if (is_array($ignore)) foreach ($ignore as $key => $value) {
|
||||
if (substr($value, 0, 1) == '*') {
|
||||
$ignore_exts[] = substr($value, 1);
|
||||
}
|
||||
}
|
||||
$msg = '';
|
||||
foreach ($files as $file) {
|
||||
if ($file == "."
|
||||
|| $file == ".."
|
||||
|| in_array($file, $ignore)
|
||||
|| in_array(strtolower(substr($file, strrpos($file, "."))), $ignore_exts))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (is_dir($local_dir . "/" . $file)) {
|
||||
$this->dirSync($local_dir . "/" . $file, $remote_dir . "/" . $file, $ignore, $print, $print_only_upload);
|
||||
} else {
|
||||
$local_file_size = filesize($local_dir . "/" . $file);
|
||||
$local_file_mtime = filemtime($local_dir . "/" . $file);
|
||||
$remote_file_info = $this->statFile($remote_dir . "/" . $file);
|
||||
if ($print) {
|
||||
if ($print_only_upload) {
|
||||
echo str_repeat(' ', strlen($msg)) . "\r";
|
||||
}
|
||||
$msg = $local_file_size . " " . $local_file_mtime . ' ' . date('c', $local_file_mtime) . ' ' . $local_dir . "/" . $file;
|
||||
if ($remote_file_info !== false) {
|
||||
$msg2 = $remote_file_info["size"] . " " . $remote_file_info["mtime"] . ' ' . date('c', $remote_file_info["mtime"]) . ' ' . $remote_dir . "/" . $file;
|
||||
} else {
|
||||
$msg2 = '--- no exists ' . $remote_dir . "/" . $file;
|
||||
}
|
||||
if ($print_only_upload) {
|
||||
echo $msg . "\r";
|
||||
} else {
|
||||
echo $msg . "\n" . $msg2 . "\n";
|
||||
}
|
||||
}
|
||||
if ($remote_file_info == false
|
||||
|| $local_file_size != $remote_file_info["size"]
|
||||
|| $local_file_mtime > $remote_file_info["mtime"])
|
||||
{
|
||||
if ($print_only_upload) {
|
||||
echo "\n" .$msg2 . "\n";
|
||||
}
|
||||
if ($print) echo 'Upload: ' . $local_dir . "/" . $file . ' ... ';
|
||||
$suc = $this->uploadFile($local_dir . "/" . $file, $remote_dir . "/" . $file);
|
||||
if ($print) echo ($suc ? "✅ OK\n" : "⛔ ERROR\n");
|
||||
$this->changeTimestamp($remote_dir . "/" . $file, $local_file_mtime);
|
||||
if ($print) echo "\n";
|
||||
}
|
||||
if ($print && ! $print_only_upload) echo "\n";
|
||||
}
|
||||
}
|
||||
if ($print_only_upload) {
|
||||
echo str_repeat(' ', strlen($msg)) . "\r";
|
||||
}
|
||||
}
|
||||
|
||||
public function fileSync($local_file, $remote_file, $print = false)
|
||||
{
|
||||
if ($print) echo '➡️ Check file: ' . $local_file . ' ... ';
|
||||
$local_file_size = filesize($local_file);
|
||||
$local_file_mtime = filemtime($local_file);
|
||||
$remote_file_info = $this->statFile($remote_file);
|
||||
if ($remote_file_info == false
|
||||
|| $local_file_size != $remote_file_info["size"]
|
||||
|| $local_file_mtime > $remote_file_info["mtime"])
|
||||
{
|
||||
if ($print) echo "🟠 changed\n";
|
||||
if ($print) echo '⬆️ Upload: ' . $local_file . ' ... ';
|
||||
$suc = $this->uploadFile($local_file, $remote_file);
|
||||
if ($print) echo ($suc ? "✅ OK\n" : "⛔ ERROR\n");
|
||||
$this->changeTimestamp($remote_file, filemtime($local_file));
|
||||
if ($print) echo "\n";
|
||||
} else {
|
||||
if ($print) echo "🟢 OK\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
1152
src/SFTPsync.php
Normal file
1152
src/SFTPsync.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user