Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -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
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 1 addition & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
15 changes: 13 additions & 2 deletions src/FileListing/TransferAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
}
14 changes: 4 additions & 10 deletions tests/Integration/MultiDirectionalFileSyncTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 4 additions & 10 deletions tests/Integration/OneWayFileSyncTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Loading