From b955b0fa89824082d97fea011cd8c228365c7944 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Sat, 18 Jul 2026 02:14:14 +0100 Subject: [PATCH] Add documentation and reliable tests --- .github/workflows/tests.yml | 29 ++++++++++ README.md | 58 ++++++++++++++++++- composer.json | 5 +- src/FileListing/TransferAction.php | 15 ++++- .../MultiDirectionalFileSyncTest.php | 14 ++--- tests/Integration/OneWayFileSyncTest.php | 14 ++--- 6 files changed, 108 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..bc7a9bd --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,29 @@ +name: Tests + +on: + push: + branches: + - master + pull_request: + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - php-version: '7.1' + composer-version: 'v2.2' + - php-version: '8.5' + composer-version: 'latest' + + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + tools: composer:${{ matrix.composer-version }} + coverage: none + - run: composer update --no-interaction --prefer-dist --no-progress + - run: vendor/bin/phpunit diff --git a/README.md b/README.md index ea77d85..ff4ce3f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,58 @@ # PHP File Sync -Synchronise files between multiple local or remote file systems + +[![Tests](https://github.com/Jord-JD/php-file-sync/actions/workflows/tests.yml/badge.svg)](https://github.com/Jord-JD/php-file-sync/actions/workflows/tests.yml) + +Synchronise files between multiple local or remote filesystems through Flysystem 1.x. + +## Installation + +```bash +composer require jord-jd/php-file-sync +``` + +Create each filesystem with the appropriate Flysystem adapter. The examples below use local adapters, but the synchronisation strategies work with any Flysystem 1.x `Filesystem` instance. + +```php +use League\Flysystem\Adapter\Local; +use League\Flysystem\Filesystem; + +$source = new Filesystem(new Local('/path/to/source')); +$destination = new Filesystem(new Local('/path/to/destination')); +``` + +## One-way synchronisation + +One-way synchronisation copies files that are missing from the destination, plus source files whose timestamps are newer. It does not delete destination-only files. + +```php +use JordJD\FileSync\FileSync; + +(new FileSync()) + ->oneWay() + ->from($source) + ->to($destination) + ->begin(); +``` + +## Multi-directional synchronisation + +Multi-directional synchronisation makes the newest version of each file available on every configured filesystem. Files that exist on only one filesystem are copied to the others. + +```php +(new FileSync()) + ->multiDirectional() + ->with($filesystemA) + ->with($filesystemB) + ->with($filesystemC) + ->begin(); +``` + +Add `->withProgressBar()` before `->begin()` on either strategy to show command-line progress. + +## Important behavior + +- Paths are compared exactly as returned by Flysystem. +- Modification timestamps decide which version is newer. +- Existing files may be overwritten by newer copies. +- Deletions are not propagated. +- Check the boolean result or exceptions produced by your Flysystem adapter when storage failures need application-specific handling. diff --git a/composer.json b/composer.json index 0553486..e94b140 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,7 @@ "nesbot/carbon": "^1.0||^2.27" }, "require-dev": { - "phpunit/phpunit": "^9.6", - "fakerphp/faker": "^1.23", - "php-coveralls/php-coveralls": "^2.0", - "cache/array-adapter": "^1.0" + "phpunit/phpunit": "^7.5||^9.6" }, "autoload": { "psr-4": { diff --git a/src/FileListing/TransferAction.php b/src/FileListing/TransferAction.php index 51ef894..be51c57 100644 --- a/src/FileListing/TransferAction.php +++ b/src/FileListing/TransferAction.php @@ -20,6 +20,17 @@ public function __construct(string $path, Filesystem $sourceFilesystem, Filesyst public function transfer(): bool { $stream = $this->sourceFilesystem->readStream($this->path); - return $this->destinationFilesystem->putStream($this->path, $stream); + + if ($stream === false) { + return false; + } + + try { + return $this->destinationFilesystem->putStream($this->path, $stream); + } finally { + if (is_resource($stream)) { + fclose($stream); + } + } } -} \ No newline at end of file +} diff --git a/tests/Integration/MultiDirectionalFileSyncTest.php b/tests/Integration/MultiDirectionalFileSyncTest.php index 76150ce..73dcc9f 100644 --- a/tests/Integration/MultiDirectionalFileSyncTest.php +++ b/tests/Integration/MultiDirectionalFileSyncTest.php @@ -24,17 +24,11 @@ public function setupDirectory($name) mkdir($path, 0777, true); } - $numFiles = random_int(1, 100); - - $faker = \Faker\Factory::create(); - - $monthInSeconds = 2592000; - - for ($i=0; $i < $numFiles; $i++) { - $filename = $faker->word().'.txt'; - $content = $faker->text(random_int(5, 1000)); + for ($i=0; $i < 3; $i++) { + $filename = $name.'-'.$i.'.txt'; + $content = 'Test content '.$name.' '.$i; file_put_contents($path.$filename, $content); - touch($path.$filename, time() - random_int(0, $monthInSeconds)); + touch($path.$filename, time() - $i); } $adapter = new Local($path); diff --git a/tests/Integration/OneWayFileSyncTest.php b/tests/Integration/OneWayFileSyncTest.php index da393f6..dc3a65e 100644 --- a/tests/Integration/OneWayFileSyncTest.php +++ b/tests/Integration/OneWayFileSyncTest.php @@ -26,17 +26,11 @@ public function setupDirectory($name, $fillWithFiles = true) if ($fillWithFiles) { - $numFiles = random_int(1, 100); - - $faker = \Faker\Factory::create(); - - $monthInSeconds = 2592000; - - for ($i=0; $i < $numFiles; $i++) { - $filename = $faker->word().'.txt'; - $content = $faker->text(random_int(5, 1000)); + for ($i=0; $i < 3; $i++) { + $filename = $name.'-'.$i.'.txt'; + $content = 'Test content '.$i; file_put_contents($path.$filename, $content); - touch($path.$filename, time() - random_int(0, $monthInSeconds)); + touch($path.$filename, time() - $i); } }