From 123deb28f650772f277fd0869e63213a95745e1e Mon Sep 17 00:00:00 2001 From: Tomas Gerulaitis Date: Fri, 15 May 2015 14:40:14 +0100 Subject: [PATCH] Make symlinks relative to their location --- .../Traits/BashModules/Filesystem.php | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Rocketeer/Traits/BashModules/Filesystem.php b/src/Rocketeer/Traits/BashModules/Filesystem.php index bd76e6f12..69c2a1272 100644 --- a/src/Rocketeer/Traits/BashModules/Filesystem.php +++ b/src/Rocketeer/Traits/BashModules/Filesystem.php @@ -55,7 +55,7 @@ public function symlink($folder, $symlink) // Switch to relative if required if ($this->rocketeer->getOption('remote.symlink') === 'relative') { - $folder = str_ireplace($this->paths->getFolder(''), '', $folder); + $folder = $this->getRelativePath($symlink, $folder); } switch ($this->environment->getOperatingSystem()) { @@ -292,4 +292,88 @@ protected function fromTo($command, $from, $to) return $this->run(sprintf('%s %s %s', $command, $from, $to)); } + + /** + * Get a relative path from one file or directory to another. + * + * If $from is a path to a file (i.e. does not end with a "/"), the + * returned path will be relative to its parent directory. + * + * @param string $from + * @param string $to + * + * @return string + */ + protected function getRelativePath($from, $to) + { + $from = $this->explodePath($from); + $to = $this->explodePath($to); + + $result = []; + $i = 0; + + // Skip the common path prefix + while ($i < count($from) && $i < count($to) && $from[$i] === $to[$i]) { + $i++; + } + + // Add ".." for each directory left in $from + $from_length = count($from) - 1; // Path length without the filename + if ($i > 0 && $i < $from_length) { + $result = array_fill(0, $from_length - $i, '..'); + } + + // Add the remaining $to path + $result = array_merge($result, array_slice($to, $i)); + + return implode('/', $result); + } + + /** + * Explode the given path into components, resolving any + * ".." components and ignoring "." and double separators. + * + * If the path starts at root directory, the first component + * will be empty. + * + * @param string $path + * @param string $separator Path separator to use, defaults to /. + * + * @return array + */ + protected function explodePath($path, $separator = '/') + { + $result = []; + + if (strpos($path, $separator) === 0) { + // Add empty component if the path starts at root directory + $result[] = ''; + } + + foreach (explode($separator, $path) as $component) { + switch ($component) { + case '..': + // ".." removes the preceding component + if (empty($result) || $result[count($result) - 1] === '..') { + // Unless the path contains only ".." so far (then keep the "..") + $result[] = '..'; + break; + } + if (count($result) === 1 && $result[0] === '') { + // Or the path is already at root (then just ignore it) + break; + } + array_pop($result); + break; + case '.': + case '': + // Ignore double separators and "." + break; + default: + $result[] = $component; + } + } + + return $result; + } }