diff --git a/src/Filesystem/Filesystem.php b/src/Filesystem/Filesystem.php index 22c5c161..cff922ac 100644 --- a/src/Filesystem/Filesystem.php +++ b/src/Filesystem/Filesystem.php @@ -231,6 +231,34 @@ public function isLocalDisk(\Illuminate\Filesystem\FilesystemAdapter $disk): boo return ($disk->getAdapter() instanceof \League\Flysystem\Local\LocalFilesystemAdapter); } + /** + * Apply a unique index to a filename from provided list i.e. + * winter.txt, [winter_1.txt, winter_2.txt] -> winter_3.txt + * winter.txt, [winter_1.txt, winter_3.txt] -> winter_4.txt + */ + public function unique(string $str, array $list, string $separator = '_', int $starting = 1, int $step = 1): string + { + $indexes = []; + + $info = pathinfo($str); + + if (empty($info['filename']) || empty($info['extension'])) { + throw new \InvalidArgumentException('$str must be a file name'); + } + + foreach ($list as $item) { + if (!preg_match('/' . $info['filename'] . $separator . '(\d*)\.' . $info['extension'] . '/', $item, $matches)) { + continue; + } + + $indexes[] = (int) $matches[1]; + } + + return empty($indexes) + ? $info['filename'] . $separator . $starting . '.' . $info['extension'] + : $info['filename'] . $separator . (max($indexes) + $step) . '.' . $info['extension']; + } + /** * Finds the path of a given class. * diff --git a/src/Support/Facades/File.php b/src/Support/Facades/File.php index df56aac4..ed24ada9 100644 --- a/src/Support/Facades/File.php +++ b/src/Support/Facades/File.php @@ -55,6 +55,7 @@ * @method static bool fileNameMatch(string $fileName, string $pattern) * @method static bool copyBetweenDisks(string|FilesystemAdapter $sourceDisk, string|FilesystemAdapter $destinationDisk, string $filePath, ?string $targetPath = null) * @method static bool moveBetweenDisks(string|FilesystemAdapter $sourceDisk, string|FilesystemAdapter $destinationDisk, string $filePath, ?string $targetPath = null) + * @method static string unique(string $str, array $list, string $separator = '_', int $starting = 1, int $step = 1) * * @see \Winter\Storm\Filesystem\Filesystem */ diff --git a/tests/Filesystem/FilesystemTest.php b/tests/Filesystem/FilesystemTest.php index b9239e2b..a67a6380 100644 --- a/tests/Filesystem/FilesystemTest.php +++ b/tests/Filesystem/FilesystemTest.php @@ -11,6 +11,27 @@ public function setUp(): void $this->filesystem = new Filesystem(); } + public function testUnique() + { + $cases = [ + // File exists, make it unique + 'winter_1.cms' => ['winter.cms', ['winter.cms', 'test_5']], + + // File already unique, return original + 'winter.cms' => ['winter.cms', ['winter_1.cms']], + + // Last index available is incremented + 'winter_4.cms' => ['winter.cms', ['winter_1.cms', 'test_5', 'winter_3.cms']], + 'winter_98.cms' => ['winter.cms', ['winter_97.cms', 'test_5', 'winter_1.cms']], + + // Separator as space + 'winter 1.cms' => ['winter.cms', ['winter_1.cms', 'test_5', 'winter_3.cms'], ' '], + ]; + foreach ($cases as $output => $config) { + $this->assertSame($output, $this->filesystem->unique(...$config)); + } + } + /** * @dataProvider providePathsForIsAbsolutePath * @see Symfony\Component\Filesystem\Tests\FilesystemTest::testIsAbsolutePath