如何在PHP中只对url的非ASCII符号进行url编码,而不对保留符号进行编码


How to url-encode only non-ASCII symbols of URL in PHP, but leave reserved symbols un-encoded?

我有一个URL,看起来像这样(注意"符号):

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

如果这很重要的话,我会从SimplePie解析器收到它。现在,如果你尝试在浏览器中访问这个特定的URL,并从地址栏中复制它,你会得到一个非ASCII符号百分比编码的URL:

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

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

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

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

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

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

经过一番研究,我得出结论,在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);
    }   
}

此函数可能会有所帮助:

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);
}

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

<?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"
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%D0%BA%D0%B2%D0%B0%D1%91