Я столкнулся с ограничением в привязках cURL для PHP. Похоже, что нет простого способа отправить одни и те же несколько значений для одного и того же ключа для постполей. Большинство обходных путей, с которыми я столкнулся для этого, включают создание полей сообщений в кодировке URL вручную tag = foo & tag = bar & tag = baz) вместо использования версии CURLOPT_POSTFIELDS с ассоциативным массивом.
Это кажется довольно обычным делом - нуждаться в поддержке, поэтому я чувствую, что, должно быть, что-то упустил. Действительно ли это единственный способ обрабатывать несколько значений для одного и того же ключа?
Хотя этот обходной путь можно считать работоспособным (если не очень раздражающим), моя основная проблема заключается в том, что мне нужно иметь возможность делать несколько значений для одного и того же ключа, а также поддерживать загрузку файлов. Насколько я могу судить, загрузка файла более или менее требует использования версии CURLOPT_POSTFIELDS с ассоциированным массивом. Так что я чувствую, что застрял.
У меня есть опубликовал об этой проблеме более подробно в списке рассылки cURL PHP в надежде, что у кого-то есть идеи по этому поводу.
Мы очень ценим предложения или подсказки о том, где я могу найти дополнительную информацию по этому поводу!






Если в качестве имени вы используете tag[], а не tag, то PHP сгенерирует массив для вас, другими словами, а не
tag=foo&tag=bar&tag=baz
Тебе нужно
tag[]=foo&tag[]=bar&tag[]=baz
Обратите внимание, что при кодировании urlen-кода для передачи это должно стать
tag%5B%5D=foo&tag%5B%5D=bar&tag%5B%5D=baz
Я думаю, что установленный стандарт для нескольких значений в одном ключе (или одном и том же ключе) состоит в том, чтобы объединить его с разделителем, например, для множественного выбора списков параметров в элементах формы. Я считаю, что этот разделитель - это символ табуляции (\t) или символ вертикальной черты (|).
Если имя ключа заканчивается [] (например, tag[]), PHP автоматически преобразует значения в массив для вашего удобства.
Я знаю, как заставить PHP принимать массив с тегом [], но проблема не в этом. Я отправляю сообщение на сайт без PHP, используя cURL, и мне нужно иметь возможность отправлять несколько значений для одного и того же ключа (тега), и я не мог заставить cURL сделать это, независимо от того, указываю ли я 'tag' или 'tag []' как имя параметра.
Ммм, не уверен, что правильно понял. Вы отправляете сообщение в так называемую чью-то форму (что вполне нормально)? Разве исходная html-форма не допускает нескольких записей в этом элементе формы? Если нет, код-получатель этого не ожидает. Если да, скачайте wirehark и посмотрите, как он закодирован, затем воспроизведите
Я знаю, что они принимают несколько значений для одного и того же тега (tag = a & tag = b) и не используют формат tag [] = a & tag [] = b. Я создал автоматический загрузчик Google Code и считаю, что этот сайт написан на Python. Мне пришлось создать пользовательские поля для создания постинфо multipart / form-data.
Кроме того, мое описание решения находится внизу списка. Хотел бы я принять свой ответ! Или, по крайней мере, поставьте на него несколько +1, чтобы он переместился наверх. Еще раз спасибо за вашу помощь, lImbus.
Имбус и Пол, спасибо за ваш вклад.
Если бы у меня был контроль над формой, в которой я отправляю сообщение, я, вероятно, смог бы найти альтернативное решение этой проблемы. Однако я не контролирую форму. И я почти уверен, что программа, читающая этот пост, не является PHP и не подчиняется стандартам tag [].
Даже если это так, cURL, похоже, тоже не подчиняется синтаксису tag []. В принципе, я пробовал следующее, и ни один из них не работал ...
curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '@/pathtofile', 'tag[]' => array('a', 'b', 'c'));
curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '@/pathtofile', 'tag' => array('a', 'b', 'c'));
И опять же, я не думаю, что передача tag [] сработает в любом случае, поскольку форма, в которую я отправляю сообщение, на самом деле ищет «tag», а не «tag []».
Я действительно начинаю чувствовать, что привязки cURL PHP действительно не поддерживают это. Что меня так удивляет. Кажется, что он может делать буквально все, что угодно, но не может делать что-то такое простое?
В итоге я написал свою собственную функцию для создания настраиваемой строки CURLOPT_POSTFIELDS с multipart / form-data. Вот это боль.
function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
// $postfields is an assoc array.
// Creates a boundary.
// Reads each postfields, detects which are @files, and which values are arrays
// and dumps them into a new array (not an assoc array) so each key can exist
// multiple times.
// Sets content-length, content-type and sets CURLOPT_POSTFIELDS with the
// generated body.
}
Я смог использовать этот метод вот так:
curl_setopt_custom_postfields($ch, array(
'file' => '@/path/to/file',
'tag' => array('a', 'b', 'c'),
));
Я не уверен в стеках CURLOPT_HTTPHEADER, поэтому, поскольку этот метод вызывает его, я удостоверился, что функция позволит пользователю при необходимости указывать дополнительные заголовки.
У меня есть полный код в этом сообщении в блоге.
Я считаю, что это ответ на мой вопрос, но теперь я вижу способ на самом деле «ответить» на мой вопрос. :) Пожалуйста, проголосуйте за это, может быть, когда-нибудь кто-нибудь примет это за меня или что-то в этом роде?
на данный момент вы можете принять свои собственные ответы, см. blog.stackoverflow.com/2009/01/accept-your-own-answers
Удивлен, что никто больше не сталкивался с этим, похоже, что-то, что должно быть частью библиотеки cURL php! Спасибо, что облегчили мне жизнь!
Я застрял в этой адской дыре в течение 3 дней сначала, я не знал, что я делаю неправильно, я понял только после создания формы и проверки запроса от инструментов разработчика в 2018 году, и это все еще не имеет правильного решения, кроме @ BeauSimensen, твой блог не работает, чувак
Веб-сайт не работает, вы можете увидеть функцию здесь yeehuichan.wordpress.com/2011/08/07/…
Я получил его, используя:
curl_setopt($ch, CURLOPT_POSTFIELDS,array('tag[0]'=>'val0','tag[1]'=>'val1'));
тогда $_POST приводит к: $_POST['tag'][0] = 'val0' и $_POST['tag'][1] = 'val1'
Я столкнулся с той же проблемой. Но я смог решить это так.
for($cnt = 0; $cnt < count($siteRows); $cnt++)
{
$curlParams['site_ids['.$cnt.']'] = $siteRows[$cnt]->site_id;
}
Работает и с файлами:
for($cnt = 0; $cnt < count($imageRows); $cnt++)
{
$curlParams['product_images['.$cnt.']'] = '@'.$imageRows[$cnt]->full_path;
}
Использует специфическую интерпретацию php имен полей сообщений, поэтому работает только в том случае, если ваша цель работает с php, но, поскольку это вопрос о написании клиентской части на php, я полагаю, что это вероятно. Мне помогло, +1
Я нашел этот ответ в Интернете и хочу опубликовать его здесь, прежде чем он исчезнет:
function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
$algos = hash_algos();
$hashAlgo = null;
foreach ( array('sha1', 'md5') as $preferred ) {
if ( in_array($preferred, $algos) ) {
$hashAlgo = $preferred;
break;
}
}
if ( $hashAlgo === null ) { list($hashAlgo) = $algos; }
$boundary =
'----------------------------' .
substr(hash($hashAlgo, 'cURL-php-multiple-value-same-key-support' . microtime()), 0, 12);
$body = array();
$crlf = "\r\n";
$fields = array();
foreach ( $postfields as $key => $value ) {
if ( is_array($value) ) {
foreach ( $value as $v ) {
$fields[] = array($key, $v);
}
} else {
$fields[] = array($key, $value);
}
}
foreach ( $fields as $field ) {
list($key, $value) = $field;
if ( strpos($value, '@') === 0 ) {
preg_match('/^@(.*?)$/', $value, $matches);
list($dummy, $filename) = $matches;
$body[] = '--' . $boundary;
$body[] = 'Content-Disposition: form-data; name = "' . $key . '"; filename = "' . basename($filename) . '"';
$body[] = 'Content-Type: application/octet-stream';
$body[] = '';
$body[] = file_get_contents($filename);
} else {
$body[] = '--' . $boundary;
$body[] = 'Content-Disposition: form-data; name = "' . $key . '"';
$body[] = '';
$body[] = $value;
}
}
$body[] = '--' . $boundary . '--';
$body[] = '';
$contentType = 'multipart/form-data; boundary=' . $boundary;
$content = join($crlf, $body);
$contentLength = strlen($content);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Length: ' . $contentLength,
'Expect: 100-continue',
'Content-Type: ' . $contentType,
));
curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
}
И использовать это:
curl_setopt_custom_postfields($ch, array(
'file' => '@a.csv',
'name' => array('James', 'Peter', 'Richard'),
));
См. Ответ @ BeauSimensen ниже.
$client = new \GuzzleHttp\Client();
$client->request('POST', $url, [
'multipart' => [
[ 'name' => 'foo', 'contents' => 'bar' ],
[ 'name' => 'foo', 'contents' => 'baz' ],
]
]);
НЕ ИСПОЛЬЗУЙТЕ GUZZLE:
# at your command line start php interactive
user@group:~:php -a
php > $arr=array('var' => array(1,2,3,4));
php > echo http_build_query($arr);
var%5B0%5D=1&var%5B1%5D=2&var%5B2%5D=3&var%5B3%5D=4
php > echo urldecode(http_build_query($arr));
var[0]=1&var[1]=2&var[2]=3&var[3]=4
Итак, вам нужен http_build_query, где вы передаете хеш-массив ключей и значений; ваша (массив) переменная вводится как ключ со значением массива вместо скалярного значения, такого как 'var' => array(1,2,3,4). Теперь http_build_query может форматировать поля сообщения команды curl:
$fields = array('key1' => 'value1', 'var' => array(1,2,3,4));
$curlPost = \http_build_query($fields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
это 3 строки кода! сколько тысяч строк кода в Guzzle? (*)
До сих пор я использовал curl для:
успешно управлять протоколом Google OAuth
подключаться к API, таким как почтовое ружье
обрабатывать PayPal умные кнопки
это замена миллиона строк какими-то сотнями!
(*): результат http_build_query может быть отформатирован в соответствии с вашими потребностями.
Стоит отметить, что для http_build_query по умолчанию используется http_build_query($fields, null, ini_get('arg_separator.output'), PHP_QUERY_RFC1738).
Я знаю, как создавать массивы в формах и URL-адресах для чтения PHP. Проблема в том, что я отправляю сообщения на сайт, отличный от PHP, который не использует хак []. Даже если это так, cURL не примет 'tag []' => array ('a', 'b', 'c') и создаст запрос, как вы предполагаете. Это проблема.