将相对URL转换为绝对URL

4
假设我有一个指向另一篇文档的URL(可以是绝对路径或相对路径),我需要将此链接转换为绝对路径。
我编写了一个简单的函数,以提供几种常见情况下的功能:
function absolute_url($url,$parent_url){
  $parent_url=parse_url($parent_url);
  if(strcmp(substr($url,0,7),'http://')==0){
    return $url;
  }
  elseif(strcmp(substr($url,0,1),'/')==0){
    return $parent_url['scheme']."://".$parent_url['host'].$url;
  }
  else{
    $path=$parent_url['path'];
    $path=substr($path,0,strrpos($path,'/'));
    return $parent_url['scheme']."://".$parent_url['host']."$path/".$url;
  }
}

$parent_url='http://example.com/path/to/file/name.php?abc=abc';
echo absolute_url('name2.php',$parent_url)."\n";
// output http://example.com/path/to/file/name2.php
echo absolute_url('/name2.php',$parent_url)."\n";
// output http://example.com/name2.php
echo absolute_url('http://name2.php',$parent_url)."\n";
// output http://name2.php

代码运行良好,但可能存在一些无法正常工作的情况,例如../../path/to/file.php。那么是否有更通用的标准类或函数可以完成这项工作,并比我的函数更好呢?
我曾经尝试在Google上搜索并查看类似的问题(一个两个),但这似乎是与服务器路径相关的解决方案,而不是我正在寻找的东西。

..\..\path\to\file.php 是一个文件路径,而不是一个 URL,因此需要一个单独的函数来处理。 - Steve
@Steve,所以在HTML文档中<a href='..\..\path\to\file.php'>file</a>不能工作吗? - Vlada Katlinskaya
这取决于浏览器,但不能保证。URI(链接中输入的内容)应该使用正斜杠。我并不是故意刁难,我真的以为你在谈论文件路径,例如用于PHP的include等。 - Steve
@Steve 哦!你说得对!我是在谈论URI。所以我的意思是 ../../path/to/file.php - Vlada Katlinskaya
这是将相对路径转换为绝对URL的PHP代码示例的英文内容。 - qdinar
3个回答

2
该函数将不使用正则表达式,将相对URL解析为给定当前页面URL中的$pgurl。它成功地解析了以下类型的URL:

/home.php?example 类型,

同级目录下的 nextpage.php 类型,

../...../.../parentdir 类型,

完整的 http://example.net URL,

和简写的 //example.net URL。

//Current base URL (you can dynamically retrieve from $_SERVER)
$pgurl = 'http://example.com/scripts/php/absurl.php';

function absurl($url) {
 global $pgurl;
 if(strpos($url,'://')) return $url; //already absolute
 if(substr($url,0,2)=='//') return 'http:'.$url; //shorthand scheme
 if($url[0]=='/') return parse_url($pgurl,PHP_URL_SCHEME).'://'.parse_url($pgurl,PHP_URL_HOST).$url; //just add domain
 if(strpos($pgurl,'/',9)===false) $pgurl .= '/'; //add slash to domain if needed
 return substr($pgurl,0,strrpos($pgurl,'/')+1).$url; //for relative links, gets current directory and appends new filename
}

function nodots($path) { //Resolve dot dot slashes, no regex!
 $arr1 = explode('/',$path);
 $arr2 = array();
 foreach($arr1 as $seg) {
  switch($seg) {
   case '.':
    break;
   case '..':
    array_pop($arr2);
    break;
   case '...':
    array_pop($arr2); array_pop($arr2);
    break;
   case '....':
    array_pop($arr2); array_pop($arr2); array_pop($arr2);
    break;
   case '.....':
    array_pop($arr2); array_pop($arr2); array_pop($arr2); array_pop($arr2);
    break;
   default:
    $arr2[] = $seg;
  }
 }
 return implode('/',$arr2);
}

使用示例:

echo nodots(absurl('../index.html'));

nodots()必须在URL转换为绝对路径后调用。

点函数有点多余,但易读、快速,不使用正则表达式,并且可以解决99%的典型URL问题(如果您想要100%确定,请扩展switch块以支持6个或更多的点,尽管我从未见过这么多点的URL)。

希望这可以帮助您,


没有测试过,但看起来正是我所需要的。 - Vlada Katlinskaya

0
$uri = "..";
$path = realpath($uri);
$root = realpath($_SERVER["DOCUMENT_ROOT"]);

if($path){
    $path = str_replace($root, "", $path);
    $path = $_SERVER["SERVER_NAME"] . $path;
    $protocol = "http";
    if(isset($_SERVER["HTTPS"])){
        $protocol .= "s";        
    }
    $path = "{$protocol}://$path";
    $path = str_replace("\\", "/", $path);
}

var_dump($path);

可能有更好/更快的方法,但我只是草草写了这个...


我需要这个函数能够处理远程文件,因为我理解这个函数只能处理本地文件,对吗? - Vlada Katlinskaya
是的...我知道了,抱歉 :) - chris_eyekiller

0

网页浏览器使用页面URL或基础标签来解析相对URL。

此脚本可以相对于基础URL解析URL或URI。URL或URI以浏览器常用的方式进行解析。

/** Build a URL
 *
 * @param array $parts An array that follows the parse_url scheme
 * @return string
 */
function build_url($parts)
{
    if (empty($parts['user'])) {
        $url = $parts['scheme'] . '://' . $parts['host'];
    } elseif(empty($parts['pass'])) {
        $url = $parts['scheme'] . '://' . $parts['user'] . '@' . $parts['host'];
    } else {
        $url = $parts['scheme'] . '://' . $parts['user'] . ':' . $parts['pass'] . '@' . $parts['host'];
    }

    if (!empty($parts['port'])) {
        $url .= ':' . $parts['port'];
    }

    if (!empty($parts['path'])) {
        $url .= $parts['path'];
    }

    if (!empty($parts['query'])) {
        $url .= '?' . $parts['query'];
    }

    if (!empty($parts['fragment'])) {
        return $url . '#' . $parts['fragment'];
    }

    return $url;
}

/** Convert a relative path into an absolute path
 *
 * @param string $path
 * @return string
 */
function abs_path($path)
{
    $path_array = explode('/', $path);

    // Solve current and parent folder navigation
    $translated_path_array = array();
    $i = 0;
    foreach ($path_array as $name) {
        if ($name === '..') {
            unset($translated_path_array[--$i]);
        } elseif (!empty($name) && $name !== '.') {
            $translated_path_array[$i++] = $name;
        }
    }

    return '/' . implode('/', $translated_path_array);
}

/** Convert a relative URL in to an absolute URL
 *
 * @param string $url URL or URI
 * @param string $base Absolute URL
 * @return string
 */
function abs_url($url, $base)
{
    $url_parts = parse_url($url);
    $base_parts = parse_url($base);

    // Handle the path if it is specified
    if (!empty($url_parts['path'])) {
        // Is the path relative
        if (substr($url_parts['path'], 0, 1) !== '/') {
            // Does the base path end with slash?
            if (substr($base_parts['path'], -1) === '/') {
                $url_parts['path'] = $base_parts['path'] . $url_parts['path'];
            } else {
                $url_parts['path'] = dirname($base_parts['path']) . '/' . $url_parts['path'];
            }
        }

        // Make path absolute
        $url_parts['path'] = abs_path($url_parts['path']);
    }

    // Use the base URL to populate the unfilled components until a component is filled
    foreach (['scheme', 'host', 'path', 'query', 'fragment'] as $comp) {
        if (!empty($url_parts[$comp])) {
            break;
        }
        $url_parts[$comp] = $base_parts[$comp];
    }

    return build_url($url_parts);
}

测试

// Base URL
$base_url = 'https://example.com/path1/path2/path3/path4/file.ext?field1=value1&field2=value2#fragment';

// URL and URIs (_ is used to see what is coming from relative URL)
$test_urls = array(
    "http://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment", // URL
    "//_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",      // URI without scheme
    "//_example.com",                                                                        // URI with host only
    "/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",                    // URI without scheme and host
    "_path1/_path2/_file.ext",                                                               // URI with path only
    "./../../_path1/../_path2/file.ext#_fragment",                                           // URI with path and fragment
    "?_field1=_value1&_field2=_value2#_fragment",                                            // URI with query and fragment
    "#_fragment"                                                                             // URI with fragment only
);

// Expected result
$expected_urls = array(
    "http://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://_example.com",
    "https://example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://example.com/path1/path2/path3/path4/_path1/_path2/_file.ext",
    "https://example.com/path1/path2/_path2/file.ext#_fragment",
    "https://example.com/path1/path2/path3/path4/file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://example.com/path1/path2/path3/path4/file.ext?field1=value1&field2=value2#_fragment"
);

foreach ($test_urls as $i => $url) {
    $abs_url = abs_url($url, $base_url);
    if ( $abs_url == $expected_urls[$i] ) {
        echo  "[OK] " . $abs_url . PHP_EOL;
    } else {
        echo  "[WRONG] " . $abs_url . PHP_EOL;
    }
}

结果

[OK] http://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://_example.com
[OK] https://example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://example.com/path1/path2/path3/path4/_path1/_path2/_file.ext
[OK] https://example.com/path1/path2/_path2/file.ext#_fragment
[OK] https://example.com/path1/path2/path3/path4/file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://example.com/path1/path2/path3/path4/file.ext?field1=value1&field2=value2#_fragment

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接