Urlencode только каталог и имена файлов URL-адреса

Мне нужно, чтобы URL-адрес кодировал только путь к каталогу и имя файла URL с помощью PHP.

Итак, я хочу кодировать что-то вроде http://example.com/file name и получить результат http://example.com/file%20name.

Конечно, если я делаю urlencode('http://example.com/file name');, тогда я получаю http%3A%2F%2Fexample.com%2Ffile+name.

Очевидным (для меня, в любом случае) решением является использование parse_url() для разделения URL-адреса на схему, хост и т.д., а затем просто urlencode() части, которые нуждаются в ней, как путь. Затем, я бы собрал URL-адрес, используя http_build_url().

Есть ли более элегантное решение? Или это в основном способ пойти?

Ответ 1

@deceze определенно заставил меня спуститься по правильному пути, так что держите его ответ. Но вот что именно сработало:

    $encoded_url = preg_replace_callback('#://([^/]+)/([^?]+)#', function ($match) {
                return '://' . $match[1] . '/' . join('/', array_map('rawurlencode', explode('/', $match[2])));
            }, $unencoded_url);

Можно отметить несколько вещей:

  • http_build_url требует установки PECL, поэтому, если вы распространяете свой код другим (как и я в этом случае), вам может потребоваться его избежать и придерживаться анализатора reg exp, как я это сделал (сильно воровство от @deceze ответ - опять же, идите вверх по этой вещи).

  • urlencode() - это не путь! Вам нужно rawurlencode() для пути, чтобы пробелы были закодированы как %20, а не +. Кодирование пробелов как + отлично подходит для строк запроса, но не так горячо для путей.

  • Это не будет работать для URL-адресов, для которых требуется имя пользователя/пароль. Для моего случая использования я не думаю, что меня это волнует, поэтому я не волнуюсь. Но если ваш вариант использования отличается в этом отношении, вам нужно позаботиться об этом.

Ответ 2

Как вы говорите, что-то в этом направлении должно это сделать:

$parts = parse_url($url);
if (!empty($parts['path'])) {
    $parts['path'] = join('/', array_map('rawurlencode', explode('/', $parts['path'])));
}
$url = http_build_url($parts);

Или, возможно:

$url = preg_replace_callback('#https?://.+/([^?]+)#', function ($match) {
           return join('/', array_map('rawurlencode', explode('/', $match[1])));
       }, $url);

(Regex не полностью протестирован)

Ответ 3

function encode_uri($url){
    $exp = "{[^0-9a-z_.!~*'();,/?:@&=+$#%\[\]-]}i";
    return preg_replace_callback($exp, function($m){
        return sprintf('%%%02X',ord($m[0]));
    }, $url);
}

Ответ 4

Я думаю, что эта функция в порядке:

function newUrlEncode ($url) {
    return str_replace(array('%3A', '%2F'), '/', urlencode($url));
}

Ответ 5

Гораздо проще:

$encoded = implode("/", array_map("rawurlencode", explode("/", $path)));