PHP 581 ~ 6 мин.

PHP 8.4: Новая функция request_parse_body

PHP 8.4: Новая функция request_parse_body

Как вы знаете PHP автоматически анализирует HTTP-запросы POST для заполнения глобальных переменных $_POST и $_FILES. Однако другие HTTP-запросы с такими методами, как PUT или PATCH уже не анализируются автоматически, и работа с обработкой данных с данными типами запроса ложится на плечи разработчика/приложения.

RFC1867 определяет тип контента multipart/form-data. Этот тип контента используется в основном для отправки HTTP-форм, содержащих файлы. PHP изначально поддерживает анализ этого типа контента, но только для запросов POST. В частности, если запрос имеет метод POST и тип содержимого multipart/form-data, тело запроса немедленно используется перед запуском PHP-скрипта и заполняется в суперглобальные переменные $_POST и $_FILES. Эта функция запускается автоматически и не предоставляется непосредственно пользователю.

Учитывая популярность REST API, которые все чаще используют HTTP методы, отличные от POST - такие как PUT, DELETE и PATCH, где использование multipart/form-data полностью допустимо, но не обрабатывается PHP автоматически. Это требует ручного анализа тела запроса нетривиального формата. Обработка больших объемов данных в пользовательской среде также может быть не оптимальной с точки зрения производительности.

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

UPD: Вот отличный пример реализации от Кирилла Несмеянова, который при свободном времени можно оформить в пакет)

Если вкратце, PHP предоставляет обёртку потока php://input, содержащую данные запроса. Для POST запросов с enctype="multipart/form-data" эта обёртка потока остается пустой, поскольку она автоматически анализируется и используется для заполнения переменных $_POST и $_FILES. Автоматической обработкой $_POST и $_FILES можно управлять с помощью директивы enable_post_data_reading=Off в INI.

Если значение enable_post_data_reading установлено как Off, или если метод HTTP-запроса имеет значение, отличное от POST, php://input обёртку потока можно прочитать в PHP-коде приложения для анализа данных HTTP-запроса:

curl --request PUT --location 'https://somehost.com/somepath' --form 'foo="bar"'

В PHP-приложении данные формы приведенного выше вызова Curl можно прочитать из потока php://input:

echo file_get_contents('php://input');
----------------------------690112416382325217175117
Content-Disposition: form-data; name="foo"
bar
----------------------------690112416382325217175117--

Итак, в PHP 8.4 была добавлена ​​новая функция request_parse_body, которая предоставляет встроенную в PHP функциональность анализа запросов для других методов HTTP-запросов, отличных от POST:

/**  
 * Parse and consume php://input and return the values for $_POST
 * and $_FILES variables.
 * 
 * @param array<string, int|string>|null $options Overrides for INI values
 * @return array<int, array> Array with key 0 being the post data  
 *   (similar to $_POST), and key 1 being the files ($_FILES).  
 */
function request_parse_body(?array $options = null): array {}
// Например, это запрос PUT 
var_dump($_POST);   // [] 
var_dump($_FILES);  // []
 
[$_POST, $_FILES]  = request_parse_body();
 
var_dump($_POST);   // уже есть данные [...] 
var_dump($_FILES);  // уже есть данные [...]

При вызове функция request_parse_body считывает все содержимое, которое доступно в потоке php://input и создает значения, которые можно использовать в переменных $_POST и $_FILES.

Возвращаемым значением будет массив, c двумя ключами, 0 и 1, содержащий проанализированные значения, которые можно использовать как $_POST(индекс ключа массива 0) и $_FILES(индекс 1). Оба ключа массива будут присутствовать всегда - даже если нет данных запроса и/или файлов.

Можно заполнить значения $_POST и $_FILES непосредственно из возвращаемых значений:

[$_POST, $_FILES] = request_parse_body();

Обратите внимание, что синтаксический анализ запроса по-прежнему имеет ограничения, установленные директивами INI. Например, если директива post_max_size (которая ограничивает максимальный размер запросов) установлена ​​в 2 мегабайта, попытка вызвать функцию request_parse_body с запросом большего размера по-прежнему будет приводить к ошибке.

Параметры анализа запроса можно переопределить на меньшие или большие значения, передав параметр $options.

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

Переопределение параметров анализа запроса

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

Например, чтобы проанализировать запрос с большим или меньшим пределом директивы post_max_size INI, можно вызвать функцию request_parse_body с нужным значением:

request_parse_body(['post_max_size' => 1024]);

Массив $options принимает только следующие переопределения:

INI/$option ключ

Описание

post_max_size

Максимальный размер данных POST, которые принимает PHP. Его значение может быть 0 - чтобы отключить ограничение.

max_input_vars

Сколько входных переменных GET/POST/COOKIE можно принять

max_multipart_body_parts

Сколько составных частей тела (комбинированная входная переменная и загрузка файлов) может быть принято.

max_file_uploads

Максимальное количество файлов, которые можно загрузить по одному запросу.

upload_max_filesize

Максимально допустимый размер загружаемых файлов.

Предоставление неверных ключей или значений приведет к возникновению ошибки ValueError.

Класс исключения RequestParseBodyException

RequestParseBodyException - это новый класс Exception, объявленный в глобальном пространстве имен, расширяющий класс Exception.

class RequestParseBodyException extends Exception {}

RequestParseBodyException исключения выдаются, если функция request_parse_body не может проанализировать данные запроса. Это может произойти, если предоставленные данные запроса недействительны, не отправляют заголовок Content-Type или если данные запроса выходят за пределы ограничения, установленного директивами INI и необязательным параметром $options.

Ниже приведен список исключений RequestParseBodyException и их причин:

  • RequestParseBodyException: Request does not provide a content type

    Запрос не содержит заголовка Content-Type.

  • RequestParseBodyException: Content-Type ARBITRARY_TYPE is not supported

    Заголовок Content-Type содержит значение, отличное от multipart/form-data или application/x-www-form-urlencoded.

  • RequestParseBodyException: Missing boundary in multipart/form-data POST data

    Запрос не содержит границы. Убедитесь, что запрос правильно отформатирован как multipart/form-data или application/x-www-form-urlencoded.

  • RequestParseBodyException: POST Content-Length of ... bytes exceeds the limit of ... bytes

    Длина содержимого превысила значение post_max_size, установленное в $options или INI.

  • RequestParseBodyException: Multipart body parts limit exceeded ... To increase the limit change max_multipart_body_parts in php.ini

    Части данных запроса превысили значение max_multipart_body_parts, установленное в параметре $options или INI.

  • RequestParseBodyException: Input variables exceeded ... To increase the limit change max_input_vars in php.ini.

    Части данных запроса превысили значение max_input_vars, установленное в $options или INI.

  • RequestParseBodyException: Maximum number of allowable file uploads has been exceeded

    Количество загружаемых файлов превышает значение max_file_uploads, установленное в $options или INI.

Предостережения request_parse_body

Функция request_parse_body предназначена для вызова только один раз за запрос и разрушительно воздействует на php://input. При последующих вызовах функция вернет массив с пустыми данными, а поток php://input будет пуст после первого вызова request_parse_body().

Не идемпотентное поведение request_parse_body

Обратите внимание, что вызов функции request_parse_body потенциально может привести к деструктивному поведению, включая поглощение потока php://input и очистки его содержимого.

  • Вызов функции request_parse_body поглощает поток php://input, после этого поток php://input будет пуст.

  • Если php://input поток был прочитан ранее (например file_get_contents('php://input'), функция request_parse_body возвратит пустой результат ([0 => [], 1 => []]).

  • функция request_parse_body не изменяет напрямую глобальные переменные $_POST и $_FILES; при желании приложение PHP может перезаписать эти переменные.

  • Только первый вызов функции request_parse_body возвращает проанализированные данные. Последующие вызовы возвращают пустой результат ([0 => [], 1 => []]).

  • Даже если функция выдает исключение, php://input все равно используется и очищается, а последующие request_parse_body вызовы возвращают пустой результат.

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

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

Что думаешь?

Категории
  • PHP 67
  • Заметки 17
  • Безопасность 4
  • Флуд 2
  • Nginx 2
  • ИТ новости 2
  • Видео 1
  • Docker 1
  • Roadmap 1
  • Архитектура 0

Хочешь поддержать сайт?

Делаем из мухи слона

sergeymukhin.com

персональный блог о веб-разработке от Сергея Мухина. Блог был основан в 2018 году, и собирался уделять основное внимание последним тенденциям, учебным пособиям, а также советам и рекомендациям, позволяющим начинающим девелоперам встать быстрее на правильную дорогу веб разработки, но что-то пошло не так 😃

Релизы PHP 8.4

Дата Релиз
8 Июня 2024 Альфа 1
20 Июня 2024 Альфа 2
04 Июля 2024 Альфа 3
16 Июля 2024 Feature freeze
18 Июля 2024 Бета 1
01 Августа 2024 Бета 2
15 Августа 2024 Бета 3
29 Августа 2024 RC 1
12 Сентября 2024 RC 2
26 Сентября 2024 RC 3
10 Октября 2024 RC 4
24 Октября 2024 RC 5
07 Ноября 2024 RC 6
21 Ноября 2024 GA

Что нового?