private function _test_make_canonical() { $this->_check_equal('/a/b/c/', make_canonical('/a/b/c/')); $this->_check_equal('/a/b/c/', make_canonical('/a/./b/c/')); $this->_check_equal('/a/b/c/', make_canonical('/a/././b/c/')); $this->_check_equal('a/b/c/', make_canonical('./a/b/c/')); $this->_check_equal('a/b/c/', make_canonical('./a/./b/c/')); $this->_check_equal('a/b/c/', make_canonical('./a/././b/c/')); $this->_check_equal('a/b/c/', make_canonical('././a/b/c/')); $this->_check_equal('a/b/c/', make_canonical('././a/./b/c/')); $this->_check_equal('a/b/c/', make_canonical('././a/././b/c/')); $this->_check_equal('/a/b/c/', make_canonical('/a/b/c/.')); $this->_check_equal('/a/b/c/', make_canonical('/a/./b/c/.')); $this->_check_equal('/a/b/c/', make_canonical('/a/././b/c/.')); $this->_check_equal('a/b/c/', make_canonical('a/b/c/.')); $this->_check_equal('a/b/c/', make_canonical('a/./b/c/.')); $this->_check_equal('a/b/c/', make_canonical('a/././b/c/.')); $this->_check_equal('a/b/c/', make_canonical('a/b/c/./.')); $this->_check_equal('a/b/c/', make_canonical('a/./b/c/./.')); $this->_check_equal('a/b/c/', make_canonical('a/././b/c/./.')); }
/** * Calculate a path between 'from' and 'to'. * Both 'to' and 'from' should be absolute paths and are assumed to be on the * same server. If the paths are the same, an empty string is returned (no path * modification needed). If there is no common path, 'to' is returned. If 'from' * is a sub-path of 'to', a chain of '..' directories is returned. If 'to' is a * sub-path of 'from', 'from' is removed from the path in order to make it * relative. * * <b>Examples:</b> * <code>path_between ('/a/b/c/', '/a/b/c/') is '' * path_between ('/a/b/c/', '/a/b/') is '../' * path_between ('/a/b/c/', '/a/') is '../../' * path_between ('/a/b/', '/a/b/c/') is 'c/' * path_between ('/a/', '/a/b/c/') is 'b/c/' * path_between ('/a/d/c', '/a/b/c/') is '/a/b/c/' * </code> * * @param string $from * @param string $to * @param FILE_OPTIONS $opts * @return string */ function path_between($from, $to, $opts = null) { if (!isset($opts)) { $opts = global_file_options(); } $from = make_canonical($from, $opts); $to = make_canonical($to, $opts); if (empty($to) || empty($from)) { return $to; } $Result = $to; if (strpos($from, $to) === 0) { // target is subset of source if (strlen($from) != strlen($to)) { $Result = ''; $sub_url = substr($from, strlen($to)); $sub_folders = explode($opts->path_delimiter, $sub_url); foreach ($sub_folders as $f) { if ($f) { $Result .= '..' . $opts->path_delimiter; } } if ($Result == '') { $Result = $opts->current_folder; } elseif (ends_with_delimiter($Result, $opts) && !ends_with_delimiter($to)) { $Result = substr($Result, 0, -1); } } else { $Result = $opts->current_folder . $opts->path_delimiter; } } else { if (strpos($to, $from) === 0) { // source is subset of target $Result = substr($to, strlen($from)); if (strlen($Result) > 1 && $Result[0] == $opts->path_delimiter) { $Result = substr($Result, 1); } elseif ($Result == $opts->path_delimiter) { $Result = $opts->current_folder . $opts->path_delimiter; } } elseif ($from[0] == $to[0] && $from[0] == $opts->path_delimiter) { // check for partial containment of rooted paths $to_folders = explode($opts->path_delimiter, trim($to, $opts->path_delimiter)); $from_folders = explode($opts->path_delimiter, trim($from, $opts->path_delimiter)); $common_root_folder_count = 0; $count = min(array(sizeof($to_folders), sizeof($from_folders))); for ($index = 0; $index < $count; $index += 1) { if ($from_folders[$index] != '') { if ($to_folders[$index] == $from_folders[$index]) { $common_root_folder_count += 1; } else { break; } } } if ($common_root_folder_count > 0) { $leader = str_repeat('..' . $opts->path_delimiter, sizeof($from_folders) - $common_root_folder_count); $Result = $leader . join($opts->path_delimiter, array_slice($to_folders, $common_root_folder_count)); if (ends_with_delimiter($to, $opts)) { $Result .= $opts->path_delimiter; } } } } return $Result; }