diff --git a/packages/filesystem/src/FlySystemAdapter.php b/packages/filesystem/src/FlySystemAdapter.php index 8b14497da..6311f2ec0 100644 --- a/packages/filesystem/src/FlySystemAdapter.php +++ b/packages/filesystem/src/FlySystemAdapter.php @@ -34,11 +34,11 @@ public static function createForPath(string $path): self { if (class_exists(Local::class)) { /** @phpstan-ignore-next-line */ - $filesystem = new FlysystemV1(new LeagueFilesystem(new Local($path))); + $filesystem = new FlysystemV1(new LeagueFilesystem(new Local($path, linkHandling: Local::SKIP_LINKS))); } else { $filesystem = new FlysystemV3( new LeagueFilesystem( - new LocalFilesystemAdapter($path), + new LocalFilesystemAdapter($path, linkHandling: LocalFilesystemAdapter::SKIP_LINKS), ), ); } diff --git a/packages/filesystem/tests/unit/FlySystemAdapterTest.php b/packages/filesystem/tests/unit/FlySystemAdapterTest.php new file mode 100644 index 000000000..ba35a9d49 --- /dev/null +++ b/packages/filesystem/tests/unit/FlySystemAdapterTest.php @@ -0,0 +1,84 @@ +root = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('guides-fs-', true); + mkdir($this->root, 0777, true); + + file_put_contents($this->root . DIRECTORY_SEPARATOR . 'index.rst', 'Index'); + + if (@symlink($this->root . DIRECTORY_SEPARATOR . 'index.rst', $this->root . DIRECTORY_SEPARATOR . 'link.rst') !== false) { + return; + } + + self::markTestSkipped('The filesystem does not support symbolic links'); + } + + protected function tearDown(): void + { + $this->remove($this->root); + } + + public function testItListsADirectoryContainingSymbolicLinksInsteadOfAborting(): void + { + $contents = FlySystemAdapter::createForPath($this->root)->listContents(''); + + $names = array_map(static fn (StorageAttributes $item): mixed => $item['basename'], $contents); + + self::assertContains('index.rst', $names, 'A regular file next to a symbolic link must still be listed'); + self::assertNotContains('link.rst', $names, 'A symbolic link is skipped rather than aborting the listing'); + } + + /** Removes a tree without following the symbolic links inside it. */ + private function remove(string $path): void + { + if (is_link($path) || !is_dir($path)) { + unlink($path); + + return; + } + + foreach (scandir($path) ?: [] as $entry) { + if ($entry === '.' || $entry === '..') { + continue; + } + + $this->remove($path . DIRECTORY_SEPARATOR . $entry); + } + + rmdir($path); + } +}