Page MenuHomePhabricator

D13424.diff
No OneTemporary

D13424.diff

diff --git a/src/filesystem/Filesystem.php b/src/filesystem/Filesystem.php
--- a/src/filesystem/Filesystem.php
+++ b/src/filesystem/Filesystem.php
@@ -853,6 +853,53 @@
}
/**
+ * Retrieve the relative location between two absolute paths.
+ *
+ * This function returns the relative path between two given absolute paths.
+ * The implementation was based on a post on
+ * [[http://stackoverflow.com/a/2638272/1369417 | StackOverflow]].
+ *
+ * @param string The source destination.
+ * @param string The target destination.
+ * @return string The relative path between the source and target
+ * destinations.
+ * @task path
+ */
+ public static function relativePath($from, $to) {
+ // Some compatibility fixes for Windows paths.
+ $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
+ $to = is_dir($to) ? rtrim($to, '\/').'/' : $to;
+ $from = str_replace('\\', '/', $from);
+ $to = str_replace('\\', '/', $to);
+
+ $from = explode('/', $from);
+ $to = explode('/', $to);
+
+ $rel_path = $to;
+
+ foreach ($from as $depth => $dir) {
+ // Find first non-matching directory.
+ if ($dir === $to[$depth]) {
+ // Ignore this directory.
+ array_shift($rel_path);
+ } else {
+ // Get number of remaining directories to $from.
+ $remaining = count($from) - $depth;
+ if ($remaining > 1) {
+ // Add traversals up to first matching directory.
+ $pad_length = (count($rel_path) + $remaining - 1) * -1;
+ $rel_path = array_pad($rel_path, $pad_length, '..');
+ break;
+ }
+ }
+ }
+
+ // `DIRECTORY_SEPARATOR` is not necessary. See
+ // http://us2.php.net/manual/en/ref.filesystem.php#73954.
+ return implode('/', $rel_path);
+ }
+
+ /**
* Test whether a path is descendant from some root path after resolving all
* symlinks and removing artifacts. Both paths must exists for the relation
* to obtain. A path is always a descendant of itself as long as it exists.
diff --git a/src/filesystem/__tests__/FilesystemTestCase.php b/src/filesystem/__tests__/FilesystemTestCase.php
--- a/src/filesystem/__tests__/FilesystemTestCase.php
+++ b/src/filesystem/__tests__/FilesystemTestCase.php
@@ -121,45 +121,47 @@
}
}
- public function testisDescendant() {
+ public function testRelativePath() {
$test_cases = array(
array(
- __FILE__,
- dirname(__FILE__),
- true,
+ '/',
+ '/foo/bar/baz',
+ 'foo/bar/baz',
),
array(
- dirname(__FILE__),
- dirname(dirname(__FILE__)),
- true,
+ '/foo',
+ '/foo/bar/baz',
+ 'bar/baz',
),
array(
- dirname(__FILE__),
- phutil_get_library_root_for_path(__FILE__),
- true,
+ '/foo/bar/baz',
+ '/foo/foobar',
+ '../foobar',
),
+
+ // Windows paths
array(
- dirname(dirname(__FILE__)),
- dirname(__FILE__),
- false,
+ 'c:\\',
+ 'c:\\Windows\\System32\\Drivers\\etc\\hosts',
+ 'Windows/System32/Drivers/etc/hosts',
),
array(
- dirname(__FILE__).'/quack',
- dirname(__FILE__),
- false,
+ 'c:\\Users\\Bill Gates\\',
+ 'c:\\Windows\\System32\\Drivers\\etc\\hosts',
+ '../../Windows/System32/Drivers/etc/hosts',
),
);
foreach ($test_cases as $test_case) {
- list($path, $root, $expected) = $test_case;
+ list($from, $to, $expected) = $test_case;
$this->assertEqual(
$expected,
- Filesystem::isDescendant($path, $root),
+ Filesystem::relativePath($from, $to),
sprintf(
- 'Filesystem::isDescendant(%s, %s)',
- phutil_var_export($path),
- phutil_var_export($root)));
+ 'Filesystem::relativePath(%s, %s)',
+ phutil_var_export($from),
+ phutil_var_export($to)));
}
}

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 29, 1:16 AM (11 h, 41 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6803107
Default Alt Text
D13424.diff (3 KB)

Event Timeline