Отправка многомерного массива с помощью PHP и CURL
Я испытываю проблемы с отправкой данных формы через CURL на PHP-скрипт, расположенный на другом хосте.
Я получаю ошибку Преобразование массива в строку
.
Вот print_r
массива, который я отправляю:
Array
(
[name] => Array
(
[0] => Jason
[1] => Mary
[2] => Lucy
)
[id] => 12
[status] => local
[file] => @/test.txt
)
Ошибка возникает на этой строке:
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $post);
Третий аргумент обязательно должен быть массивом, так как мне нужно, чтобы заголовок Content-Type
был установлен на multipart/form-data
, поскольку я отправляю файл через этот же массив. Поэтому я не могу преобразовать массив в строку запроса или использовать http_build_query()
.
Также у меня нет доступа к коду на принимающем хосте, поэтому я не могу сериализовать и десериализовать массив.
Я предполагаю, что значение ключа name, будучи массивом, является причиной этой ошибки. Я также предполагаю, что CURLOPT_POSTFIELDS
не поддерживает многомерные массивы. Есть ли какие-либо обходные пути для этой проблемы, или мне не повезло?
Заранее спасибо!
5 ответ(ов)
Функция http_build_query_for_curl
предназначена для преобразования многомерного массива или объекта в ассоциативный массив, который можно использовать для отправки данных через cURL в формате application/x-www-form-urlencoded
. Вот подробное объяснение кода:
function http_build_query_for_curl( $arrays, &$new = array(), $prefix = null ) {
// Если входные данные - объект, преобразуем его в массив
if ( is_object( $arrays ) ) {
$arrays = get_object_vars( $arrays );
}
// Проходим по каждому элементу массива
foreach ( $arrays AS $key => $value ) {
// Формируем ключ с учетом префикса, если он задан
$k = isset( $prefix ) ? $prefix . '[' . $key . ']' : $key;
// Если значение является массивом или объектом, рекурсивно вызываем функцию
if ( is_array( $value ) OR is_object( $value ) ) {
http_build_query_for_curl( $value, $new, $k );
} else {
// Если значение - скаляр, добавляем его в результирующий массив
$new[$k] = $value;
}
}
}
// Пример данных
$arrays = array(
'name' => array(
'first' => array(
'Natali', 'Yura'
)
)
);
// Инициализируем пустой массив для результата
$post = array();
// вызываем функцию
http_build_query_for_curl( $arrays, $post );
// Выводим результирующий массив
print_r($post);
Объяснение работы функции:
Преобразование объекта в массив: В начале функции проверяется, является ли
$arrays
объектом. Если это так, он преобразуется в массив с помощьюget_object_vars()
.Итерация по массиву: Цикл
foreach
проходит по каждому элементу массива или объекта.Формирование ключей: Используется префикс для формирования ключей, что позволяет поддерживать вложенную структуру данных. Например, если у вас есть массив с вложенными массивами, вы получите ключи в формате
name[first][0]
.Рекурсивный вызов: Если значение является массивом или объектом, функция будет вызываться рекурсивно, что позволяет обработать любое количество уровней вложенности.
Заполнение результирующего массива: Если значение является скаляром (например, строка или число), оно добавляется в результирующий массив
$new
.
Результат
При выполнении кода с указанным массивом, вы получите структурированный массив $post
, который можно использовать для передачи данных через cURL.
Концепция массива на самом деле не существует в контексте HTTP-запросов. PHP (предположительно, как и другие серверные языки) имеет встроенную логику, которая позволяет принимать данные запроса, которые выглядят как массив (для него), и преобразует их в массив, заполняя такие суперглобальные массивы, как $_GET
, $_POST
и так далее.
Например, когда вы отправляете массив из формы методом POST, элементы формы часто выглядят примерно так:
<form ...>
<input name="my_array[0]">
<input name="my_array[1]">
<input name="my_array[2]">
</form>
или даже так:
<form ...>
<input name="my_array[]">
<input name="my_array[]">
<input name="my_array[]">
</form>
Хотя PHP знает, что делать с этими данными, когда он их получает (т.е. строить массив), для HTML и HTTP это всего лишь три независимых поля ввода, которые случайно имеют похожие (или даже одинаковые, хотя это не является технически корректным HTML) имена.
Чтобы сделать обратное в вашем запросе cURL, вам нужно разложить ваш массив на строковые представления его ключей. Например, для вашего массива name
вы можете сделать что-то вроде этого:
foreach ($post['name'] as $id => $name) {
$post['name[' . $id . ']'] = $name;
}
unset($post['name']);
В результате ваш массив $post
будет выглядеть так:
Array
(
[name[0]] => Jason
[name[1]] => Mary
[name[2]] => Lucy
[id] => 12
[status] => local
[file] => @/test.txt
)
Таким образом, каждый ключ в массиве, который вы отправляете, будет скалярным значением, что и ожидает cURL, а массив будет представлен так, как это необходимо для HTTP.
Вам нужно будет вручную сформировать строку POST, а не передавать целый массив. Затем вы можете переопределить автоматически выбираемый заголовок контента cURL с помощью:
curl_setopt($c, CURLOPT_HTTPHEADER, array("Content-type: multipart/form-data"));
Сериализация в JSON была бы проще, но, как вы сами говорите, у вас нет контроля над принимающей стороной, поэтому придется немного повозиться.
Самое простое решение — это сделать следующее:
$array = urldecode(http_build_query($array));
Ниже приведен пример кода, где это используется на практике:
https://gist.github.com/gayanhewa/142c48162f72e68a4a23
Когда у вас есть вложенный раздел $params
в приведенном выше примере, он будет правильно обработан и подготовлен для отправки через curl
.
Опция cURL CURLOPT_POSTFIELDS
принимает в качестве аргумента либо строку, либо простой массив, но не поддерживает вложенные массивы. Если вы попытаетесь передать вложенный массив, то получите ошибку Array to string conversion
.
Однако функция http_build_query()
может обрабатывать вложенные массивы, поэтому вы можете использовать её для преобразования массива $_POST
в строку и затем отправить эту строку. То есть, если у вас есть следующий код:
curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
замените его на:
curl_setopt($ch, CURLOPT_POSTFIELDS, urldecode(http_build_query($_POST)));
Таким образом, ваша проблема будет решена, и данные будут корректно отправлены.
Как отправить POST-запрос с помощью PHP?
Как добавить элементы в пустой массив в PHP?
Вставка нового элемента в массив в любом месте в PHP
Сортировка массива объектов по одному свойству
Что такое заголовок postman-token в сгенерированном коде Postman?