From 38d3a9645b021685cf2d05c2a60ef9a97d3986e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Auswo=CC=88ger?= Date: Thu, 25 May 2023 23:26:31 +0200 Subject: [PATCH] [Filesystem] Follow symlinks when dumping files --- Filesystem.php | 6 ++++ Tests/FilesystemTest.php | 67 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/Filesystem.php b/Filesystem.php index 98725e914..23192bc74 100644 --- a/Filesystem.php +++ b/Filesystem.php @@ -669,6 +669,12 @@ public function dumpFile(string $filename, $content) $dir = \dirname($filename); + if (is_link($filename) && $linkTarget = $this->readlink($filename)) { + $this->dumpFile(Path::makeAbsolute($linkTarget, $dir), $content); + + return; + } + if (!is_dir($dir)) { $this->mkdir($dir); } diff --git a/Tests/FilesystemTest.php b/Tests/FilesystemTest.php index 768b9db6f..252635bc9 100644 --- a/Tests/FilesystemTest.php +++ b/Tests/FilesystemTest.php @@ -1091,14 +1091,16 @@ public function testReadBrokenLink() { $this->markAsSkippedIfSymlinkIsMissing(); - if ('\\' === \DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does not support creating "broken" symlinks'); + if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70400) { + $this->markTestSkipped('Windows does not support reading "broken" symlinks in PHP < 7.4.0'); } $file = $this->workspace.'/file'; $link = $this->workspace.'/link'; + touch($file); $this->filesystem->symlink($file, $link); + $this->filesystem->remove($file); $this->assertEquals($file, $this->filesystem->readlink($link)); $this->assertNull($this->filesystem->readlink($link, true)); @@ -1605,6 +1607,38 @@ public function testDumpFileOverwritesAnExistingFile() $this->assertStringEqualsFile($filename, 'bar'); } + public function testDumpFileFollowsSymlink() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + $linknameA = $this->workspace.\DIRECTORY_SEPARATOR.'bar.txt'; + $linknameB = $this->workspace.\DIRECTORY_SEPARATOR.'baz.txt'; + $this->filesystem->symlink($filename, $linknameA); + $this->filesystem->symlink($linknameA, $linknameB); + + $this->filesystem->dumpFile($linknameB, 'bar'); + + $this->assertFileExists($filename); + $this->assertFileExists($linknameA); + $this->assertFileExists($linknameB); + $this->assertStringEqualsFile($filename, 'bar'); + $this->assertStringEqualsFile($linknameA, 'bar'); + $this->assertStringEqualsFile($linknameB, 'bar'); + + // Windows does not support reading "broken" symlinks in PHP < 7.4.0 + if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70400) { + return; + } + + $this->filesystem->remove($filename); + $this->filesystem->dumpFile($linknameB, 'baz'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'baz'); + $this->assertStringEqualsFile($linknameA, 'baz'); + $this->assertStringEqualsFile($linknameB, 'baz'); + } + public function testDumpFileWithFileScheme() { $scheme = 'file://'; @@ -1678,6 +1712,35 @@ public function testAppendToFileWithResource() } } + public function testAppendToFileFollowsSymlink() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'foo'); + $linknameA = $this->workspace.\DIRECTORY_SEPARATOR.'bar.txt'; + $linknameB = $this->workspace.\DIRECTORY_SEPARATOR.'baz.txt'; + $this->filesystem->symlink($filename, $linknameA); + $this->filesystem->symlink($linknameA, $linknameB); + + $this->filesystem->appendToFile($linknameA, 'bar'); + $this->filesystem->appendToFile($linknameB, 'baz'); + + $this->assertFileExists($filename); + $this->assertFileExists($linknameA); + $this->assertFileExists($linknameB); + $this->assertStringEqualsFile($filename, 'foobarbaz'); + $this->assertStringEqualsFile($linknameA, 'foobarbaz'); + $this->assertStringEqualsFile($linknameB, 'foobarbaz'); + + $this->filesystem->remove($filename); + $this->filesystem->appendToFile($linknameB, 'foo'); + $this->filesystem->appendToFile($linknameA, 'bar'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'foobar'); + $this->assertStringEqualsFile($linknameA, 'foobar'); + $this->assertStringEqualsFile($linknameB, 'foobar'); + } + public function testAppendToFileWithScheme() { $scheme = 'file://';