← Все статьи

SiteGuard12 мин чтения

Форма сохраняет не все поля: как проверить max_input_vars в PHP

После нажатия «Сохранить» сайт показывает успех, но последние строки таблицы остаются прежними, часть настроек исчезает, а отдельные флажки сбрасываются. Если маленькая форма работает, а большая теряет данные, стоит проверить max_input_vars в PHP. Однако увеличение этой настройки помогает только тогда, когда потеря действительно происходит при разборе входных переменных.

Надёжная диагностика проходит всю цепочку: что браузер отправил, что PHP разобрал, что приложение признало допустимым, что записалось в базу и что вернулось при повторном чтении. Ниже — порядок такой проверки, расчёт необходимого лимита и условия, при которых частичный запрос нужно отклонить целиком.

Что ограничивает max_input_vars

В документации PHP для max_input_vars указано значение по умолчанию 1000. Ограничение применяется отдельно к переменным $_GET, $_POST и $_COOKIE. При превышении возникает предупреждение уровня E_WARNING, а последующие входные переменные отбрасываются. Фактическое значение на хостинге может отличаться от исходного.

Речь идёт о количестве входных переменных, а не о длине текста или размере запроса в мегабайтах. Тысяча коротких значений и одно длинное описание создают разные нагрузки на разбор. Уменьшение размера изображения не решает проблему множества полей, а повышение числа переменных не увеличивает допустимый размер всего тела запроса.

Характерный признак — зависимость от состава формы: при добавлении нескольких повторяемых блоков начинают пропадать значения ближе к концу передаваемого набора. Это гипотеза, которую нужно подтвердить. Если поле отсутствовало уже в браузерном запросе или было исключено валидатором, причина находится на другом этапе.

Проверку проводите на тестовой записи с узнаваемыми безопасными значениями. Не используйте реальные настройки магазина или заказы для многократного воспроизведения частичного сохранения.

Почему число элементов на странице не равно числу переменных

Браузер передаёт набор значений по правилам формы. В алгоритме HTML Standard отключённые элементы и неотмеченные checkbox или radio пропускаются. Обычному полю требуется непустое имя name; из кнопок учитывается соответствующий отправитель формы. Множественный select может дать несколько записей. Скрытые именованные поля тоже участвуют, если не исключены другими условиями.

Поэтому ориентироваться на число видимых строк нельзя. CMS может добавлять служебный идентификатор, признак удаления, порядок сортировки и скрытое значение для каждого флажка. Пользователь видит три редактируемые ячейки, а запрос содержит восемь переменных на строку.

Учитывайте способ отправки. JavaScript может сформировать собственный объект, дополнить FormData, удалить пустые значения или передать только изменённые поля. Тогда подсчёт элементов в DOM описывает интерфейс, но не фактическое тело запроса. Проверять нужно именно отправленный набор после всех преобразований.

Не включайте disabled-поле обратно только ради увеличения полноты запроса. Сначала выясните, должно ли оно вообще редактироваться этим пользователем и откуда сервер обязан брать его значение. Поведение интерфейса не заменяет проверку прав в обработчике.

Как считать массивы, скрытые значения и повторяющиеся имена

PHP понимает массивы в именах полей, что описано в руководстве по внешним переменным. Но группировка items[42][price] и items[42][quantity] не превращает две переданные пары в одну входную переменную. Общий ключ items служит контейнером результата, а не способом обойти ограничение количества.

По этой причине count($_POST) не является точным счётчиком отправленных полей. Большой массив может занимать один ключ верхнего уровня. Рекурсивный подсчёт тоже нужно трактовать осторожно: в него могут попасть контейнеры, а повторяющиеся имена при разборе не обязательно сохранятся как отдельные значения.

Считайте реальные передаваемые пары и сравнивайте их со структурой, которую ожидает обработчик. Для учебной строки с идентификатором, названием, ценой и количеством запишите четыре поля, а затем отдельно добавьте скрытые признаки и выбранные множественные значения. Не считайте одно и то же скрытое поле одновременно в служебной части и составе строки.

Проверьте ошибки именования. PHP преобразует точки и пробелы в именах переменных в подчёркивания; совпадения после преобразования могут маскировать проблему под потерю полей. Если пропадает конкретное имя независимо от размера формы, начните с него.

Как проверить исходный payload в браузере

На тестовом контуре откройте инструменты разработчика и найдите запрос, который действительно сохраняет форму. Зафиксируйте метод, URL, Content-Type и передаваемые данные. У сложной страницы может быть несколько запросов: предварительная проверка, загрузка вложения, сохранение строк и итоговое подтверждение. Не принимайте первый успешный ответ за результат всей операции.

Внесите разные тестовые значения в начало, середину и конец набора. Затем проверьте, присутствуют ли соответствующие имена и значения в отправленном payload. Если последнее поле не отправлено, изучайте сериализацию и обработчики JavaScript. Настройки PHP пока менять рано.

Для подсчёта используйте представление, которое сохраняет повторяющиеся пары. Превращение запроса в словарь с единственным значением на имя может скрыть дубли. Число строк в визуальной панели браузера тоже сверяйте с фактическим форматом: вложенные структуры иногда отображаются свёрнутыми.

Рабочие пароли, cookies, токены и персональные данные для этого эксперимента не нужны. Сохраняйте очищенную схему запроса и результаты подсчёта. Если требуется передать запись подрядчику, уберите секреты и оставьте достаточно структуры, чтобы воспроизвести порядок и количество полей.

На каком этапе исчезает значение

Сравнение браузера и базы напрямую оставляет слишком большой промежуток неизвестности. Добавьте временную диагностику на границах обработки. Её задача — показать наличие нужных ключей, число строк и итог валидации, а не записать всё содержимое формы в общий журнал.

ЭтапЧто сопоставитьКуда смотреть при расхождении
Исходящий запросОжидаемые имена с фактическим payloadСериализация, disabled, динамические поля
Разобранный ввод PHPНужные ключи и строки до бизнес-логикиЛимиты, кодировка, имена и тип тела
Данные после проверкиВходная структура и разрешённые измененияПравила валидации и преобразование типов
Запись в базуПодтверждённые изменения и результат транзакцииОшибки сохранения и ограничения таблиц
Повторное чтениеСохранённые значения и отображениеЧтение другой записи, кэш, форматирование

Используйте один идентификатор тестовой попытки во всех диагностических отметках. Иначе запрос одной вкладки легко сопоставить с записью другой. Для отсутствующего поля фиксируйте именно отсутствие, отдельно от пустой строки, нуля или false: эти состояния могут иметь разный смысл.

При повторном чтении выполните отдельный запрос к сохранённой тестовой записи. Значения, оставшиеся в открытом редакторе после нажатия кнопки, могут быть локальным состоянием страницы. Они ещё не доказывают, что база приняла изменения. Если чтение использует кэш, отдельно установите, какой источник проверяется и когда он обновляется.

Если PHP получил полный набор, а после проверки осталась половина, повышение max_input_vars будет лишь случайным изменением конфигурации. Исправлять нужно подтверждённое место потери.

Как узнать действующее значение в обработчике сайта

Проверьте настройку в том веб-процессе, который обслуживает проблемный URL. Для этого в защищённой диагностике используют ini_get('max_input_vars'), версию PHP и PHP_SAPI. Полезно также установить, какой конфигурационный файл и дополнительные настройки применены к этому обработчику.

Результат команды PHP в терминале не доказывает конфигурацию сайта. Консольный запуск и веб-запрос могут использовать разные версии, файлы настроек и окружения. Даже на одном сервере два домена иногда обслуживаются разными пулами PHP. Проверка должна относиться к фактическому пути сохранения, включая поддомен или отдельный backend.

Не публикуйте общедоступную страницу с полным phpinfo ради одной цифры. Достаточно ограниченной диагностической записи, доступной ответственному разработчику. После завершения проверки удалите временный вывод; в постоянных журналах оставьте только необходимые сведения.

Если запросы распределяются между несколькими узлами, проверьте каждый экземпляр. Разные значения лимита объясняют ситуацию, когда одинаковая форма сохраняется через раз. Вместе со значением фиксируйте идентификатор узла, чтобы успешный ответ одного сервера не скрывал старую конфигурацию другого.

Для обращения в поддержку подготовьте адрес обработчика без секретных параметров, время теста, его идентификатор, измеренное число переменных и фактическое значение настройки. Такой набор позволяет проверить конкретный запрос, не передавая подрядчику всю рабочую форму с пользовательскими данными.

Где искать предупреждение и как подтвердить обрезание

Сопоставьте время тестового запроса с журналом ошибок PHP или веб-пула. При превышении лимита ожидается предупреждение о количестве входных переменных. На рабочем сайте оно не обязано появляться на экране, поэтому сообщение «Сохранено» и HTTP 200 не опровергают частичный разбор.

Разбор обычной формы происходит до выполнения её PHP-обработчика. Поэтому включённое внутри скрипта отображение ошибок или поздно установленный пользовательский обработчик предупреждений может не показать причину. Проверяйте настройки журналирования уровня среды и события начала запроса.

Сильное подтверждение — совпадение трёх наблюдений: браузер отправил полный набор, в PHP нет последующих ожидаемых ключей, а журнал содержит соответствующее предупреждение для этой попытки. Затем на тестовом контуре измените только подтверждённый ограничивающий параметр и повторите тот же сценарий.

Если предупреждения нет, не объявляйте лимит виновником автоматически. Оно могло не записаться, но также возможны конфликт имён, другой разборщик, фильтрация или ошибка приложения. Диагностику продолжайте по зафиксированной цепочке данных.

Какие ограничения нельзя путать с max_input_vars

Post_max_size ограничивает размер POST-данных, включая загрузку файлов. Руководство PHP указывает: при превышении этого размера массивы $_POST и $_FILES оказываются пустыми. Это другая картина, чем сохранение начальной части большого набора. Внешний веб-сервер или прокси также может отклонить тело ещё до PHP.

Max_input_nesting_level ограничивает глубину вложенности входных массивов; документированное значение по умолчанию — 64. Форма с небольшим количеством очень глубоко вложенных ключей может упереться в эту границу, хотя число переменных далеко от тысячи. Увеличение только max_input_vars такую структуру не исправит.

Для multipart существует ещё max_multipart_body_parts: учитываются части с полями и файлами вместе. В официальном шаблоне конфигурации PHP значение по умолчанию −1 означает сумму max_input_vars и max_file_uploads, а не отсутствие ограничения. Сверяйте наличие и поведение настройки с установленной версией PHP.

Разделяйте четыре измерения: количество переменных, байты тела, глубину вложенности и количество частей multipart. При диагностике с файлами проверяйте также ошибки загрузки. Не повышайте все пределы одновременно: иначе будет трудно установить причину и оценить необходимый запас ресурсов.

Учебный расчёт: почему пять строк меняют результат

Предположим, тестовая форма содержит 12 общих скрытых полей, восемь передаваемых значений в каждой строке и шесть остальных управляющих полей. Все имена конечных значений различаются; файлы и переменное число выбранных вариантов в этом примере отсутствуют.

Состав формыРасчётВсего переменных
120 строк12 + 120 × 8 + 6978
125 строк12 + 125 × 8 + 61018
Проектный максимум: 150 строк12 + 150 × 8 + 61218

При действующем лимите 1000 второй набор превышает его на 18 переменных. Для стандартного разбора, если именно этот предел сработал первым, часть ожидаемых значений не попадёт в результат. При другом порядке передачи пропадут другие ключи: визуальное положение на странице не заменяет проверку порядка payload.

В этой условной задаче команда может проверить значение 1400: оно покрывает проектный максимум 1218 и оставляет запас 182 переменные. Это расчёт для заданной формы, не универсальная рекомендация. Если каждая строка получит ещё один скрытый параметр, расчёт придётся пересмотреть.

Отдельно задайте допустимое число строк в приложении. Технический запас парсера не означает, что пользователь должен получить возможность отправить любое количество объектов одним сохранением.

Как увеличить max_input_vars в нужном месте

Сначала подтвердите расчёт и согласуйте новый предел с администратором хостинга. Затем измените настройку способом, поддерживаемым конкретной средой: через панель, управляемую конфигурацию PHP или настройки соответствующего обработчика. Не копируйте чужой серверный фрагмент без проверки режима работы сайта.

У max_input_vars режим изменения INI_PERDIR, также известный по константе PHP_INI_PERDIR. Согласно правилам конфигурации PHP, такой режим не допускает изменение через ini_set в пользовательском скрипте. Кроме того, для уже разобранного запроса такая попытка не вернёт отброшенные поля.

Для приведённого учебного расчёта сама строка в подходящем INI-контексте выглядит так:

max_input_vars = 1400

Применение изменения зависит от окружения: может потребоваться обновление процессов или ожидание перечитывания конфигурации. Выполняйте предусмотренную хостингом процедуру, затем заново запросите действующее значение через проблемный веб-обработчик. Сохранённый файл настроек ещё не доказывает, что все экземпляры его применили.

Не устанавливайте огромный предел «на всякий случай». Измерьте максимальный законный запрос, добавьте обоснованный запас и проверьте время обработки и расход памяти. Если форма постоянно растёт, разумнее пересмотреть операцию сохранения, чем бесконечно увеличивать общий предел.

Почему отсутствие поля нельзя считать командой очистки

Самый опасный эффект возникает, когда приложение трактует неполную форму как полный новый набор. Не пришли последние товары — обработчик удаляет их. Нет ключа настройки — записывает пустое значение. Так техническое ограничение превращается в изменение данных, которого пользователь не запрашивал.

Определите договор сохранения: какие поля обязательны, сколько строк ожидается, какие идентификаторы допустимы и как явно выражается удаление. Отсутствие неотмеченного checkbox может быть штатным поведением формы, но отсутствие обязательной части строки должно приводить к отказу. Для частичного обновления заранее задайте, какие пропуски означают «оставить прежнее».

Проверяйте весь набор до применения изменений. Если запрос не соответствует согласованной структуре, отклоните операцию и сохраните прежние данные. Транзакция полезна для атомарной записи, но сама по себе не обнаружит неполный ввод: сначала требуется проверка контракта.

Служебный счётчик или маркер конца формы может помочь диагностике, но не является достаточной защитой. Клиент может передать ошибочное значение, а сам маркер тоже способен исчезнуть. Сопоставляйте структуру с серверными правилами и доступными объектами, не доверяя одному скрытому полю.

Поможет ли переход на JSON или сохранение частями

В описании $_POST указаны формы application/x-www-form-urlencoded и multipart/form-data. Для application/json читают исходное тело через php://input и разбирают JSON отдельно. Само число свойств такого JSON не ограничивается max_input_vars, поскольку это другой путь обработки.

Однако смена формата не освобождает от ограничений тела, памяти, глубины структуры и прикладного числа объектов. Новый обработчик должен проверять тип содержимого, корректность JSON, разрешённые поля, права и полный договор операции. Простое упаковывание всей формы в одну строку лишь переносит ответственность за разбор.

Сохранение небольшими частями может быть удобнее для больших таблиц, но требует явного поведения при промежуточном отказе. Пользователь должен понимать, что уже подтверждено, что осталось несохранённым и можно ли безопасно повторить действие. Нельзя показывать общий успех после первой успешно записанной порции.

Выбирайте изменение архитектуры по характеру задачи. Если законная форма немного превысила разумный предел, достаточно проверенного изменения конфигурации и защиты от неполного ввода. Если один запрос пытается заменить огромный каталог, лучше спроектировать отдельную управляемую операцию с проверяемым результатом.

Как принять исправление и убедиться, что данные сохранились

Повторите тест исходной формы и её максимального допустимого варианта. Используйте синтетические записи, отмеченные значения и контролируемый доступ. Проверка должна завершаться повторным чтением данных, а не только отсутствием предупреждения или зелёным сообщением интерфейса.

  • Количество отправленных переменных соответствует расчёту, включая скрытые и множественные значения.
  • Фактическая настройка проверена во всех веб-обработчиках, обслуживающих запрос.
  • PHP получает ожидаемую структуру без потери обязательных ключей.
  • Валидация различает отсутствие, пустое значение и явную команду удаления.
  • Некорректный или чрезмерный запрос не применяется к базе частично.
  • После сохранения повторное чтение подтверждает начало, середину и конец набора.
  • Тест с превышением допустимого объёма даёт понятный отказ, сохраняя прежние данные.

Зафиксируйте подтверждённую причину, прежнее и новое значение, расчёт запаса и проверенный предел формы. Эти сведения помогут после переноса сайта или обновления окружения: исчезнувшая настройка не должна снова обнаруживаться по испорченным данным пользователей.

Внешнюю HTTP-доступность сайта дополнительно наблюдайте через SiteGuard. Полноту сохранения формы подтверждайте проверкой её данных: доступная страница и успешная запись всех ожидаемых полей требуют отдельных доказательств.