【问题标题】:Transform relative path into absolute URL using PHP使用 PHP 将相对路径转换为绝对 URL
【发布时间】:2011-05-25 13:47:10
【问题描述】:

如何使用 php 将相对路径转换为绝对 URL?

【问题讨论】:

标签: php parsing relative-path


【解决方案1】:
function rel2abs($rel, $base)
{
    /* return if already absolute URL */
    if (parse_url($rel, PHP_URL_SCHEME) != '') return $rel;

    /* queries and anchors */
    if ($rel[0]=='#' || $rel[0]=='?') return $base.$rel;

    /* parse base URL and convert to local variables:
       $scheme, $host, $path */
    extract(parse_url($base));

    /* remove non-directory element from path */
    $path = preg_replace('#/[^/]*$#', '', $path);

    /* destroy path if relative url points to root */
    if ($rel[0] == '/') $path = '';

    /* dirty absolute URL */
    $abs = "$host$path/$rel";

    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
    for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}

    /* absolute URL is ready! */
    return $scheme.'://'.$abs;
}

【讨论】:

  • 这在大多数情况下已经足够好了,但它fails a lot of edge cases
  • 如果相对 url 以 ?它应该在拆分相对和绝对网址之前从基础中删除查询部分
  • $rel 不应该是一个带有相对 URL 的字符串吗?你为什么要尝试$rel[0]?它只是返回一个错误Uninitialized string offset: 0
  • 此链接现已失效;导致一些非法网站(可能是恶意软件)
  • rel 以“//”开头时失败
【解决方案2】:

我喜欢 jordanstephens 从链接中提供的代码!我投了赞成票。 l0oky 启发我确保该函数与端口、用户名和密码 URL 兼容。我的项目需要它。

function rel2abs( $rel, $base )
{
    /* return if already absolute URL */
    if( parse_url($rel, PHP_URL_SCHEME) != '' )
        return( $rel );

    /* queries and anchors */
    if( $rel[0]=='#' || $rel[0]=='?' )
        return( $base.$rel );

    /* parse base URL and convert to local variables:
       $scheme, $host, $path */
    extract( parse_url($base) );

    /* remove non-directory element from path */
    $path = preg_replace( '#/[^/]*$#', '', $path );

    /* destroy path if relative url points to root */
    if( $rel[0] == '/' )
        $path = '';

    /* dirty absolute URL */
    $abs = '';

    /* do we have a user in our URL? */
    if( isset($user) )
    {
        $abs.= $user;

        /* password too? */
        if( isset($pass) )
            $abs.= ':'.$pass;

        $abs.= '@';
    }

    $abs.= $host;

    /* did somebody sneak in a port? */
    if( isset($port) )
        $abs.= ':'.$port;

    $abs.=$path.'/'.$rel;

    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
    for( $n=1; $n>0; $abs=preg_replace( $re, '/', $abs, -1, $n ) ) {}

    /* absolute URL is ready! */
    return( $scheme.'://'.$abs );
}

【讨论】:

  • 但它会返回绝对文件系统路径,我们想要绝对 url
  • 这应该返回一个 URL。它是否为您返回文件路径?
【解决方案3】:

增加了对保留当前查询的支持。对 ?page=1 等有很大帮助...

function rel2abs($rel, $base)
{
    /* return if already absolute URL */
    if (parse_url($rel, PHP_URL_SCHEME) != '')
        return ($rel);

    /* queries and anchors */
    if ($rel[0] == '#' || $rel[0] == '?')
        return ($base . $rel);

    /* parse base URL and convert to local variables: $scheme, $host, $path, $query, $port, $user, $pass */
    extract(parse_url($base));

    /* remove non-directory element from path */
    $path = preg_replace('#/[^/]*$#', '', $path);

    /* destroy path if relative url points to root */
    if ($rel[0] == '/')
        $path = '';

    /* dirty absolute URL */
    $abs = '';

    /* do we have a user in our URL? */
    if (isset($user)) {
        $abs .= $user;

        /* password too? */
        if (isset($pass))
            $abs .= ':' . $pass;

        $abs .= '@';
    }

    $abs .= $host;

    /* did somebody sneak in a port? */
    if (isset($port))
        $abs .= ':' . $port;

    $abs .= $path . '/' . $rel . (isset($query) ? '?' . $query : '');

    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'];
    for ($n = 1; $n > 0; $abs = preg_replace($re, '/', $abs, -1, $n)) {
    }

    /* absolute URL is ready! */

    return ($scheme . '://' . $abs);
}

【讨论】:

  • 对于以“//”开头的 rel 也失败
【解决方案4】:

网络浏览器使用页面 URL 或 base tag 来解析相对 URL。

此脚本可以解析相对于基本 URL 的 URL。

/** Build a URL
 *
 * @param array $parts An array that follows the parse_url scheme
 * @return string
 */
function build_url(array $parts): string
{
    $url = $parts['scheme'] . '://';

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

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

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

    $url .= $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'])) {
        $url .= '#' . $parts['fragment'];
    }

    return $url;
}

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

    // Solve current and parent folder navigation
    $abs_path_array = array();
    foreach ($path_array as $name) {
        if (empty($name)) {
            continue;
        }

        if ($name == '..') {
            array_pop($abs_path_array);
        } elseif ($name != '.') {
            $abs_path_array[] = $name;
        }
    }

    return '/' . implode('/', $abs_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(string $url, string $base): string
{
    $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 (!str_starts_with($url_parts['path'], '/')) {
            if (str_ends_with($base_parts['path'], '/')) {
                $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

【讨论】:

    【解决方案5】:

    实际上不是关于转换路径而不是 url 的问题吗? PHP 实际上有一个函数:realpath()。您唯一应该注意的是符号链接。

    PHP 手册中的示例:

    chdir('/var/www/');
    echo realpath('./../../etc/passwd') . PHP_EOL;
    // Prints: /etc/passwd
    
    echo realpath('/tmp/') . PHP_EOL;
    // Prints: /tmp
    

    【讨论】:

    • 您能否提供一个使用示例?
    • 此方法的结果是路径而不是 URL。该问题要求提供 URL。
    【解决方案6】:

    我更新了函数以修复以“//”开头的相对 URL,从而提高执行速度。

    function getAbsoluteUrl($relativeUrl, $baseUrl){
    
        // if already absolute URL 
        if (parse_url($relativeUrl, PHP_URL_SCHEME) !== null){
            return $relativeUrl;
        }
    
        // queries and anchors
        if ($relativeUrl[0] === '#' || $relativeUrl[0] === '?'){
            return $baseUrl.$relativeUrl;
        }
    
        // parse base URL and convert to: $scheme, $host, $path, $query, $port, $user, $pass
        extract(parse_url($baseUrl));
    
        // if base URL contains a path remove non-directory elements from $path
        if (isset($path) === true){
            $path = preg_replace('#/[^/]*$#', '', $path);
        }
        else {
            $path = '';
        }
    
        // if realtive URL starts with //
        if (substr($relativeUrl, 0, 2) === '//'){
            return $scheme.':'.$relativeUrl;
        }
    
        // if realtive URL starts with /
        if ($relativeUrl[0] === '/'){
            $path = null;
        }
    
        $abs = null;
    
        // if realtive URL contains a user
        if (isset($user) === true){
            $abs .= $user;
    
            // if realtive URL contains a password
            if (isset($pass) === true){
                $abs .= ':'.$pass;
            }
    
            $abs .= '@';
        }
    
        $abs .= $host;
    
        // if realtive URL contains a port
        if (isset($port) === true){
            $abs .= ':'.$port;
        }
    
        $abs .= $path.'/'.$relativeUrl.(isset($query) === true ? '?'.$query : null);
    
        // replace // or /./ or /foo/../ with /
        $re = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'];
        for ($n = 1; $n > 0; $abs = preg_replace($re, '/', $abs, -1, $n)) {
        }
    
        // return absolute URL
        return $scheme.'://'.$abs;
    
    }
    

    【讨论】:

      【解决方案7】:
      function url_to_absolute($baseURL, $relativeURL) {  
          $relativeURL_data = parse_url($relativeURL);
      
          if (isset($relativeURL_data['scheme'])) {
              return $relativeURL;
          }
      
          $baseURL_data = parse_url($baseURL);
      
          if (!isset($baseURL_data['scheme'])) {
              return $relativeURL;
          }
      
          $absoluteURL_data = $baseURL_data;
      
          if (isset($relativeURL_data['path']) && $relativeURL_data['path']) {
              if (substr($relativeURL_data['path'], 0, 1) == '/') {
                  $absoluteURL_data['path'] = $relativeURL_data['path'];
              } else {
                  $absoluteURL_data['path'] = (isset($absoluteURL_data['path']) ? preg_replace('#[^/]*$#', '', $absoluteURL_data['path']) : '/') . $relativeURL_data['path'];
              }
      
              if (isset($relativeURL_data['query'])) {
                  $absoluteURL_data['query'] = $relativeURL_data['query'];
              } else if (isset($absoluteURL_data['query'])) {
                  unset($absoluteURL_data['query']);
              }
          } else {
              $absoluteURL_data['path'] = isset($absoluteURL_data['path']) ? $absoluteURL_data['path'] : '/';
      
              if (isset($relativeURL_data['query'])) {
                  $absoluteURL_data['query'] = $relativeURL_data['query'];
              } else if (isset($absoluteURL_data['query'])) {
                  $absoluteURL_data['query'] = $absoluteURL_data['query'];
              }
          }
      
          if (isset($relativeURL_data['fragment'])) {
              $absoluteURL_data['fragment'] = $relativeURL_data['fragment'];
          } else if (isset($absoluteURL_data['fragment'])) {
              unset($absoluteURL_data['fragment']);
          }
      
          $absoluteURL_path = ltrim($absoluteURL_data['path'], '/');
          $absoluteURL_path_parts = array();
      
          for ($i = 0, $i2 = 0; $i < strlen($absoluteURL_path); $i++) {
              if (isset($absoluteURL_path_parts[$i2])) {
                  $absoluteURL_path_parts[$i2] .= $absoluteURL_path[$i];
              } else {
                  $absoluteURL_path_parts[$i2] = $absoluteURL_path[$i];
              }
      
              if ($absoluteURL_path[$i] == '/') {
                  $i2++;
              }
          }
      
          reset($absoluteURL_path_parts);
      
          while (true) {
              if (rtrim(current($absoluteURL_path_parts), '/') == '.') {
                  unset($absoluteURL_path_parts[key($absoluteURL_path_parts)]);
      
                  continue;
              } else if (rtrim(current($absoluteURL_path_parts), '/') == '..') {
                  if (prev($absoluteURL_path_parts) !== false) {
                      unset($absoluteURL_path_parts[key($absoluteURL_path_parts)]);
                  } else {
                      reset($absoluteURL_path_parts);
                  }
      
                  unset($absoluteURL_path_parts[key($absoluteURL_path_parts)]);
      
                  continue;
              }
      
              if (next($absoluteURL_path_parts) === false) {
                  break;
              }
          }
      
          $absoluteURL_data['path'] = '/' . implode('', $absoluteURL_path_parts);
      
          $absoluteURL = isset($absoluteURL_data['scheme']) ? $absoluteURL_data['scheme'] . ':' : '';
          $absoluteURL .= (isset($absoluteURL_data['user']) || isset($absoluteURL_data['host'])) ? '//' : '';
          $absoluteURL .= isset($absoluteURL_data['user']) ? $absoluteURL_data['user'] : '';
          $absoluteURL .= isset($absoluteURL_data['pass']) ? ':' . $absoluteURL_data['pass'] : '';
          $absoluteURL .= isset($absoluteURL_data['user']) ? '@' : '';
          $absoluteURL .= isset($absoluteURL_data['host']) ? $absoluteURL_data['host'] : '';
          $absoluteURL .= isset($absoluteURL_data['port']) ? ':' . $absoluteURL_data['port'] : '';
          $absoluteURL .= isset($absoluteURL_data['path']) ? $absoluteURL_data['path'] : '';
          $absoluteURL .= isset($absoluteURL_data['query']) ? '?' . $absoluteURL_data['query'] : '';
          $absoluteURL .= isset($absoluteURL_data['fragment']) ? '#' . $absoluteURL_data['fragment'] : '';
      
          return $absoluteURL;
      }
      

      【讨论】:

      • 还有其他答案提供了 OP 的问题,它们是多年前发布的。发布答案时,请确保添加新的解决方案或更好的解释,尤其是在回答较旧的问题时。
      【解决方案8】:

      我使用了相同的代码:http://nashruddin.com/PHP_Script_for_Converting_Relative_to_Absolute_URL 但我稍微修改了一下,所以如果基本 url 包含端口号,它会返回包含端口号的相对 URL。

      function rel2abs($rel, $base)
      {
          /* return if already absolute URL */
          if (parse_url($rel, PHP_URL_SCHEME) != '') return $rel;
      
          /* queries and anchors */
          if ($rel[0]=='#' || $rel[0]=='?') return $base.$rel;
      
          /* parse base URL and convert to local variables:
             $scheme, $host, $path */
          extract(parse_url($base));
      
          /* remove non-directory element from path */
          $path = preg_replace('#/[^/]*$#', '', $path);
      
          /* destroy path if relative url points to root */
          if ($rel[0] == '/') $path = '';
      
          /* dirty absolute URL // with port number if exists */
          if (parse_url($base, PHP_URL_PORT) != ''){
              $abs = "$host:".parse_url($base, PHP_URL_PORT)."$path/$rel";
          }else{
              $abs = "$host$path/$rel";
          }
          /* replace '//' or '/./' or '/foo/../' with '/' */
          $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
          for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
      
          /* absolute URL is ready! */
          return $scheme.'://'.$abs;
      }
      

      希望这对某人有所帮助!

      【讨论】:

        【解决方案9】:

        此函数将在$pgurl 没有正则表达式中将相对 URL 解析为 给定当前页面 URL。成功解决:

        /home.php?example 类型,

        same-dir nextpage.php 类型,

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

        完整的http://example.net 网址,

        和速记//example.net urls

        //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 转换为绝对 URL 之后调用。

        dots 函数有点多余,但可读、快速、不使用正则表达式,并且可以解析 99% 的典型 url(如果你想 100% 确定,只需扩展 switch 块以支持 6+点,虽然我从未在 URL 中看到过这么多点)。

        希望这会有所帮助,

        【讨论】:

          【解决方案10】:

          你可以使用这个作曲家包来做到这一点。 https://packagist.org/packages/wa72/url

          作曲家需要 wa72/url

          • 将 URL 字符串解析为对象

          • 添加和修改查询参数

          • 设置和修改url的任何部分

          • 用 PHP 风格的查询参数测试 URL 的相等性 方式

          • 支持协议相关的网址

          • 将绝对、主机相关和协议相关的 URL 转换为 相对的,反之亦然

          【讨论】:

            【解决方案11】:

            这使@jordansstephens 的不支持绝对网址的答案以'//'开头。

            function rel2abs($rel, $base)
            {
                /* return if already absolute URL */
                if (parse_url($rel, PHP_URL_SCHEME) != '') return $rel;
            
                /* Url begins with // */
                if($rel[0] == '/' && $rel[1] == '/'){
                    return 'https:' . $rel;
                }
            
                /* queries and anchors */
                if ($rel[0]=='#' || $rel[0]=='?') return $base.$rel;
            
                /* parse base URL and convert to local variables:
                   $scheme, $host, $path */
                extract(parse_url($base));
            
                /* remove non-directory element from path */
                $path = preg_replace('#/[^/]*$#', '', $path);
            
                /* destroy path if relative url points to root */
                if ($rel[0] == '/') $path = '';
            
                /* dirty absolute URL */
                $abs = "$host$path/$rel";
            
                /* replace '//' or '/./' or '/foo/../' with '/' */
                $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
                for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
            
                /* absolute URL is ready! */
                return $scheme.'://'.$abs;
            }
            

            【讨论】:

              【解决方案12】:

              如果相关目录已经存在,这将完成这项工作:

              function rel2abs($relPath, $baseDir = './')
              { 
              if ('' == trim($path))
              {
                  return $baseDir;
                  }
                  $currentDir = getcwd();
                  chdir($baseDir);
                  $path = realpath($path);
                  chdir($currentDir);
                  return $path;
              }
              

              【讨论】:

              • 该参数称为 $relPath,但您在整个函数体中使用 $path。纠正其中之一...
              猜你喜欢
              • 2015-01-24
              • 2011-02-21
              • 2017-08-01
              • 2011-11-07
              • 2011-05-02
              相关资源
              最近更新 更多