【问题标题】:php parse_url reverse -- parsed urlphp parse_url reverse -- 解析的url
【发布时间】:2020-10-07 02:40:50
【问题描述】:

有没有办法从解析的 url 中反转 url?

$url = 'http://www.domain.com/dir/index.php?query=blabla#more_bla';
$parse = parse_url($url);
print_r($parse);
/*
array(
 'scheme'=>'http://',
 etc....
)
*/
$revere = reverse_url($parse); // probably does not exist but u get the point

echo $reverse;
//outputs:// "http://www.domain.com/dir/index.php?query=blabla#more_bla"

或者,如果有一种方法可以验证缺少推荐网址的一部分的网址,例如

www.mydomain.com

mydomain.com

应该全部返回 http://www.mydomain.com 或使用正确的子域

【问题讨论】:

  • 我查看了http_build_url,但如果网址不同并且$url 上未提及其他属性,这看起来很麻烦
  • www.example.comexample.com 都不是有效的绝对 URL;将被解释为 URL 路径。

标签: php parse-url


【解决方案1】:

这是我用于分解和重建 URL 的两个函数:

function http_parse_query($query) {
    $parameters = array();
    $queryParts = explode('&', $query);
    foreach ($queryParts as $queryPart) {
        $keyValue = explode('=', $queryPart, 2);
        $parameters[$keyValue[0]] = $keyValue[1];
    }
    return $parameters;
}

function build_url(array $parts) {
    return (isset($parts['scheme']) ? "{$parts['scheme']}:" : '') . 
        ((isset($parts['user']) || isset($parts['host'])) ? '//' : '') . 
        (isset($parts['user']) ? "{$parts['user']}" : '') . 
        (isset($parts['pass']) ? ":{$parts['pass']}" : '') . 
        (isset($parts['user']) ? '@' : '') . 
        (isset($parts['host']) ? "{$parts['host']}" : '') . 
        (isset($parts['port']) ? ":{$parts['port']}" : '') . 
        (isset($parts['path']) ? "{$parts['path']}" : '') . 
        (isset($parts['query']) ? "?{$parts['query']}" : '') . 
        (isset($parts['fragment']) ? "#{$parts['fragment']}" : '');
}

// Example
$parts = parse_url($url);

if (isset($parts['query'])) {
    $parameters = http_parse_query($parts['query']);
    foreach ($parameters as $key => $value) {
        $parameters[$key] = $value; // do stuff with $value
    }
    $parts['query'] = http_build_query($parameters);
}

$url = build_url($parts);

【讨论】:

    【解决方案2】:

    你应该可以的

    http_build_url($parse)
    

    注意:http_build_url 只能通过安装 pecl_http 获得。

    根据文档,它专门设计用于处理来自parse_url 的输出。这两个函数都处理锚点、查询参数等,因此没有“$url 上未提及的其他属性”。

    若要在http:// 缺失时添加它,请在解析之前使用基本检查:

    if (strpos($url, "http://") != 0)
        $url = "http://$url";
    

    【讨论】:

    • 我的问题是(也许我想多了)但是如果http://https:// 因为它不是很常见但电子商务网站有这个功能怎么办。我正在将其开发为 cms 系统,他们肯定需要使用 https 或其他协议,有时还需要使用端口。它会正确处理吗?
    • 唯一的方法是尝试向相关网站发出请求。为此,您可以查看php.net/manual/en/function.httprequest-send.php
    • 注意:致命错误:调用未定义函数 http_build_url() .... 我认为您可能需要安装一个额外的模块
    • 这是否可用于共享托管,因为您知道托管服务提供商有时如何:)
    • @TomaszKapłoński 我认为作为一个人们可以投票的独立答案会很好
    【解决方案3】:

    这个函数应该可以解决问题:

    function unparse_url(array $parsed): string {
        $pass      = $parsed['pass'] ?? null;
        $user      = $parsed['user'] ?? null;
        $userinfo  = $pass !== null ? "$user:$pass" : $user;
        $port      = $parsed['port'] ?? 0;
        $scheme    = $parsed['scheme'] ?? "";
        $query     = $parsed['query'] ?? "";
        $fragment  = $parsed['fragment'] ?? "";
        $authority = (
            ($userinfo !== null ? "$userinfo@" : "") .
            ($parsed['host'] ?? "") .
            ($port ? ":$port" : "")
        );
        return (
            (\strlen($scheme) > 0 ? "$scheme:" : "") .
            (\strlen($authority) > 0 ? "//$authority" : "") .
            ($parsed['path'] ?? "") .
            (\strlen($query) > 0 ? "?$query" : "") .
            (\strlen($fragment) > 0 ? "#$fragment" : "")
        );
    }
    

    这是一个简短的测试:

    function unparse_url_test() {
        foreach ([
            '',
            'foo',
            'http://www.google.com/',
            'http://u:p@foo:1/path/path?q#frag',
            'http://u:p@foo:1/path/path?#',
            'ssh://root@host',
            '://:@:1/?#',
            'http://:@foo:1/path/path?#',
            'http://@foo:1/path/path?#',
        ] as $url) {
            $parsed1 = parse_url($url);
            $parsed2 = parse_url(unparse_url($parsed1));
    
            if ($parsed1 !== $parsed2) {
                print var_export($parsed1, true) . "\n!==\n" . var_export($parsed2, true) . "\n\n";
            }
        }
    }
    
    unparse_url_test();
    

    【讨论】:

    • 为每个 URL 部分调用 isset 后跟 strlen 效率低下,因为 parse_url 仅使用具有值的部分填充返回数组。唯一返回 false 的 strlen 调用在您的代码中传递为 null,即使这样,与 null 进行比较也比调用函数更有效。
    • 为了进一步说明我的观点,parse_url 为您的示例中的最后一个 URL 返回 array("path"=>"://:@:1/")。完全去除"query""fragment"
    • 如果unparse_url() 仅被称为unparse_url(parse_url(...)),那是正确的,但是使用unparse_url() 的代码当然会parse_url() 的结果做一些事情在给unparse_url()之前。这可能涉及添加具有空值的键,因此该函数需要正确处理。
    • 公平点,如果这是您的用例,但是在我今天早些时候的 QA 测试中,我发现了一个您错过的测试用例:unparse_url(parse_url('http://:@foo:1/path/path?#')) 将导致 http://foo:1/path/path。现在,据我所知,没有服务使用空字符串作为匿名/访客用户名,但它仍然不匹配。
    • 看来userpassparse_url() 区分为空("")和未设置的仅有的两件事。我已经更新了代码,谢谢。
    【解决方案4】:

    另一个实现:

    function build_url(array $elements) {
        $e = $elements;
        return
            (isset($e['host']) ? (
                (isset($e['scheme']) ? "$e[scheme]://" : '//') .
                (isset($e['user']) ? $e['user'] . (isset($e['pass']) ? ":$e[pass]" : '') . '@' : '') .
                $e['host'] .
                (isset($e['port']) ? ":$e[port]" : '')
            ) : '') .
            (isset($e['path']) ? $e['path'] : '/') .
            (isset($e['query']) ? '?' . (is_array($e['query']) ? http_build_query($e['query'], '', '&') : $e['query']) : '') .
            (isset($e['fragment']) ? "#$e[fragment]" : '')
        ;
    }
    

    结果应该是:

    {
        "host": "example.com"
    }
    /* //example.com/ */
    
    {
        "scheme": "https",
        "host": "example.com"
    }
    /* https://example.com/ */
    
    {
        "scheme": "http",
        "host": "example.com",
        "port": 8080,
        "path": "/x/y/z"
    }
    /* http://example.com:8080/x/y/z */
    
    {
        "scheme": "http",
        "host": "example.com",
        "port": 8080,
        "user": "anonymous",
        "query": "a=b&c=d",
        "fragment": "xyz"
    }
    /* http://anonymous@example.com:8080/?a=b&c=d#xyz */
    
    {
        "scheme": "http",
        "host": "example.com",
        "user": "root",
        "pass": "stupid",
        "path": "/x/y/z",
        "query": {
            "a": "b",
            "c": "d"
        }
    }
    /* http://root:stupid@example.com/x/y/z?a=b&c=d */
    
    {
        "path": "/x/y/z",
        "query": "a=b&c=d"
    }
    /* /x/y/z?a=b&c=d */
    

    【讨论】:

      【解决方案5】:

      此答案是@BradMace 接受的答案的附录。我最初将此添加为评论,但他建议将其添加为单独的答案,所以在这里。

      pecl_http 提供的使用http_build_url($parse) 的原始答案适用于扩展版本1.x - 版本2.x 及更高版本是面向对象的并且语法已更改。

      在较新的版本中(在 pecl_http v.3.2.3 上测试)实现应该是:

      $httpUrl = new \http\Url($parsed);
      $url = $httpUrl->toString();
      

      【讨论】:

        【解决方案6】:

        十年后使用几十年前的方法:)

        考虑到您将始终拥有方案和主机。 (可选)路径、查询和片段偏移:

        $a = parse_url('https://example.com/whatever-season/?drink=water&sleep=better');
        

        用经典的 sprintf 重建:

        function buildUrl(array $a){
                return sprintf('%s://%s%s%s%s',
                                    $a['scheme'], $a['host'], $a['path'] ?? '',
                                    $a['query'] ? '?' . $a['query'] : '',
                                    $a['fragment'] ? '#' . $a['fragment'] : '');
        }
        

        其中 path 偏移量由 the null coalescing operator (PHP 7+) 操作。

        【讨论】:

        • 拥有方案和主机实际上并不是一个让步——您假设一个有效且完整的 URL 已传递给parse_url()。尝试使用相对 URL 来查看它。 parse_url('/abc/def?ghi');
        猜你喜欢
        • 2010-09-28
        • 2013-07-21
        • 1970-01-01
        • 1970-01-01
        • 2011-03-29
        • 1970-01-01
        • 2013-05-25
        • 2011-08-01
        相关资源
        最近更新 更多