Разноска многомерного массива с помощью PHP и CURL

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

Я Array to string conversion

это 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().

также у меня нет доступа к коду на принимающем хосте, поэтому я не могу сериализовать и отменить сериализацию массива.

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

спасибо заранее!

8 ответов


вам нужно будет построить строку POST вручную, а не передавать весь массив. Затем вы можете переопределить автоматически выбранный заголовок содержимого curl с помощью:

curl_setopt($c, CURLOPT_HTTPHEADER, array("Content-type: multipart/form-data"));

сериализация/json-ifying было бы проще, но, как вы говорите, у вас нет контроля над принимающей стороной, поэтому у вас есть немного дополнительной работы.


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'
        )
    )
);


http_build_query_for_curl( $arrays, $post );

print_r($post);

понятие массива на самом деле не существует, когда дело доходит до HTTP-запросов. PHP (и, вероятно, другие языки на стороне сервера) имеет логику, запеченную в том, что может принимать данные запроса, которые выглядит как массив (к нему) и объединяет его как массив при заполнении $_GET, $_POST etc.

например, когда вы публикуете массив из формы, элементы формы часто выглядят примерно так:

<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.


самое простое решение-сделать a:

$array = urldecode(http_build_query($array));

Ниже приведен пример кода, где это используется в реальной жизни :

https://gist.github.com/gayanhewa/142c48162f72e68a4a23

когда у вас есть вложенный раздел $params в приведенном выше gist, он проанализирует его соответствующим образом и подготовит его для публикации через curl.


сначала я хотел бы поблагодарить Даниил Vandersluis за его глубокий ответ. Основываясь на его вводе, я придумал это, чтобы исправить проблему из исходного вопроса:

<?php

function curl_postfields_flatten($data, $prefix = '') {
  if (!is_array($data)) {
    return $data; // in case someone sends an url-encoded string by mistake
  }

  $output = array();
  foreach($data as $key => $value) {
    $final_key = $prefix ? "{$prefix}[{$key}]" : $key;
    if (is_array($value)) {
      // @todo: handle name collision here if needed
      $output += curl_postfields_flatten($value, $final_key);
    }
    else {
      $output[$final_key] = $value;
    }
  }
  return $output;
}

использование должно выглядеть так:

curl_setopt($this->ch, CURLOPT_POSTFIELDS, curl_postfields_flatten($post));

эта функция преобразует массивы следующим образом:

array(
  'a' => 'a',
  'b' => array(
    'c' => array(
      'd' => 'd',
      'e' => array(
        'f' => 'f',
      ),
    ),
  ),
);

в:

array(
  'a' => 'a',
  'b[c][d]' => 'd',
  'b[c][e][f]' => 'f',
)

он не обрабатывает случаи со смешанным форматом, когда есть ключевое столкновение, такое как это:

array(
 'b[c]' => '1',
 'b' => array(
   'c' => '2', 
  ),
);

выход будет содержать только первое значение для этого ключа

array(
 'b[c]' => '1'
)

Я думаю, вам нужно будет передать параметры в виде строки:

curl_setopt($this->ch, CURLOPT_POSTFIELDS, 'name[]=Jason&name[]=Mary&name[]=Lucy...');

затем вы сможете установить заголовок вручную с помощью CURLOPT_HTTPHEADER.


опция 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 = "ac=on&p=1&pr[]=0&pr[]=1&a[]=3&a[]=4&pl=on&sp[]=3&ct[]=3&s=1&o=0&pp=3&sortBy=date";
parse_str($post,$fields); 

$url = 'http://example.com/';


//open connection
$ch = curl_init();

//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);

//execute post
$result = curl_exec($ch);

//close connection
curl_close($ch);