PHP cURL PUT request where data is array - php

I've got a cURL request that works from the command line, but I can't figure out how to translate it into the PHP implementation of cURL. I believe that my issue is with the formatting of the data that's being sent, but I'm not 100% sure that is the case. This is the curl command I want to send:
# curl -X PUT -d '{"shared_link": {"access":"open"}}' \
-H "Authorization: Bearer ACCESSCODE" https://api.example.com/files/12345
I know that command works! So here's how I'm trying to reproduce it in PHP (where I have a variety of other curl commands working, but none quite like this).
$url = 'https://api.example.com/files/1234';
$header = array('Authorization: Bearer ACCESSCODE');
$data = array('shared_link'=>array('access'=>'open'));
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_CUSTOMREQUEST,'PUT');
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
curl_setopt($ch,CURLOPT_HTTPHEADER,$header);
$output = curl_exec($ch);
curl_close($ch);
return $output;
What I expect to get (and what I do get, when running the command from the command line) is a JSON response from the API server containing all the info about the file, including a change to the 'shared_link' value. What I get instead is a JSON response containing all the info about the file, with the 'shared_link' value unchanged. This is identical to if I were sending a GET request, or a PUT request where the format of the data was valid, but which didn't match any of the file's fields that were possible to update.
So I don't know if the problem with my request is the format of the data (this is what I think is the most likely) or whether I'm correctly configuring curl to do a PUT. I believe I'm doing that the way I've seen it described in a number of other examples, but it still seems a bit strange to me, so I can't be totally sure I'm doing it correctly. In addition, there could be some other area where I'm making a mistake that I don't recognize!
I've tried a number of different ways of formatting the data, none of which worked for me, including:
// This fails with 400 bad request
$data = http_build_query(array('shared_link'=>array('access'=>'open')));
// So does this
$data = urlencode("shared_link[access]=open");
// This fails because the JSON gets converted into objects,
// which POSTFIELDS won't accept
$data = json_decode('{"shared_link": {"access":"open"}}');
I'm running out of things to try. Can someone help me figure out what I'm doing wrong? And if there's any relevant information that I've left out, just let me know and I'll be happy to provide it.
Thanks in advance.
EDIT:
So the answer, it turns out, was so obvious that I overlooked it!
All I had to do was:
$data = '{"shared_link": {"access": "open"}}'
So yeah, question answered. Thanks CBroe!
In my (sort of) defense, the documentation for CURLOPT_POSTFIELDS (http://php.net/manual/en/function.curl-setopt.php) says that the value for that option has to be either an array or a urlencoded string, which is what I was going on. So I was working under that assumption, which was clearly mistaken, since what worked is definitely neither an array nor a urlencoded string.

You can try this
$post_body = http_build_query(
array(
'shared_link[access]' => 'open'
)
);
After that, pass curl opt as curl_setopt($curl, CURLOPT_POSTFIELDS, urldecode($post_body));

Related

How to do a HTTP (POST) Request in PHP without requiring any configuration

I am creating a PHP package that I want anyone to be able to use.
I've not done any PHP dev in a few years and I'm unfamiliar with pear and pecl.
The first part of my question is related to Pecl and Pear:
It seems to me that Pear and pecl are updating my computer, rather than doing anything to my code base, which leads me to the assumption that anything I do with them will also need to be duplicated by anyone wanting to use my package. Is that correct?
The 2nd part of my question is specific, I just want to do a simple HTTP (POST) request, and ideally I'd like to do it without any config required by those who use my package.
These are options I'm considering :
HTTPRequest seems like the perfect option, but it says "Fatal error: Uncaught Error: Class 'HttpRequest' not found" when I try and use it out of the box, and when I follow these instructions for installing it I get, "autoheader: error: AC_CONFIG_HEADERS not found in configure.in
ERROR: `phpize' failed" -- I don't want to debug something crazy like that in order to do a simple HTTP request, nor do I want someone using my package to have to struggle through something like that.
I've used HTTP_Request2 via a pear install and it works for me, but there is nothing added to my codebase at all, so presumably this will break for someone trying to use my package unless they follow the same install steps?
I know that I can use CURL but the syntax for that seems way over the top for such a simple action (I want my code to be really easy to read)
I guess I can use file_get_contents() .. is that the best option?
and perhaps I'll phrase the 2nd part of my question as :
Is there an approach that is considered best practice for (1) doing a HTTP request in PHP, and (2) for creating a package that is able to be easily used by anyone?
This really depends on what you need your request for. While it can be daunting when first learning it, I prefer to use cURL requests most of the time unless all I need to do is query the page with no headers. It becomes pretty readable once you get used to the syntax and the various options in my opinion. When all I need to do is query a page with no headers, I will usually use file_get_contents as this is a lot nicer looking and simpler. I also think most PHP developers can agree with me on this standpoint. I recommend using cURL requests as, when you need to set headers, they're very organized and more popular than messing with file_get_contents.
EDIT
When learning how to do cURL in PHP, the list of options on the documentation page is your friend! http://php.net/manual/en/function.curl-setopt.php
Here's an example of a simple POST request using PHP that will return the response text:
$data = array("arg1" => "val1", "arg2" => true); // POST data included in your query
$ch = curl_init("http://example.com"); // Set url to query
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); // Send via POST
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); // Set POST data
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return response text
curl_setopt($ch, CURLOPT_HEADER, "Content-Type: application/x-www-form-urlencoded"); // send POST data as form data
$response = curl_exec($ch);
curl_close($ch);

Unable to find what kind of CURL Post Request

I've recieved the following data which I need to post to server
and I cant find out how exactly I need to do it in PHP.
I need to send numbers, but I cant see any parameter thier asking me to send it from.
any ideas how I could send this data?
AFAIK you need this:
$postdata = file_get_contents("php://input");
Details:
http://php.net/manual/en/wrappers.php.php
Or maybe the other way:
RAW POST using cURL in PHP
(I am bit lost in your question.)
The main idea there:
curl_setopt($ch, CURLOPT_POSTFIELDS, "numbers\nnumbers\n..." );

CURL call works in terminal, but it won't in PHP

I've been searching through extensively, but couldn't find any topics which are really related to my problem, so I decided to ask a new question...
I'm trying to issue a batch request to the Facebook Graph API using CURL, which works fine in Terminal, but it simply won't in PHP...
Steps to reproduce:
1.) Obtain an access token for yourself from the Graph API Explorer (no permissions needed, just a valid token) Link: http://developers.facebook.com/tools/explorer
2.) Paste it in the following terminal command: (PLEASE be careful, you need to paste it in 4 placeholders in total to work correctly!)
curl \
-F 'access_token=PASTE_ACCESS_TOKEN_HERE' \
-F 'batch=[{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"},{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"},{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"}]' \
https://graph.facebook.com
3.) Execute it in terminal, it should return lots of headers, and your name 4 times in the body section (JSON encoded).
4.) Until now, everything should be working. Here comes the bad part, paste your token again in the 4 placeholders in the following PHP script:
<?php
$url = 'https://graph.facebook.com';
$fields = array(
'access_token' => 'PASTE_ACCESS_TOKEN_HERE',
'batch' =>
'[{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"},{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"},{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"}]'
);
//url-ify the data for the POST
foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
$fields_string = rtrim($fields_string, '&');
//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, count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
//execute post
$result = curl_exec($ch);
//close connection
curl_close($ch);
print_r($result);
?>
6.) Run the code, and you should (at least I do...) get the following error message from Facebook: '{"error":{"message":"Malformed access token ACCESS_TOKEN_HERE....\"}]","type":"OAuthException","code":190}}1'
I guess that Facebook thinks that the JSON closing braces are part of the access token, and because of that it will return this error. I tried removing the escaped double quote and braces from the end of $fields['batch'] , but then it throws another error stating that the batch parameter must be a JSON string (obviously...)
Am I missing something or is this Facebook's fault (if so, I don't understand why the Terminal solution works...)?
Here are the official Facebook Docs BTW:
http://developers.facebook.com/docs/reference/api/batch
Thanks in advance!
EDIT:
I found a (temporary) workaround, this will work (of course, since it uses a shell script):
<?php
$result = shell_exec('curl \
-F \'access_token=PASTE_ACCESS_TOKEN_HERE\' \
-F \'batch=[{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"},{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"},{"method":"GET","relative_url":"fql?q=SELECT+name+FROM+user+WHERE+uid%3Dme%28%29&access_token=PASTE_ACCESS_TOKEN_HERE"}]\' \
https://graph.facebook.com');
print_r(json_decode($result),true);
?>
Does somebody know why this is a worse solution (I suppose it is) than using PHP-cURL?
I read somewhere that you shouldn't use shell scripts in PHP, I don't know why though...
Thanks!
I ran the first half of your code just to see what the contents of $fields_string would be before starting any of the curl commands. For some reason rtrim is not removing the last ampersand in the string for me. Perhaps use this instead to remove the last character:
$fields_string = substr($fields_string, 0, -1);

Parsing PHP CURL data

I have been messing with CURL for the past day and I cannot seem to figure out how to parse out the return data. I know I could write a REGEX to extract data from the response but it seems like there is some function I am probably missing. Here is what I would like to try and do.
I want to make POST to a different domain and get back 3 things
1. the response headers
2. the response data
3. a session cookie
Is there a way I can get those 3 things back separately? right now I just get back a plain text response with the response header and the response data. I would like to be able to do something like
$Response = curl_exec($Curl_Connection);
$ResponseData = $Response['Data'];
$ResponseHeader = $Response['Header'];
ResponseCookie = $Response['Cookie'];
Does curl provide anything like this?
As I answered:
no post data returned when requesting headers CURLOPT_HEADER. PHP CURL
Add:
curl_setopt($Curl_Connection, CURLOPT_HEADER, TRUE);
$Response = curl_exec($Curl_Connection);
$curlHeaderSize=curl_getinfo($ch,CURLINFO_HEADER_SIZE);
$ResponseData = mb_substr($result, $curlHeaderSize);
$ResponseHeader = mb_substr($result, 0, $curlHeaderSize);
preg_match_all('|Set-Cookie: (.*);|U', $ResponseHeader, $content);
$ResponseCookie = implode(';', $content[1]);
According to the curl docs:
/* TRUE to include the header in the output. */
curl_setopt($Curl_Connection, CURLOPT_HEADER, TRUE);
So the header is added to the output of curl_exec command. I've been spitting this out and I don't see any way to retrieve the header separately from the response body. It gets worse when you start reading compressed output (zipped, inflated)
On top of that, it's a one big string, not an array, so in case you you want the header in a format like for example curl_getinfo brings back an array.
The easiest is to do this:
$backend_output = curl_exec($ch);
list( $backend_response_headers, $backend_response_body)
= explode("\r\n\r\n", $backend_output, 2);
That will split those 2 up, but you end up with a string for the response header, not an array which would be so much more helpful. Now making an arrray from that isn't so evident, even with a regex as you can't split on for example something simple like /(\w)\s:(\w)/ as ':' can occur in certain fields. It would be very cool if curl would offer the headers seperately but so far as I go through the docs, it doesn't seem to be there.
Concerning your session cookies, I believe you need to use CURLOPT_COOKIESESSION = true option for that, but I have less experience on cookies as I hardly ever have the professional need to use them. Good luck
update: The headers you can parse with http://php.net/manual/en/function.http-parse-headers.php or a custom function from the user comment section if you lack pecl.

curl sending GET instead of POST

Actually, it's gotten so messy that I'm not even sure curl is the culprit. So, here's the php:
$creds = array(
'pw' => "xxxx",
'login' => "user"
);
$login_url = "https://www.example.net/login-form"; //action value in real form.
$loginpage = curl_init();
curl_setopt($loginpage, CURLOPT_HEADER, 1);
curl_setopt($loginpage, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($loginpage, CURLOPT_URL, $login_url);
curl_setopt($loginpage, CURLOPT_POST, 1);
curl_setopt($loginpage, CURLOPT_POSTFIELDS, $creds);
$response = curl_exec($loginpage);
echo $response;
I get the headers (which match the headers of a normal, successful request), followed by the login page (I'm guessing curl captured this due to a redirect) which has an error to the effect of "Bad contact type".
I thought the problem was that the request had the host set to the requesting server, not the remote server, but then I noticed (in Firebug), that the request is sent as GET, not POST.
If I copy the login site's form, strip it down to just the form elements with values, and put the full URL for the action, it works just great. So I would think this isn't a security issue where the login request has to originate on the same server, etc. (I even get rid of the empty hidden values and all of the JS which set some of the other cookies).
Then again, I get confused pretty quickly.
Any ideas why it's showing up as GET, or why it's not working, for that matter?
When troubleshooting the entire class of PHP-cURL-related problems, you simply have to turn on CURLOPT_VERBOSE and give CURLOPT_STDERR a file handle.
tail -f your file, compare the headers and response to the ones you see in Firebug, and the problem should become clear.
The request is made from the server, and will not show up in Firebug. (You probably confused it with another request by your browser). Use wireshark to find out what really happens. You are not setting CURLOPT_FOLLOWLOCATION; redirects should not be followed.
Summarizing: Guess less, post more. Link to a pcap dump, and we will be able to tell exactly what you're doing wrong; or post the exact output of the php script, and we might.
The shown code does a multipart formpost (since you pass a hash array to the POSTFIELDS option), which probably is not what the target server expects.
try throwing in a print_r(curl_getinfo($loginpage)) at the end, see what the header data it sent back as.
also, if your trying to fake that your logging in from their site, your going to want to make sure your sending the correct referrer with your post, so that they "think" you were on the website when you sent it.

Categories