8000 [Filesystem] Add a cross-platform readlink method · symfony/symfony@7f5981e · GitHub
[go: up one dir, main page]

Skip to content

Commit 7f5981e

Browse files
committed
[Filesystem] Add a cross-platform readlink method
1 parent ed2cdfa commit 7f5981e

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

src/Symfony/Component/Filesystem/Filesystem.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,46 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false)
316316
}
317317
}
318318

319+
/**
320+
* Return the target of a link.
321+
*
322+
* @param string $path A link path.
323+
* @param bool $finalTarget Whether or not to recursively follow links
324+
* until a final target is reached.
325+
*
326+
* @return string Return the resolved link.
327+
*
328+
* @throws IOException When the link does not exist or is not readable.
329+
*/
330+
public function readlink($path, $finalTarget = false)
331+
{
332+
if (!$this->exists($path)) {
333+
throw new IOException(sprintf('The link %s does not exist and cannot be read.', $path));
334+
}
335+
336+
if (!is_link($path)) {
337+
return $path;
338+
}
339+
340+
// On Windows, transitive links are resolved to the final target by
341+
// readlink(). realpath(), however, returns the target link on Windows,
342+
// but not on Unix.
343+
344+
// /link1 -> /link2 -> /file
345+
346+
// Windows: readlink(/link1) => /file
347+
// realpath(/link1) => /link2
348+
349+
// Unix: readlink(/link1) => /link2
350+
// realpath(/link1) => /file
351+
352+
if ('\\' === DIRECTORY_SEPARATOR) {
353+
return $finalTarget ? readlink($path) : realpath($path);
354+
}
355+
356+
return $finalTarget ? realpath($path) : readlink($path);
357+
}
358+
319359
/**
320360
* Given an existing path, convert it to a path relative to a given starting path.
321361
*

src/Symfony/Component/Filesystem/Tests/FilesystemTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,62 @@ public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist()
767767
$this->assertEquals($file, readlink($link2));
768768
}
769769

770+
public function testReadLinkNotFinal()
771+
{
772+
$this->markAsSkippedIfSymlinkIsMissing();
773+
774+
$file = $this->workspace.DIRECTORY_SEPARATOR.'file';
775+
$link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link';
776+
$link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link';
777+
778+
touch($file);
779+
780+
$this->filesystem->symlink($file, $link1);
781+
$this->filesystem->symlink($link1, $link2);
782+
783+
$this->assertTrue(is_link($link1));
784+
$this->assertEquals($file, $this->filesystem->readlink($link1, false));
785+
$this->assertTrue(is_link($link2));
786+
$this->assertEquals($link1, $this->filesystem->readlink($link2, false));
787+
}
788+
789+
public function testReadLinkFinal()
790+
{
791+
$this->markAsSkippedIfSymlinkIsMissing();
792+
793+
$file = $this->workspace.DIRECTORY_SEPARATOR.'file';
794+
$link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link';
795+
$link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link';
796+
797+
touch($file);
798+
799+
$this->filesystem->symlink($file, $link1);
800+
$this->filesystem->symlink($link1, $link2);
801+
802+
$this->assertTrue(is_link($link1));
803+
$this->assertEquals($file, $this->filesystem->readlink($link1, true));
804+
$this->assertTrue(is_link($link2));
805+
$this->assertEquals($file, $this->filesystem->readlink($link2, true));
806+
}
807+
808+
public function testReadLinkNotLink()
809+
{
810+
$file = $this->workspace.DIRECTORY_SEPARATOR.'file';
811+
812+
touch($file);
813+
814+
$this->assertEquals($file, $this->filesystem->readlink($file, false));
815+
$this->assertEquals($file, $this->filesystem->readlink($file, true));
816+
}
817+
818+
/**
819+
* @expectedException \Symfony\Component\Filesystem\Exception\IOException
820+
*/
821+
public function testReadLinkFails()
822+
{
823+
$this->filesystem->readlink($this->workspace.DIRECTORY_SEPARATOR.'invalid');
824+
}
825+
770826
/**
771827
* @dataProvider providePathsForMakePathRelative
772828
*/

0 commit comments

Comments
 (0)
0