【问题标题】:PHP: How to resolve a relative urlPHP:如何解析相对网址
【发布时间】:2010-11-17 14:57:20
【问题描述】:

我需要一个函数,它给出一个相对 URL,并且一个基返回一个绝对 URL。我搜索并发现了许多以不同方式执行此操作的函数。

resolve("../abc.png", "http://example.com/path/thing?foo=bar")
# returns http://example.com/abc.png

有没有规范的方法?

在这个网站上,我看到了 python 和 c# 的很好的例子,让我们得到一个 PHP 解决方案。

【问题讨论】:

标签: php url relative-path resolveurl relative-url


【解决方案1】:

如果您已经使用GuzzleHttp,另一种解决方案。

此解决方案基于GuzzleHttp\Client的内部方法。

use GuzzleHttp\Psr7;

function resolve(string $uri, ?string $base_uri): string
{
    $uri = Psr7\uri_for($uri);

    if (isset($base_uri)) {
        $uri = Psr7\UriResolver::resolve(Psr7\uri_for($base_uri), $uri);
    }

    // optional: set default scheme if missing
    $uri = $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;

    return (string) $uri;
}

【讨论】:

  • 这段代码有点老了,但我认为最好的解决方案,这里是现代版本`use GuzzleHttp\Psr7\UriResolver;使用 GuzzleHttp\Psr7\Utils;函数解析($uri, $base_uri):string { $uri = Utils::uriFor($uri); if (isset($base_uri)) { $uri = UriResolver::resolve( Utils::uriFor($base_uri), $uri); } // 可选:如果缺少则设置默认方案 $uri = $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;返回(字符串)$uri; } `
【解决方案2】:

这是另一个可以处理协议相关 url 的函数

<?php
function getAbsoluteURL($to, $from = null) {
    $arTarget = parse_url($to);
    $arSource = parse_url($from);
    $targetPath = isset($arTarget['path']) ? $arTarget['path'] : '';

    if (isset($arTarget['host'])) {
        if (!isset($arTarget['scheme'])) {
            $proto = isset($arSource['scheme']) ? "{$arSource['scheme']}://" : '//';
        } else {
            $proto = "{$arTarget['scheme']}://";
        }
        $baseUrl = "{$proto}{$arTarget['host']}" . (isset($arTarget['port']) ? ":{$arTarget['port']}" : '');
    } else {
        if (isset($arSource['host'])) {
            $proto = isset($arSource['scheme']) ? "{$arSource['scheme']}://" : '//';
            $baseUrl = "{$proto}{$arSource['host']}" . (isset($arSource['port']) ? ":{$arSource['port']}" : '');
        } else {
            $baseUrl = '';
        }
        $arPath = [];

        if ((empty($targetPath) || $targetPath[0] !== '/') && !empty($arSource['path'])) {
            $arTargetPath = explode('/', $targetPath);
            if (empty($arSource['path'])) {
                $arPath = [];
            } else {
                $arPath = explode('/', $arSource['path']);
                array_pop($arPath);
            }
            $len = count($arPath);
            foreach ($arTargetPath as $idx => $component) {
                if ($component === '..') {
                    if ($len > 1) {
                        $len--;
                        array_pop($arPath);
                    }
                } elseif ($component !== '.') {
                    $len++;
                    array_push($arPath, $component);
                }
            }
            $targetPath = implode('/', $arPath);
        }
    }

    return $baseUrl . $targetPath;
}

// SAMPLES
// Absolute path => https://www.google.com/doubleclick/
echo getAbsoluteURL('/doubleclick/', 'https://www.google.com/doubleclick/insights/') . "\n";
// Relative path 1 => https://www.google.com/doubleclick/studio
echo getAbsoluteURL('../studio', 'https://www.google.com/doubleclick/insights/') . "\n";
// Relative path 2 => https://www.google.com/doubleclick/insights/case-studies.html
echo getAbsoluteURL('./case-studies.html', 'https://www.google.com/doubleclick/insights/') . "\n";
// Relative path 3 => https://www.google.com/doubleclick/insights/case-studies.html
echo getAbsoluteURL('case-studies.html', 'https://www.google.com/doubleclick/insights/') . "\n";
// Protocol relative url => https://www.google.com/doubleclick/
echo getAbsoluteURL('//www.google.com/doubleclick/', 'https://www.google.com/doubleclick/insights/') . "\n";
// Empty path => https://www.google.com/doubleclick/insights/
echo getAbsoluteURL('', 'https://www.google.com/doubleclick/insights/') . "\n";
// Different url => http://www.yahoo.com/
echo getAbsoluteURL('http://www.yahoo.com/', 'https://www.google.com') . "\n";

【讨论】:

    【解决方案3】:

    我注意到上面赞成的答案使用了 RegEx,这在处理 URL 时可能很危险。

    此函数将在$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 中看到过这么多点)。

    希望这会有所帮助,

    【讨论】:

      【解决方案4】:

      也许这篇文章可以提供帮助?

      http://nashruddin.com/PHP_Script_for_Converting_Relative_to_Absolute_URL

      编辑:为方便起见,在下面复制代码

      <?php
          function rel2abs($rel, $base)
          {
              /* return if already absolute URL */
              if (parse_url($rel, PHP_URL_SCHEME) != '' || substr($rel, 0, 2) == '//') 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;
          }
      ?>
      

      【讨论】:

      • 如果基本 URL 是:foobar.com 没有尾随 /,则此实现不起作用。它也不尊重基本 URL 中的端口号。
      • 它的设计是忽略任何为此使用的 RFC 规范。我会说这是 猜测 绝对 URL 或相对 URL。
      【解决方案5】:

      已在 pguardiario 评论中链接的页面中链接的其他工具:http://publicmind.in/blog/urltoabsolute/https://github.com/monkeysuffrage/phpuri

      我从http://nadeausoftware.com/articles/2008/05/php_tip_how_convert_relative_url_absolute_url 的评论中找到了其他工具:

      require_once 'Net/URL2.php';
      $base = new Net_URL2('http://example.org/foo.html');
      $absolute = (string)$base->resolve('relative.html#bar'); 
      

      【讨论】:

        【解决方案6】:

        如果你有pecl-http,你可以使用http://php.net/manual/en/function.http-build-url.php

        <?php
        $url_parts = parse_url($relative_url);
        $absolute = http_build_url($source_url, $url_parts, HTTP_URL_JOIN_PATH);
        

        例如:

        <?php
        function getAbsoluteURL($source_url, $relative_url)
        {
            $url_parts = parse_url($relative_url);
            return http_build_url($source_url, $url_parts, HTTP_URL_JOIN_PATH);
        }
        echo getAbsoluteURL('http://foo.tw/a/b/c', '../pic.jpg') . "\n";
        // http://foo.tw/a/pic.jpg
        
        echo getAbsoluteURL('http://foo.tw/a/b/c/', '../pic.jpg') . "\n";
        // http://foo.tw/a/b/pic.jpg
        
        echo getAbsoluteURL('http://foo.tw/a/b/c/', 'http://bar.tw/a.js') . "\n";
        // http://bar.tw/a.js
        
        echo getAbsoluteURL('http://foo.tw/a/b/c/', '/robots.txt') . "\n";
        // http://foo.tw/robots.txt
        

        【讨论】:

        • 仅供参考,http_build_url 方法是未与 PHP 捆绑的 PECL 扩展的一部分。
        • 这如描述的那样工作,但在安装 pecl 扩展时要小心。新发布的 2.0 版本现在使用命名空间,并没有直接提供这个功能。所以我安装了一个旧版本,它非常适合我:pecl install pecl_http-1.7.6
        【解决方案7】:
        function absoluteUri($Path, $URI)
        {   # Requires PHP4 or better.
            $URL = parse_url($URI);
            $Str = "{$URL['scheme']}://";
        
            if (isset($URL['user']) || isset($URL['pass']))
                $Str .= "{$URL['user']}:{$URL['pass']}@";
        
            $Str .= $URL['host'];
        
            if (isset($URL['port']))
                $Str .= ":{$URL['port']}";
        
            $Str .= realpath($URL['path'] . $Path); # This part might have an issue on windows boxes.
        
            if (isset($URL['query']))
                $Str .= "?{$URL['query']}";
        
            if (isset($URL['fragment']))
                $Str .= "#{$URL['fragment']}";
        
            return $Str;
        }
        
        absoluteUri("../abc.png", "http://example.com/path/thing?foo=bar");
        # Should return "http://example.com/abc.png?foo=bar" on Linux boxes.
        

        【讨论】:

        • 在 linux 机器上而不是 windows 上?这似乎是迄今为止唯一不值得考虑的解决方案。
        猜你喜欢
        • 2021-10-04
        • 1970-01-01
        • 2011-04-05
        • 2012-04-09
        • 2022-01-16
        • 2012-09-03
        • 2013-05-02
        相关资源
        最近更新 更多