Глава 11 · С нуля к обработке запроса
Суперглобальные переменные: откуда PHP берёт данные запроса
Карта $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION, $_SERVER, $_ENV, $_REQUEST и $GLOBALS: источник, доступ, типы и безопасное чтение.
Что означает «суперглобальная» и кто заполняет массив
Данные приходят разными путями
PHP не складывает всё в один массив: источник определяет, где читать значение.
- Адрес и поля браузера
Пришли извне: сначала проверяем тип, смысл и допустимый размер.
- URL: ?page=2 → $_GET
$_GET['page'] — строка «2», пока мы её не проверили и не преобразовали.
- POST-форма → $_POST
name="email" связывается с $_POST['email']. JSON читают отдельно.
- Загрузка → $_FILES
Там метаданные и временный путь; содержимое не становится строкой в $_POST.
- Браузер и серверное состояние
Не путайте присланный идентификатор с хранящимися на сервере данными.
- Cookie → $_COOKIE
Браузер прислал значения cookie. Их можно подделать, удалить или не прислать.
- Сессия → $_SESSION
session_start() восстанавливает данные сессии через настроенное хранилище.
- Метаданные → $_SERVER
Метод, пути и сведения о запросе; набор полей зависит от окружения.
$_ENV, $_REQUEST и $GLOBALS разобраны в таблице ниже. Стрелка показывает происхождение, а не доверенность данных.
Нужно выбрать страницу каталога из адреса /example.php?page=2. Значение 2 пришло от человека, поэтому PHP заранее положил его в $_GET. Это именованный набор значений — массив. Запись $_GET['page'] означает «взять значение с ключом page». Вы не создаёте $_GET присваиванием: в HTTP его наполняет PHP из URL.
Суперглобальная — переменная, доступная и снаружи, и внутри функции без объявления global. «Супер» относится к доступности имени, а не к качеству данных или хранению между запросами. Как подписанный лоток входящих документов, $_GET группирует данные по ключам; но на диске такого лотка нет, и новый запрос получает свои значения.
- GET /example.php?page=2
- PHP создаёт $_GET['page'] = '2'
- Проверяем: строка и целое от 1 до 20
- В ответе: «Страница каталога 2»
Вы можете сами создать $name = 'Анна';. Но адрес страницы, поля формы и cookie приходят извне. Для них PHP предоставляет специальные переменные. Суперглобальная переменная доступна в любой области видимости, в том числе внутри функции без global. Это доступность имени, а не гарантия доверия к содержимому.
| Переменная | Источник и задача | Чего не предполагать |
|---|---|---|
$_GET | Параметры строки запроса URL, например ?page=2 | Есть и при POST-запросе с query string; значения обычно строки или массивы |
$_POST | Поля тела POST для urlencoded/multipart | JSON не превращается в $_POST автоматически |
$_FILES | Метаданные и временные пути multipart-загрузок | Клиентское имя и MIME не являются доказательством |
$_COOKIE | Cookie, присланные браузером | Посетитель может изменить или удалить их |
$_SESSION | Данные сессии после session_start() | Их нужно связывать с проверенным пользователем, а не доверять user_id из формы |
$_SERVER | Метаданные запроса и окружения сервера | Набор зависит от SAPI; HTTP_* часто отражают клиентские заголовки |
$_ENV | Переменные окружения при соответствующей конфигурации | Массив может быть пуст; getenv имеет собственный контракт |
$_REQUEST | Объединение GET/POST/COOKIE по настройкам | Происхождение и порядок приоритетов скрыты; явно выбирайте источник |
$GLOBALS | Переменные глобальной области PHP | Это не автоматически «настройки сайта» и не общее хранилище всех запросов |
Данные из URL, тела, cookie и заголовков сначала проверяют. Число 2 в адресе обычно приходит строкой "2"; запись page[]=2 создаёт массив. Необязательный ключ может отсутствовать.
Проверить данные в настоящем HTTP-запросе
Начните с одного источника. ?? '1' выбирает первую страницу при отсутствии параметра. filter_var получает значение, фильтр целого числа и карту options с минимальным и максимальным допустимым значением. При успехе возвращает int; при отказе — false. Строгое === false проверяет именно отказ.
Ниже вход не подставлен автором: он приходит через браузер. Массив options — настройки нашей проверки; $_GET — отдельный массив с внешними данными. Их одинаковая синтаксическая форма не делает их одинаково доверенными.
<?php
$rawPage = $_GET['page'] ?? '1';
if (!is_string($rawPage)) {
http_response_code(422);
exit('Номер страницы должен быть одной строкой.');
}
$page = filter_var($rawPage, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 20]]);
if ($page === false) {
http_response_code(422);
exit('Выберите страницу от 1 до 20.');
}
header('Content-Type: text/plain; charset=utf-8');
echo 'Страница каталога ', $page;
Сохраните example.php, запустите php -S 127.0.0.1:8080 и откройте /example.php?page=2: статус 200, «Страница каталога 2». Без query — «Страница каталога 1». ?page=0 даст 422 и «Выберите страницу от 1 до 20.», ?page[]=2 — 422 и сообщение об одной строке. CLI php example.php показывает только значение по умолчанию и не проверяет доставку GET.
Сравните ?page=2 и ?page=3: меняется только значение, а путь через проверку одинаков. Если поле нужно нескольким функциям, передавайте уже проверенный $page аргументом. Следующие разделы объясняют доступность суперглобальных имён и другие источники; готовая форма поиска избавляет посетителя от ручного редактирования URL.
Первое чтение: отсутствующий ключ и неожиданный тип
<?php
declare(strict_types=1);
$query = ['page' => '2'];
$rawPage = $query['page'] ?? '1';
if (!is_string($rawPage)) {
echo "Номер страницы должен быть строкой\n";
} else {
$page = filter_var($rawPage, FILTER_VALIDATE_INT, [
'options' => ['min_range' => 1, 'max_range' => 1000],
]);
echo $page === false ? "Некорректная страница\n" : "Страница: $page\n";
}
Ожидаемый результат
Страница: 2
$query — снимок для независимого запуска; в обработчике источником будет $_GET. ?? задаёт значение при отсутствующем ключе/null, но не проверяет тип и диапазон. filter_var вернёт int при успехе либо false при ошибке, поэтому сравнение строгое. Попробуйте [], ['page' => '0'] и ['page' => ['2']].
Если значение понадобится нескольким функциям, прочитайте его на входе запроса, проверьте и передайте аргументом. Так функция расчёта сможет работать и на сайте, и в CLI, и в тесте.
Суперглобальная доступна внутри функции, но явный аргумент удобнее проверять
<?php
declare(strict_types=1);
function requestMethod(array $server): string
{
$value = $server['REQUEST_METHOD'] ?? 'GET';
return is_string($value) ? $value : 'GET';
}
echo requestMethod(['REQUEST_METHOD' => 'POST']), "\n";
echo requestMethod([]), "\n";
Ожидаемый результат
POST
GET
В HTTP-контроллере вызов выглядит requestMethod($_SERVER). В примере массив передан явно, поэтому результат не зависит от запуска через CLI или веб-сервер. GET здесь выбран как учебное значение по умолчанию; обработчик изменения данных должен явно требовать POST, а не разрешать действие при отсутствии метода.
Прямая запись $_POST['email'] внутри функции синтаксически разрешена. Но она связывает функцию с текущим запросом. Для проверки адреса понятнее validateEmail($value): источник данных выбирает вызывающая сторона.
Ключи $_SERVER различаются между веб-сервером и CLI. Не выводите весь массив в публичную страницу: окружение и заголовки могут содержать секреты. Для учебной диагностики смотрите только нужные поля и вымышленные данные.
GET и POST могут присутствовать одновременно
- Строка запроса source=footer → $_GET['source']
- Тело email=reader%40example.test → $_POST['email']
- Метод POST → $_SERVER['REQUEST_METHOD']
Название $_GET относится к параметрам URL, а не к условию «запрос обязательно GET». Для формы action="contact.php?source=footer" method="post" источник перехода попадёт в $_GET, поля — в $_POST. При одинаковом имени в двух источниках решите явно, какое значение разрешено использовать.
Для JSON читают file_get_contents('php://input') и затем json_decode, проверяя ошибки, размер и структуру. Для загружаемого файла используют $_FILES. Подробные маршруты: HTML → GET/POST, файл → $_FILES, JSON-тело webhook.
Самопроверка: найдите источник до написания кода
Сайт получил POST /orders.php?preview=1. В теле формы quantity=2, браузер прислал cookie theme=dark. Где взять каждое значение, и почему цена из скрытого поля price не должна определять цену заказа?
Разобрать решение
preview читают из $_GET, quantity из $_POST, theme из $_COOKIE. Все три значения пришли от клиента. Количество проверяют по типу, диапазону и правилам заказа; theme — по разрешённому списку. Цену сервер получает из своего каталога с учётом разрешённых скидок, иначе посетитель мог бы изменить её до отправки.
Правила внешних имён и границы источников
| Случай | Что требуется помнить |
|---|---|
| name="profile[email]" | Получится $_POST["profile"]["email"]. Проверить надо обе ступени: profile должен быть массивом, email — строкой. |
| name="a.b" или name="a b" | PHP преобразует точку/пробел в подчёркивание; избегайте неоднозначных имён и используйте простой согласованный набор. |
| name="foo[bar]tail" | После корректной записи массива суффикс может игнорироваться: получится foo/bar, а не отдельное поле с tail. |
| input type=image с name="pick" | Координаты нажатия становятся pick_x и pick_y. Обычная кнопка submit проще для типовой формы. |
| $_REQUEST | Состав и приоритет GET/POST/COOKIE задаются request_order/variables_order. Источник неочевиден, поэтому обработчик читает $_POST или $_GET явно. Это не живая ссылка на их будущие изменения. |
| $_COOKIE и setcookie | Входной массив описывает cookie текущего запроса. setcookie формирует заголовок для браузера; новый cookie придёт с последующим запросом. |
| $_ENV и getenv | Доступность зависит от конфигурации и окружения процесса. Не отражайте секреты из переменных окружения в страницу. |
| $GLOBALS | Даёт доступ к глобальным переменным данного выполнения; не общий обмен между посетителями. В современных версиях ограничены операции над всем массивом $GLOBALS. |
Внешний ввод остаётся недоверенным независимо от источника. Скрытое поле, cookie и HTTP-заголовок тоже меняются клиентом. Для всех предопределённых переменных сохраняется отдельный официальный справочник; практические маршруты продолжаются через сессии, $_FILES и области видимости.