Skip to content

Commit

Permalink
bug #36497 [Filesystem] Handle paths on different drives (crishoj)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the 3.4 branch.

Discussion
----------

[Filesystem] Handle paths on different drives

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| License       | MIT

`makePathRelative` strips and ignores the drive letters given Windows paths on different drives, resulting in a relative path which does not resolve to the desired target.

This PR makes `makePathRelative` notice paths on different drives, and return the full (absolute) target path in case instead.

Commits
-------

00e727a [Filesystem] Handle paths on different drives
  • Loading branch information
nicolas-grekas committed May 4, 2020
2 parents cf0d086 + 00e727a commit bd952b9
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 18 deletions.
35 changes: 17 additions & 18 deletions src/Symfony/Component/Filesystem/Filesystem.php
Expand Up @@ -455,37 +455,36 @@ public function makePathRelative($endPath, $startPath)
$startPath = str_replace('\\', '/', $startPath);
}

$stripDriveLetter = function ($path) {
if (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) {
return substr($path, 2);
}

return $path;
$splitDriveLetter = function ($path) {
return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0]))
? [substr($path, 2), strtoupper($path[0])]
: [$path, null];
};

$endPath = $stripDriveLetter($endPath);
$startPath = $stripDriveLetter($startPath);

// Split the paths into arrays
$startPathArr = explode('/', trim($startPath, '/'));
$endPathArr = explode('/', trim($endPath, '/'));

$normalizePathArray = function ($pathSegments, $absolute) {
$splitPath = function ($path, $absolute) {
$result = [];

foreach ($pathSegments as $segment) {
foreach (explode('/', trim($path, '/')) as $segment) {
if ('..' === $segment && ($absolute || \count($result))) {
array_pop($result);
} elseif ('.' !== $segment) {
} elseif ('.' !== $segment && '' !== $segment) {
$result[] = $segment;
}
}

return $result;
};

$startPathArr = $normalizePathArray($startPathArr, static::isAbsolutePath($startPath));
$endPathArr = $normalizePathArray($endPathArr, static::isAbsolutePath($endPath));
list($endPath, $endDriveLetter) = $splitDriveLetter($endPath);
list($startPath, $startDriveLetter) = $splitDriveLetter($startPath);

$startPathArr = $splitPath($startPath, static::isAbsolutePath($startPath));
$endPathArr = $splitPath($endPath, static::isAbsolutePath($endPath));

if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) {
// End path is on another drive, so no relative path exists
return $endDriveLetter.':/'.($endPathArr ? implode('/', $endPathArr).'/' : '');
}

// Find for which directory the common path stops
$index = 0;
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
Expand Up @@ -1111,10 +1111,14 @@ public function providePathsForMakePathRelative()
['/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'],
['/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'],
['C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'],
['C:/aa/bb/cc', 'c:/aa/dd/..', 'bb/cc/'],
['c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'],
['C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'],
['C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'],
['C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'],
['D:/', 'C:/aa/../bb/cc', 'D:/'],
['D:/aa/bb', 'C:/aa', 'D:/aa/bb/'],
['D:/../../aa/../bb/cc', 'C:/aa/dd/..', 'D:/bb/cc/'],
];

if ('\\' === \DIRECTORY_SEPARATOR) {
Expand Down

0 comments on commit bd952b9

Please sign in to comment.