tags:

views:

1328

answers:

6

I’ve run into a limitation in the cURL bindings for PHP. It appears there is no easy way to send the same multiple values for the same key for postfields. Most of the workarounds I have come across for this have involved creating the URL encoded post fields by hand tag=foo&tag=bar&tag=baz) instead of using the associative array version of CURLOPT_POSTFIELDS.

It seems like a pretty common thing to need to support so I feel like I must have missed something. Is this really the only way to handle multiple values for the same key?

While this workaround might be considered workable (if not really annoying), my main problem is that I need to be able to do multiple values for the same key and also support file upload. As far as I can tell, file upload more or less requires to use the associate arravy version of CURLOPT_POSTFIELDS. So I feel like I am stuck.

I have posted about this problem in more detail on the cURL PHP mailing list in the hopes that someone there has some ideas about this.

Suggestions or hints on where I can look for more information on this are greatly appreciated!

+2  A: 

If you use tag[] rather than tag for the name, PHP will generate an array for you, in other words, rather than

tag=foo&tag=bar&tag=baz

You need

tag[]=foo&tag[]=bar&tag[]=baz

Note that when urlencoded for transmission this should become

tag%5B%5D=foo&tag%5B%5D=bar&tag%5B%5D=baz
Paul Dixon
I know how to create arrays in forms and URLs for PHP to read. The problem is that I'm posting to a non-PHP site that does not use the [] hack. Even if it did, cURL will not accept 'tag[]' => array('a', 'b', 'c') and create the request like you are suggesting it will. This is the problem.
Beau Simensen
+1  A: 

I think the established standard for multiple values in one key (or the same key) is to have it concatenated with a delimiter, such as for multiple selections of option lists in form elements. I believe this delimiter is the tab character (\t) or the pipe symbol (|).

If the keyname is terminated with [] (like tag[]), PHP will automatically convert the values into an array for your convenience.

lImbus
I know how to get PHP to accept an array with tag[], but that is not the problem. I am posting to a non-PHP site using cURL and need to be able to send multiple values for the same key (tag), and I could not get cURL to do that, whether I specify 'tag' or 'tag[]' as the param name.
Beau Simensen
Mhmm, not sure I get this right. You're posting to so-to-say somebody elses form (which is perfectly fine) ?Doesn't the original html-form allow for multiple entries in that form-element ? If no, the receiving code will not expecting it. If yes, get wireshark and look how it is encoded, then replay
lImbus
Beau Simensen
Also, my description on th esolution is at the bottom of the list. I wish I could accept my own answer! Or at least get some +1's on it so that it gets moved to the top. Thanks again for your help, lImbus.
Beau Simensen
+1  A: 

lImbus and paul, thank you for your input.

If I had control over the form I am posting to, I could probably find an alternate solution to this problem. However, I do not have any control over the form. And I am almost positive that the software reading the post is not PHP and does not obey the tag[] standards.

Even if it did, cURL does not seem to obey the tag[] syntax either. Basically, I tried the following and neither worked...

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

And again, I don't think that passing tag[] would work anyway as the form I am posting to is actually looking for 'tag' and not 'tag[]'.

I am really starting to get the feeling that the cURL PHP bindings really have no support for this. Which seems so surprising to me. It seems like it can do quite literally anything else, yet it is unable to do something simple like this?

Beau Simensen
+3  A: 

I ended up writing my own function to build a custom CURLOPT_POSTFIELDS string with multipart/form-data. What a pain.

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

I was able to use this method like this:

curl_setopt_custom_postfields($ch, array(
    'file' => '@/path/to/file',
    'tag' => array('a', 'b', 'c'),
));

I am not certain of CURLOPT_HTTPHEADER stacks, so since this method calls it, I made certain that the function would allow for the user to specify additonal headers if needed.

I have the full code available in this blog post.

Beau Simensen
I believe this answers my question, but I see now way to actually "answer" my question. :) Please upvote this so maybe someday someone can accept it for me or something?
Beau Simensen
you can, as of now, accept your own answers, see http://blog.stackoverflow.com/2009/01/accept-your-own-answers/
lImbus
A: 

I got it working using : curl_setopt($ch, CURLOPT_POSTFIELDS,array('tag[0]'=>'val0','tag[1]'=>'val1'));

then $_POST results in: $_POST['tag'][0] = 'val0' and $_POST['tag'][1] = 'val1'

Sandro Pons
+1  A: 

I ran into the same issue. But I was able to solve it this way.

for($cnt = 0; $cnt < count($siteRows); $cnt++)
{
    $curlParams['site_ids['.$cnt.']'] = $siteRows[$cnt]->site_id; 
}

Works for files too:

for($cnt = 0; $cnt < count($imageRows); $cnt++)
{
    $curlParams['product_images['.$cnt.']'] = '@'.$imageRows[$cnt]->full_path;
}
rawberg