PHP Curl results into invalid json - php
I want to send json data via PUT to a REST service using the following php code:
$data = '{"api_key":"my-api-key","post":{"exception":["2015-04-10T11:09:51+00:00 ERR (3):\\nexception 'Exception' with message 'a simple exception' in \/private\/var\/www\/index.php:1\\nStack trace:\\n#0 {main}"],"access":["::1 - - [10\/Apr\/2015:13:08:17 +0200] \"GET \/index.php HTTP\/1.1\" 200 19039"]}}';
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data))
);
$response = curl_exec($curl);
As you can see I send valid json (validated by http://jsonlint.com/). On the side of the service I get the following json:
{"api_key":"my-api-key","post":{"exception":["2015-04-10T11:09:51+00:00 ERR (3):\\\\nexception \'Exception\' with message \'a simple exception\' in \\/private\\/var\\/www\\/index.php:1\\\\nStack trace:\\\\n#0 {main}"],"access":["::1 - - [10\\/Apr\\/2015:13:08:17 +0200] \\"GET \\/index.php HTTP\\/1.1\\" 200 19039"]}}
Validating this says I got a parse error. And this seems correct as I can't understand why further escaping is done like \\\\n. What am I doing wrong here?
I doubt that this is a valid use of a PUT and should be a POST but that is just a technicality. If you can, make it a POST. The PUT is likely the root of the issue.
The CURLOPT_PUT uses CURLOPT_INFILE and CURLOPT_INFILESIZE
The URLOPT_CUSTOMREQUEST should work fine but it appears you have some RFC 3986 escaping going on for some unexplainable reason.
You may want to use rawurldecode ( ) on the Service Side.
This will give you (appears correct but I'm not the guy to verify):
{"api_key":"my-api-key","post":{"exception":["2015-04-10T11:09:51+00:00 ERR (3):\\nexception 'Exception' with message 'a simple exception' in \/private\/var\/www\/index.php:1\\nStack trace:\\n#0 {main}"],"access":["::1 - - [10\/Apr\/2015:13:08:17 +0200] \"GET \/index.php HTTP\/1.1\" 200 19039"]}}
Disclaimer: I have never used curl to POST Content-Type: application/json data.
You need to look at your Request Header and maybe Response. It would be interesting to see if you get the proper PUT Response.
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
Check your Request Header Content-Type
To get the headers:
The response Header will be in the returned transfer.
The Request Header will be in curl_getinfo()
The curl info will also include the HTTP Response Status. Which for a PUT should be 201 or 301 but not important.
$data = curl_exec($ch);
$skip = intval(curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$responseHeader = substr($data,0,$skip);
$data= substr($data,$skip);
$info = var_export(curl_getinfo($ch),true);
echo $responseHeader . $info . $data;
If you still want to send the json without curl escaping it I have some thoughts on that. but there is probably a post on that subject here somewhere.
Related
php://input empty when sending long JSON via raw POST
I am sending raw JSON POST data with cURL via PHP, and the server receives it correctly when the JSON is not too long. However, it seems to receive an empty body when it's bigger. This is how I get the body from the server: $dataReceived = file_get_contents("php://input"); When I log $dataReceived on the server side, the expected content is in the log when not big, e.g. this: {"products":[],"allProductIds":["898","920","937","947","979","1007","1044","1064","1124","1146","1162","1178","1194","1215","1239","1291","1346","1395","1444","1629","1673","1695","1757","1868","1929","1992","2018","2050","2096","2112","2136","2178","2225","2280","2303","2318","2340","2394","2447","2459","2476","2486","2510","2520","2558","2591","2626","2655","2689","2727","2975","3175","3224","3283","3311","3333","3342","3353","3376","3389","3400","3419","3469","3506","3535","3610","3722","3765","3782","3802","4002","4020","4039","4060","4094","4130","4171","4213","4246","4281","4317","4348","4426","4473","4502","4545","4558","4563","4663","4688","4713","4733","4757","4875","4924","4984","5028","5057","5088","5102","5117","5145","5174","5197","5216","5236","5255","5275","5310","5336","5369","5422","5433","5464","5477","5505","5516","5541","5563","5587","5616","5627","5673","5703","5744","5773","5822","5839","5903","5999","6075","6196","6326","6483","6794","8360","8429","8599","8724","8798","8953","9150","9198","9247","9295","9347","9415","9463","9636","9991","10105","10219","10337","10451","10566","10681","10796","10912","11134","11244","11354","11465","11576","11687","11798","11874","11924","11973","12025","12074","12125","12175","12640","12699","12838","12844","12848","12852","12862","12866","12870","12874","12880","12884","12888","12894","12935","12943","12947","12952","12958","12962","12966","12970","12974","12979","12988","12992","12997","13001","13010","13012","13016","13020","13026","13030","13034","13039","13049","13059","13093","13128","13144","13148","13154","13158","13162","13172","13182","13217","13223","13368","2851","3828","4804"],"brandlyProductIds":["229082","226046","223246","216800","216750","215456","213613","213157","143436","143406","143362","143312","134066","134022","221020","242298","144021","143921","242348","333387","333424","333443","333517","333624","333681","334021","334043","334086","334135","334159","334184","334220","334261","334318","334351","334376","334398","334448","334505","334530","334552","334581","334606","334639","334672","334701","334734","334763","334792","334825","334962","335099","335325","335382","335439","335464","335482","335491","335500","335521","335532","335553","335575","335680","335770","335795","335877","335991","336032","336049","336066","336088","336276","336293","336310","336329","336370","336411","336452","336493","336530","336561","336598","336631","336705","336751","336782","336819","336850","336859","336977","337002","337033","337058","337083","337133","337207","337256","337366","337403","337428","337456","337473","337490","337515","337540","337565","337582","337599","337616","337637","337682","337710","337738","337791","337800","337831","337881","337906","337928","337953","337981","338006","338031","338056","338099","338135","338175","338201","338244","338269","338340","338422","187730","338815","338968","339121","339427","340235","340308","340461","340614","340687","340840","340987","341060","341133","341206","341279","341352","341425","341675","341981","342072","342254","342350","342441","342532","342714","342805","342896","343042","343115","343188","343261","343407","343480","343553","343626","343690","343754","343818","343882","343946","344010","225836","225770","344210","344214","344216","344218","303935","303610","303601","344223","344234","344236","344238","344241","204360","344254","344256","344262","344264","344268","344270","344272","344274","344277","344279","344281","344283","344285","344287","344288","344290","344292","344295","344299","344301","344303","344308","344313","344330","344333","344335","344337","344340","344342","344344","344349","344354","344371","150836","344434"]} But i get an empty $dataReceived for example when the sent JSON is the one in this pastebin https://pastebin.com/p5ZLrxig This is the code i have to send the request, with verbose log protected static function _apiCall(string $url, array $additionalHeaders = [], string $method="GET", string $data=''){ if($method=="POST"){ $curlLog = __DIR__ . '/curl-log.txt'; $f = fopen($curlLog, 'w'); $ch = curl_init($url); curl_setopt($ch, CURLOPT_VERBOSE, true); curl_setopt($ch, CURLOPT_STDERR, $f); curl_setopt($ch, CURLOPT_STDOUT, $f); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $hdr = [ 'Content-Type: application/json', 'Content-Length: ' . strlen($data) ]; foreach($additionalHeaders as $h) $hdr[] = $h; curl_setopt($ch, CURLOPT_HTTPHEADER, $hdr); curl_setopt($ch, CURLOPT_POSTFIELDS, $data ); $info = curl_getinfo($ch); $result = curl_exec($ch); curl_close($ch); fclose($f); mail("myemail#email.com","$url curl info",print_r($info,true) . ", result:$result, curl log: " . file_get_contents("file://$curlLog") . "\r\n, data:$data"); } else { // ... } } This is the log i get in the email for the big JSON: https://pastebin.com/0pYHTk4h Now, can it be an issue with POST size limit, either in PHP or Apache settings? But i doubt so, since the data is just 33Kb Content-Length: 34581 Also, if it was the case, shouldn't we expect the server (Apache + PHP 7.4.x) to return an HTTP error code like HTTP 500? Instead, we have HTTP 100 and HTTP 200. Seems not related to JSON formatting too, again because we'd expect the server to return an error code. One aspect that may be relevant: the called URL (from which i am logging php://input on server side) actually is a custom Wordpress REST API endpoint (that i have created by calling register_rest_route inside a rest_api_init action handler). I am not currently aware of POST size limits or related issues with WP API, i have no idea if the issue can be with WP. Any hints are appreciated!
Why is the curl code not executed in PHP script? (WAMP Server)
I am trying to get a token to use the Microsoft Graph API (https://learn.microsoft.com/en-us/graph/auth-v2-user?context=graph%2Fapi%2F1.0&view=graph-rest-1.0) via Curl. I have set up a simple Php file with this function: function getToken() { echo "start gettoken"; var_dump(extension_loaded('curl')); $jsonStr = http_build_query(Array( "client_id" => "***", "scope" => "https://graph.microsoft.com/.default", "client_secret" => "***", "grant_type" => "client_credentials" )); $headers = Array("Content-Type: application/x-www-form-urlencoded", "Content-Length: " . strlen($jsonStr)); $ch = curl_init("https://login.microsoftonline.com/***.onmicrosoft.com/oauth2/v2.0/token"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $token = curl_exec($ch); echo "test after curl"; return $token; curl_error($ch); } However, what I want to know is why the curl request is not working. Also the echo after the curl codeblock is not being executed, while 'start gettoken' is. PHP_curl is enabled in my WAMP. Why is this?
Are you sure CURL is enabled because that code you have posted is ok and giving echo response before and after curl execution.
you're sending the token request in a JSON-format, and then you're lying to the server saying it's application/x-www-form-urlencoded-encoded when it's actually application/json-encoded! since these 2 formats are completely incompatible, the server fails to parse it, and... ideally it should have responded HTTP 400 bad request (because your request can't be parsed as x-www-form-urlencoded) anyhow, to actually send it in the application/x-www-form-urlencoded-format, replace json_encode() with http_build_query() also get rid of the "Content-Length:"-header, it's easy to mess up (aka error-prone) if you're doing it manually (and indeed, you messed it up! there's supposed to be a space between the : and the number, you didn't add the space, but the usual error is supplying the wrong length), but if you don't do it manually, then curl will create the header for you automatically, which is not error-prone.
PHP HTTP POST Request
I need to send an XML string via HTTP POST to another server using the settings below... POST /xmlreceive.asmx/CaseApplicationZipped HTTP/1.1 Host: www.dummyurl.co.uk Content-Type: application/x-www-form-urlencoded Content-Length: length XMLApplication=XMLstring&byArray=base64string I'm guessing I need to set this up via cURL or maybe fsockopen. I've tried the following but not having any luck at getting it to work. $url = "http://www.dummyurl.co.uk/XMLReceive.asmx/CaseApplicationZipped"; $headers = array( "Content-Type: application/x-www-form-urlencoded"//, ); $post = http_build_query(array('XMLApplication' => $XML, 'byArray' => $base64)); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); echo "response: ".$response; The remote server gives the following response... "Object reference not set to an instance of an object."
Not enough rep yet to comment, so I'll post this as an answer. PHP's cURL automatically send the Host (from $url) and the Content-Length headers, so you don't need to specify those manually. A handy function exists for building the $post string: http_build_query. It'll handle properly encoding your POST body. It would look something like $post = http_build_query(array('XMLApplication' => $XML, 'byArray' => $base64)); If you want to log out the headers you received, check the curl_getopt function. As for the error you received, it seems like you're passing the remote site things it doesn't expect. I can't speak for what you're passing or what the site's expecting, but Input string was not in a correct format seems to imply that your $XML is not formatted correctly, or is being passed as an incorrect parameter.
PHP curl for the sc2ranks.com API
This is the first question ever here, so I'll do my best. Background I'm working on a small Starcraft II website that uses an API from a well known website. After looking at the documentation I got the example working rather quickly, returning the JSON code: curl -X POST 'http://api.sc2ranks.com/v2/characters/search' -d 'name=Wodan&bracket=1v1&league=gold&expansion=hots&rank_region=global&api_key='<api_key>' Hoping for an easy ride I created a small script that performs a HTTP-GET request on the API returning the base statistics for every in-game user. This URL can ofcourse be directly formed in the browser: http://api.sc2ranks.com/v2/characters/eu/1616021?api_key=<api_key> This is handled in my code in the following way (note: $url is the above URL and $this->session is the initialization of curl): curl_setopt($this->session,CURLOPT_URL, $url); curl_setopt($this->session,CURLOPT_RETURNTRANSFER, true); curl_setopt($this->session,CURLOPT_HEADER, true); if( !$result = curl_exec($this->session) ) exit("cURL Error: " . curl_error($this->session)); else return $result; The problem Well on my way I decided to use the more advanced functions of the API to get more detailed information on the member. This is however where the confusion starts. Looking back at the above original curl post in the terminal, it shows two options. The -X (request) and the -d (post data). Figuring the first would be the URL make the HTTP-POST request to and the later being the data, I came up with the following example: $url = "http://api.sc2ranks.com/v2/characters/search/"; $data = "league=all&rank_region=global&expansion=hots&bracket=1v1&name=Wodan?api_key=<my_key>"; curl_setopt($this->session, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($this->session, CURLOPT_POSTFIELDS, $data); curl_setopt($this->session, CURLOPT_RETURNTRANSFER, true); curl_setopt($this->session, CURLOPT_HTTPHEADER, array( 'Content-Type: text/html', 'Content-Length: ' .strlen($data)) ); if( !$result = curl_exec($this->session) ) exit("cURL Error: " . curl_error($this->session)); else return $result; I've also tried to pass the above through as JSON format. The output As a result I only get the following as a server response:String '{' was not found in 'HTTP/1.1 404 Not Found Server: nginx Date: Sun, 17 Nov 2013 22:14:03 GMT Content-Type: text/html; charset=utf-8 Content-Length: 0 Connection: close Status: 404 Not Found X-Request-Id: 700726570451c891d48862edfc8545fb X-Runtime: 0.009002 X-Rack-Cache: invalidate, pass ' What I think is going wrong Most likely I'm not forming the request right to the server or I need to target a specific file, other then they have documented it. I have found two similar questions here concerning POST's with Curl. But they were of little help to me and I feel that this might help more people out. Hopefully someone in this community has some more experience with curl or the actual API.
use the following code $apiParams = array ("league" => "all", "rank_region" => "global", "expansion" => "hots", "bracket" => "1v1", "name" => "Wodan", "api_key" => "<my_key>"); $Url = "http://api.sc2ranks.com/v2/characters/search"; $curlHandler = curl_init($Url); curl_setopt($curlHandler, CURLOPT_POST, true); curl_setopt($curlHandler, CURLOPT_POSTFIELDS, $apiParams); curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, true); curl_setopt($curlHandler, CURLOPT_HTTPHEADER, array( 'Content-Type: text/html', 'Content-Length: ' .strlen($data)) ); $statusCode = curl_exec($curlHandler); curl_close($curlHandler);
Hello people and thank you all for your help! I have solved the problem on my own now, but I had to scroll through the PHP manual for setopt itself. This is the following code that works: $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,"http://api.sc2ranks.com/v2/characters/search"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS,"name=Wodan&bracket=1v1&league=gold&expansion=hots&rank_region=global&api_key=<my_key>"); //curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_exec ($ch); curl_close ($ch); Note the commented out line above. The manual say's the following about CURLOPT_RETURNTRANSFER: TRUE to return the transfer as a string of the return value of curl_exec() instead of outputting it out directly. Since this example is not a HTTP GET-request but a HTTP POST-request there will be no direct string collected from the URL. When set to false it will collect any output that gets returned. My interpretation of the manual might be wrong, but this has solved the issue at least. I hope this will help other people. Thanks everyone for your help.
Getting content body from http post using php CURL
I am trying to debug an http post the I am trying to send from list application. I have been able to send the correct post from php CURL which corectly interfaces with my drupal 7 website and uploads an image. In order to get this to work in my lisp application I really need to see the content body of my http post I have been able to see the headers using a call like this: curl_setopt($curl, CURLOPT_STDERR, $fp); curl_setopt($curl, CURLOPT_VERBOSE, 1); and the headers look the same in my lisp application but I have been unable to examine the body of the post. I have searched online and other people have asked this question but no one posted a response. The content type of my http post is: application/x-www-form-urlencoded I have also tried many http proxy debuging tools but they only ever the http GET to get my php page but never capture the get sent from server once the php code is executed. EDIT: I have added a code snipet showing where I actually upload the image file. // file $file = array( 'filesize' => filesize($filename), 'filename' => basename($filename), 'file' => base64_encode(file_get_contents($filename)), 'uid' => $logged_user->user->uid, ); $file = http_build_query($file); // REST Server URL for file upload $request_url = $services_url . '/file'; // cURL $curl = curl_init($request_url); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); curl_setopt($curl, CURLOPT_STDERR, $fp); curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_POST, 1); // Do a regular HTTP POST curl_setopt($curl, CURLOPT_POSTFIELDS, $file); // Set POST data curl_setopt($curl, CURLOPT_HEADER, FALSE); // Ask to not return Header curl_setopt($curl, CURLOPT_COOKIE, "$cookie_session"); // use the previously saved session curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($curl, CURLOPT_FAILONERROR, TRUE); curl_setopt_array($curl, array(CURLINFO_HEADER_OUT => true) ); $response = curl_exec($curl);
CURLOPT_VERBOSE should actually show the details. If you're looking for the response body content, you can also use CURLOPT_RETURNTRANSFER, curl_exec() will then return the response body. If you need to inspect the request body, CURLOPT_VERBOSE should give that to you but I'm not totally sure. In any case, a good network sniffer should give you all the details transparently. Example: $curlOptions = array( CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_VERBOSE => TRUE, CURLOPT_STDERR => $verbose = fopen('php://temp', 'rw+'), CURLOPT_FILETIME => TRUE, ); $url = "http://stackoverflow.com/questions/tagged/java"; $handle = curl_init($url); curl_setopt_array($handle, $curlOptions); $content = curl_exec($handle); echo "Verbose information:\n", !rewind($verbose), stream_get_contents($verbose), "\n"; curl_close($handle); echo $content; Output: Verbose information: * About to connect() to stackoverflow.com port 80 (#0) * Trying 64.34.119.12... * connected * Connected to stackoverflow.com (64.34.119.12) port 80 (#0) > GET /questions/tagged/java HTTP/1.1 Host: stackoverflow.com Accept: */* < HTTP/1.1 200 OK < Cache-Control: private < Content-Type: text/html; charset=utf-8 < Date: Wed, 14 Mar 2012 19:27:53 GMT < Content-Length: 59110 < * Connection #0 to host stackoverflow.com left intact <!DOCTYPE html> <html> <head> <title>Newest 'java' Questions - Stack Overflow</title> <link rel="shortcut icon" href="http://cdn.sstatic.net/stackoverflow/img/favicon.ico"> <link rel="apple-touch-icon" href="http://cdn.sstatic.net/stackoverflow/img/apple-touch-icon.png"> <link rel="search" type="application/opensearchdescription+xml" title="Stack Overflow" href="/opensearch.xml"> ...
Just send it to a random local port and listen on it. # terminal 1 nc -l localhost 12345 # terminal 2 php -e <?php $curl = curl_init('http://localhost:12345'); // etc
If you're talking about viewing the response, if you add curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );, then the document returned by the request should be returned from your call to curl_exec. If you're talking about viewing the postdata you are sending, well, you should be able to view that anyway since you're setting that in your PHP. EDIT: Posting a file, eh? What is the content of $file? I'm guessing probably a call to file_get_contents()? Try something like this: $postdata = array( 'upload' => '#/path/to/upload/file.ext' ); curl_setopt( $curl, CURLOPT_POSTFIELDS, $postdata ); You can't just send the file, you still need a postdata array that assigns a key to that file (so you can access in PHP as $_FILES['upload']). Also, the # tells cURL to load the contents of the specified file and send that instead of the string.
You were close: The PHP manual instructs that you must call the constant CURLINFO_HEADER_OUT in both curl_setopt and curl_getinfo. $ch = curl_init($url); ... other curl options ... curl_setopt($ch,CURLINFO_HEADER_OUT,true); curl_exec(ch); //Call curl_getinfo(*args) after curl_exec(*args) otherwise the output will be NULL. $header_info = curl_getinfo($ch,CURLINFO_HEADER_OUT); //Where $header_info contains the HTTP Request information Synopsis Set curl_setopt Set curl_getinfo Call curl_getinfo after curl_exec
I think you're better off doing this with a proxy than in the PHP. I don't think it's possible to pull the raw POST data from the PHP CURL library. A proxy should show you the request and response contents
To get the header the CURLINFO_HEADER_OUT flag needs to be set before curl_exec is called. Then use curl_getinfo with the same flag to get the header after curl_exec. If you want to see the post data, grab the value you set at CURLOPT_POSTFIELDS For example: $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://example.com/webservice"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload)); curl_setopt($ch, CURLINFO_HEADER_OUT, true); curl_exec($ch); $header = curl_getinfo($ch, CURLINFO_HEADER_OUT); curl_close($ch); echo "Request-Header:\r\n" . $header . "\r\n"; echo "Request-Body(URL Encoded):\r\n" . http_build_query($payload) . "\r\n"; echo "Request-Body(Json Encoded):\r\n" . json_encode($payload) . "\r\n";