Буквально на днях столкнулся с забавной ситуацией — дебажил одну форму, где 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. Но это уже другая история…