8000 [Filesystem] Add a cross-platform readlink/realpath methods for neste… · symfony/symfony@7ed04a4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7ed04a4

Browse files
committed
[Filesystem] Add a cross-platform readlink/realpath methods for nested links
1 parent ad85c79 commit 7ed04a4

File tree

3 files changed

+137
-0
lines changed

3 files changed

+137
-0
lines changed

src/Symfony/Component/Filesystem/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
3.2.0
5+
-----
6+
7+
* added `readlink()` as a platform independent method to read links
8+
49
3.0.0
510
-----
611

src/Symfony/Component/Filesystem/Filesystem.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,47 @@ private function linkException($origin, $target, $linkType)
383383
throw new IOException(sprintf('Failed to create %s link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target);
384384
}
385385

386+
/**
387+
* Resolves links in paths.
388+
*
389+
* With $canonicalize = false (default)
390+
* - if $path does not exist or is not a link, returns null
391+
* - if $path is a link, returns the next direct target of the link without considering the existence of the target
392+
*
393+
* With $canonicalize = true
394+
* - if $path does not exist, returns null
395+
* - if $path exists, returns its absolute fully resolved final version
396+
*
397+
* @param string $path A filesystem path
398+
* @param bool $canonicalize Whether or not to return a canonicalized path
399+
*
400+
* @return string|null
401+
*/
402+
public function readlink($path, $canonicalize = false)
403+
{
404+
if (!$canonicalize && !is_link($path)) {
405+
return;
406+
}
407+
408+
if ($canonicalize) {
409+
if (!$this->exists($path)) {
410+
return;
411+
}
412+
413+
if ('\\' === DIRECTORY_SEPARATOR) {
414+
$path = readlink($path);
415+
}
416+
417+
return realpath($path);
418+
}
419+
420+
if ('\\' === DIRECTORY_SEPARATOR) {
421+
return realpath($path);
422+
}
423+
424+
return readlink($path);
425+
}
426+
386427
/**
387428
* Given an existing path, convert it to a path relative to a given starting path.
388429
*

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

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,85 @@ public function testLinkWithSameTarget()
956956
$this->assertTrue(is_file($link));
957957
$this->assertEquals(fileinode($file), fileinode($link));
958958
}
959+
public function testReadRelativeLink()
960+
{
961+
$this->markAsSkippedIfSymlinkIsMissing();
962+
963+
if ('\\' === DIRECTORY_SEPARATOR) {
964+
$this->markTestSkipped('Relative symbolic links are not supported on Windows');
965+
}
966+
967+
$file = $this->normalize($this->workspace.'/file');
968+
$link1 = $this->normalize($this->workspace.'/dir/link');
969+
$link2 = $this->normalize($this->workspace.'/dir/link2');
970+
touch($file);
971+
972+
$this->filesystem->symlink($this->normalize('../file'), $link1);
973+
$this->filesystem->symlink('link', $link2);
974+
975+
$this->assertEquals($this->normalize('../file'), $this->filesystem->readlink($link1));
976+
$this->assertEquals('link', $this->filesystem->readlink($link2));
977+
$this->assertEquals($file, $this->filesystem->readlink($link1, true));
978+
$this->assertEquals($file, $this->filesystem->readlink($link2, true));
979+
$this->assertEquals($file, $this->filesystem->readlink($file, true));
980+
}
981+
982+
public function testReadBrokenLink()
983+
{
984+
$this->markAsSkippedIfSymlinkIsMissing();
985+
986+
if ('\\' === DIRECTORY_SEPARATOR) {
987+
$this->markTestSkipped('Windows does not support creating "broken" symlinks');
988+
}
989+
990+
$file = $this->workspace.DIRECTORY_SEPARATOR.'file';
991+
$link = $this->workspace.DIRECTORY_SEPARATOR.'link';
992+
993+
$this->filesystem->symlink($file, $link);
994+
995+
$this->assertEquals($file, $this->filesystem->readlink($link));
996+
$this->assertNull($this->filesystem->readlink($link, true));
997+
998+
touch($file);
999+
$this->assertEquals($file, $this->filesystem->readlink($link, true));
1000+
}
1001+
1002+
public function testReadAbsoluteLink()
1003+
{
1004+
$this->markAsSkippedIfSymlinkIsMissing();
1005+
1006+
$file = $this->normalize($this->workspace.'/file');
1007+
$link1 = $this->normalize($this->workspace.'/dir/link');
1008+
$link2 = $this->normalize($this->workspace.'/dir/link2');
1009+
touch($file);
1010+
1011+
$this->filesystem->symlink($file, $link1);
1012+
$this->filesystem->symlink($link1, $link2);
1013+
1014+
$this->assertEquals($file, $this->filesystem->readlink($link1));
1015+
$this->assertEquals($link1, $this->filesystem->readlink($link2));
1016+
$this->assertEquals($file, $this->filesystem->readlink($link1, true));
1017+
$this->assertEquals($file, $this->filesystem->readlink($link2, true));
1018+
$this->assertEquals($file, $this->filesystem->readlink($file, true));
1019+
}
1020+
1021+
public function testReadLinkDefaultPathDoesNotExist()
1022+
{
1023+
$this->assertNull($this->filesystem->readlink($this->normalize($this->workspace.'/invalid')));
1024+
}
1025+
1026+
public function testReadLinkDefaultPathNotLink()
1027+
{
1028+
$file = $this->normalize($this->workspace.'/file');
1029+
touch($file);
1030+
1031+
$this->assertNull($this->filesystem->readlink($file));
1032+
}
1033+
1034+
public function testReadLinkCanonicalizedPathDoesNotExist()
1035+
{
1036+
$this->assertNull($this->filesystem->readlink($this->normalize($this->workspace.'invalid'), true));
1037+
}
9591038

9601039
/**
9611040
* @dataProvider providePathsForMakePathRelative
@@ -1321,4 +1400,16 @@ public function testCopyShouldKeepExecutionPermission()
13211400

13221401
$this->assertFilePermissions(767, $targetFilePath);
13231402
}
1403+
1404+
/**
1405+
* Normalize the given path (transform each blackslash into a real directory separator).
1406+
*
1407+
* @param string $path
1408+
*
1409+
* @return string
1410+
*/
1411+
private function normalize($path)
1412+
{
1413+
return str_replace('/', DIRECTORY_SEPARATOR, $path);
1414+
}
13241415
}

0 commit comments

Comments
 (0)
0