Avatar ·

В чем разница между urlencode и rawurlencode в PHP и когда что использовать?

Всем привет! Я начинающий PHP-разработчик, и у меня возникла путаница с двумя функциями: `urlencode()` и `rawurlencode()`. Я знаю, что обе они используются для кодирования URL, но не могу понять, в чем между ними принципиальная разница и какой вариант правильнее использовать. Допустим, мне нужно передать в URL строку с кириллицей и пробелами: ```php $string = "Привет мир!"; echo urlencode($string); // %D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82+%D0%BC%D0%B8%D1%80%21 echo rawurlencode($string); // %D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80%21 ``` Я заметил, что `urlencode` заменяет пробел на `+`, а `rawurlencode` — на `%20`. Но когда я читал документацию RFC 3986, там говорится, что пробел должен кодироваться как `%20`. Почему же тогда существует функция `urlencode`, которая использует `+`? Это какая-то устаревшая версия? Или это особенность PHP? Меня особенно интересует, какой вариант использовать для: 1. Формирования GET-параметров в строке запроса 2. Кодирования частей URL (пути) 3. Передачи данных в AJAX-запросах Заранее спасибо за разъяснение! Примеры кода приветствуются. Хочется понять не просто "как", но и "почему" так сложилось исторически.<<>>

Основное различие: стандарты кодирования

Главное различие между urlencode() и rawurlencode() кроется в исторически сложившихся стандартах. urlencode() следует устаревшему стандарту application/x-www-form-urlencoded (RFC 1866), который используется для кодирования данных HTML-форм. rawurlencode() соответствует современному стандарту RFC 3986 для URL-адресов.

Ключевые отличия в кодировании символов

  • Пробел: urlencode() → +, rawurlencode() → %20
  • Символы -, _, ., ~: urlencode() кодирует ~ как %7E, а rawurlencode() оставляет его без изменений
  • Кириллица и спецсимволы: обе функции кодируют одинаково (в UTF-8)

Когда что использовать?

1. Для GET-параметров в строке запроса — используйте urlencode(), если вы работаете с HTML-формами или legacy-системами. Однако для современной разработки рекомендуется rawurlencode(), так как он строго соответствует RFC 3986:

// Современный подход (рекомендуется)
$name = "Иван Иванов";
$url = "https://example.com/api?name=" . rawurlencode($name);
// Результат: https://example.com/api?name=%D0%98%D0%B2%D0%B0%D0%BD%20%D0%98%D0%B2%D0%B0%D0%BD%D0%BE%D0%B2

// Legacy-подход (для обратной совместимости)
$url = "https://example.com/api?name=" . urlencode($name);
// Результат: https://example.com/api?name=%D0%98%D0%B2%D0%B0%D0%BD+%D0%98%D0%B2%D0%B0%D0%BD%D0%BE%D0%B2

2. Для кодирования частей URL (пути) — строго используйте rawurlencode(). Символ + в пути URL интерпретируется как литеральный плюс, а не как пробел:

// Кодирование пути
$filename = "документ версия 2.0.pdf";
$path = "/files/" . rawurlencode($filename);
// Результат: /files/%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%20%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8F%202.0.pdf

// НЕПРАВИЛЬНО: urlencode() для пути
$path = "/files/" . urlencode($filename);
// Результат: /files/%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82+%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8F+2.0.pdf
// Символ '+' будет воспринят как плюс, а не пробел!

3. Для AJAX-запросов — используйте rawurlencode(), если вы формируете URL вручную. Если используете jQuery или Fetch API с объектом данных, они автоматически кодируют правильно:

// Ручное формирование (плохая практика, но для примера)
$data = "ключ=значение с пробелом";
$ajaxUrl = "https://api.example.com/submit?" . rawurlencode($data);

// Правильный способ с http_build_query()
$params = ['ключ' => 'значение с пробелом'];
$queryString = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
// Результат: %D0%BA%D0%BB%D1%8E%D1%87=%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%81%20%D0%BF%D1%80%D0%BE%D0%B1%D0%B5%D0%BB%D0%BE%D0%BC

Практические рекомендации

  • Всегда используйте rawurlencode() для кодирования частей URL (путь, имя файла, поддомен). Это безопасно и соответствует стандарту.
  • Для query-параметров лучше использовать http_build_query() с флагом PHP_QUERY_RFC3986 — это автоматически применит правильное кодирование.
  • Избегайте urlencode() в новых проектах. Он существует для обратной совместимости со старыми системами, которые ожидают + вместо %20.
  • Помните о двойном кодировании: не применяйте эти функции к уже закодированным строкам. Используйте rawurldecode() перед повторным кодированием.
// Пример безопасного формирования URL
function buildSafeUrl(string $base, array $params): string {
    $query = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
    return $base . '?' . $query;
}

$url = buildSafeUrl('https://api.example.com/search', [
    'q' => 'PHP руководство',
    'page' => 1
]);
// Результат: https://api.example.com/search?q=PHP%20%D1%80%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE&page=1
<<>>

Альтернативный взгляд: когда urlencode() всё же нужен

Хотя rawurlencode() является стандартом де-факто, есть ситуации, где urlencode() незаменим:

  • Работа с устаревшими API, которые ожидают application/x-www-form-urlencoded и не понимают %20 (редко, но встречается в старых системах).
  • Эмуляция отправки HTML-формы через cURL или file_get_contents. Здесь urlencode() + http_build_query() без флага RFC3986 — правильный выбор.
// Отправка POST-запроса как HTML-форма
$data = ['name' => 'Иван Иванов', 'message' => 'Привет!'];
$options = [
    'http' => [
        'method' => 'POST',
        'header' => 'Content-type: application/x-www-form-urlencoded',
        'content' => http_build_query($data) // использует urlencode() по умолчанию
    ]
];
$context = stream_context_create($options);
$result = file_get_contents('http://example.com/form', false, $context);

Также стоит отметить, что urlencode() часто используется в комбинации с urldecode() для безопасной передачи данных через куки или сессионные переменные, где символ + не вызывает проблем.

Итог: raw