【问题标题】:How to url-encode only non-ASCII symbols of URL in PHP, but leave reserved symbols un-encoded?如何在 PHP 中仅对 URL 的非 ASCII 符号进行 url 编码,但保留未编码的符号?
【发布时间】:2012-04-07 12:48:21
【问题描述】:

我有一个如下所示的网址(注意“„ 符号):

http://tinklarastis.omnitel.lt/kokius-aptarnavimo-kanalus-klientui-siulo-„omnitel“-1494

我从 SimplePie 解析器收到它,如果这很重要的话。现在,如果您尝试在浏览器中访问此特定 URL 并从地址栏中复制它,您将获得一个包含非 ASCII 符号的 URL percent encoded:

http://tinklarastis.omnitel.lt/kokius-aptarnavimo-kanalus-klientui-siulo-%E2%80%9Eomnitel%E2%80%9C-1494

我试图了解如何在 PHP 中模拟相同的转换。我不能简单地使用urlencode() 或urlrawencode(),因为它们同时编码了非ASCII 符号和 保留符号,而在我的情况下,保留符号(/?& 等)应该保持原样。

到目前为止,我只看到solutions 涉及将 URL 拆分为保留符号之间的片段,然后使用 urlencode(),但这对我来说感觉很骇人听闻,我希望有一个更优雅的解决方案。我尝试了iconv()、mb_convert_encoding() 的各种变体,但都没有成功。

【问题讨论】:

  • 您链接到的解决方案中有什么“骇人听闻”的地方?您正在寻找的琐碎字符串操作的“优雅”方式是什么?
  • @YourCommonSense 我可能错了,但对我来说,我的情况似乎不是任意字符串操作练习。相反,它是一个相当通用的编码任务——浏览器在从地址栏复制 URL 时这样做表明它背后应该有一些标准/含义。
  • 是的,这是通用编码任务。这本身就是微不足道的字符串操作。而且您已经有了解决方案。如果您已经找到答案,我认为发布另一个问题毫无意义。
  • 我正在寻找一种明确的方法来解决这个任务。为了转义 HTML,可以使用 htmlspecialchars() 或者只使用字符代码和 str_replace() 编写自定义函数。你是对的,我知道自定义方式,但我正在寻找一种使用内置字符串操作函数的解决方案(无论它们多么微不足道)。

标签: php utf-8 urlencode


【解决方案1】:

我有一个简单的单行代码,用于仅使用 preg_match_callback 对非 ASCII 字符进行就地编码:

preg_replace_callback('/[^\x20-\x7f]/', function($match) {
    return urlencode($match[0]);
}, $url);

请注意,匿名函数仅在 PHP 5.3+ 中受支持。

【讨论】:

  • 这应该是公认的答案。 它处理 URL 中任意位置的非 ASCII 字符(路径 和 查询字符串),并且不需要执行检查,如 OP 的答案中的“避免双重编码”。
  • 很好的解决方案,谢谢!如果您还希望将标准空白编码为+,请改用'/[^\x21-\x7f]/'。如果您希望将它们编码为 %20 以符合 RFC 3986,请调用 rawurlencode() 而不是 urlencode()。
【解决方案2】:
function cyrillicaToUrlencode($text){
return $line = preg_replace_callback('/([а-яё])/ui',
                            function ($matches) {
                                return urlencode($matches[0]);
                            }, 
                            $text); 
}

echo cyrillicaToUrlencode("https://test.com/Москваёtext1Воронежtext2Москваёtext3yМоскваё___-Москваё");

将返回 - https://test.com/%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%D1%91text1%D0%92%D0%BE%D1%80%D0%BE%D0%BD%D0%B5%D0%B6text2%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%D1%91text3y%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%D1%91___-%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%D1%91

【讨论】:

    【解决方案3】:

    此功能可能会有所帮助:

    function sanitizeUrl($url)
    {
        $chars = '$-_.+!*\'(),{}|\\^~[]`<>#%";/?:@&=';
        $pattern = '~[^a-z0-9' . preg_quote($chars, '~') . ']+~iu';
    
        $callback = create_function('$matches', 'return urlencode($matches[0]);');
    
        return preg_replace_callback($pattern, $callback, $url);
    }
    

    【讨论】:

    • 只允许在 url 字符中使用:'-._~:/?#[]@!$&\'()*+,;='
    【解决方案4】:

    经过一番研究,我得出一个结论,在 PHP 中没有办法做得很好(但是,python / perl 等其他语言似乎确实有适合这种用例的功能)。这是我想出的功能(确保 URL 的路径片段编码):

    function url_path_encode($url) {
        $path = parse_url($url, PHP_URL_PATH);
        if (strpos($path,'%') !== false) return $url; //avoid double encoding
        else {
            $encoded_path = array_map('urlencode', explode('/', $path));
            return str_replace($path, implode('/', $encoded_path), $url);
        }   
    }
    

    【讨论】:

      【解决方案5】:

      我认为这会满足您的需求。

      <?php
      
      $string = 'http://tinklarastis.omnitel.lt/kokius-aptarnavimo-kanalus-klientui-siulo-„omnitel“-1494/?foo=bar&fizz=buzz';
      
      var_dump(filter_var($string, FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH));
      

      这会让你:

      $ php test.php
      string(140) "http://tinklarastis.omnitel.lt/kokius-aptarnavimo-kanalus-klientui-siulo-&#226;&#128;&#158;omnitel&#226;&#128;&#156;-1494/?foo=bar&fizz=buzz"
      

      【讨论】:

      • 建议不错,但与 RFC 3986 不匹配
      猜你喜欢
      • 1970-01-01
      • 2010-10-31
      • 1970-01-01
      • 2011-12-14
      • 1970-01-01
      • 1970-01-01
      • 2011-07-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多