Почему $_REQUEST показывает не то, что ты ожидаешь: разбираемся с variables_order

Буквально на днях столкнулся с забавной ситуацией — дебажил одну форму, где GET-параметр почему-то не попадал в $_REQUEST. Или попадал, но не тот, который я ожидал увидеть. Оказалось, что виновата настройка variables_order в php.ini, о которой многие разработчики либо забывают, либо вообще не знают.

Что вообще происходит?

Представь: у тебя есть страница с параметром в URL (?debug=1) и одновременно cookie с тем же именем (debug=0). Казалось бы, логично ожидать, что $_REQUEST[‘debug’] будет равно 1, ведь GET-параметры приоритетнее… Но нет! PHP может показать тебе 0 из cookie.

Вот реальный пример из жизни:

// URL: example.com/page.php?status=active
// Cookie: status=inactive

echo $_GET['status'];     // active
echo $_COOKIE['status'];  // inactive  
echo $_REQUEST['status']; // inactive (!)

Сначала я думал, что где-то накосячил с кодом. Потом полез в настройки.

variables_order — вот где собака зарыта

В php.ini есть параметр variables_order, который определяет в каком порядке PHP собирает данные для $_REQUEST. По умолчанию там стоит «EGPCS»:

  • E — Environment (переменные окружения)
  • G — GET
  • P — POST
  • C — Cookie
  • S — Server

Фишка в том, что если один и тот же ключ встречается в разных источниках, последний перезаписывает предыдущие. То есть при EGPCS приоритет такой: Environment < GET < POST < Cookie < Server.

Поэтому cookie перетирает GET-параметр!

Практический пример

Допустим, у нас есть форма поиска, которая запоминает последний запрос в cookie:

<?php
// Устанавливаем cookie при первом поиске
if (isset($_GET['q'])) {
    setcookie('q', $_GET['q'], time() + 3600);
}

// При variables_order = "EGPCS"
// URL: /search?q=новый запрос
// Cookie: q=старый запрос

var_dump($_GET['q']);     // "новый запрос"
var_dump($_COOKIE['q']);  // "старый запрос"  
var_dump($_REQUEST['q']); // "старый запрос" — ups!
?>

Получается парадокс: пользователь ввёл новый поисковый запрос, но система использует старый из cookie.

Как это фиксить?

Вариант 1: Не используй $_REQUEST вообще. Серьёзно, это 2025 год! Работай напрямую с $_GET, $_POST, $_COOKIE.

// Вместо $_REQUEST['param']
$param = $_GET['param'] ?? $_POST['param'] ?? null;

Вариант 2: Поменяй variables_order в php.ini на «EGCPS» (cookie перед GET/POST):

variables_order = "EGCPS"

Вариант 3: Создай свою функцию для получения параметров с нужным приоритетом:

function getParam($key, $default = null) {
    // Приоритет: POST > GET > COOKIE
    return $_POST[$key] ?? $_GET[$key] ?? $_COOKIE[$key] ?? $default;
}

Почему это вообще так устроено?

Честно говоря, логика PHP в этом вопросе мне не очень понятна. variables_order появился ещё в PHP 4, когда веб был другим. Тогда, видимо, считалось нормальным смешивать все источники данных в одну кучу.

Сейчас это скорее источник багов, чем полезная фича. Именно поэтому современные фреймворки (Laravel, Symfony) вообще не используют $_REQUEST и работают с отдельными источниками данных.

Мораль истории

$_REQUEST — это legacy, от которого лучше отказаться. Если уж совсем нужно получить параметр «откуда угодно», лучше написать свою функцию с понятной логикой приоритетов.

И да, обязательно проверь свой variables_order, если работаешь с чужим кодом. Можешь сэкономить пару часов дебага!

// Быстрая проверка текущих настроек
echo "variables_order: " . ini_get('variables_order');

P.S. Кстати, request_order — это отдельная настройка, которая влияет только на $_REQUEST. Но это уже другая история…

Оставить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *