PHP HTTP POST Request - php

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.

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.

Convert URL encoded to JSON object

I've never worked with JSON objects before but I think this is probably the best route for what I'm trying to achieve (if you think there is a better option please let me know, though I cannot use SOAP)
I am trying to submit some data via POST to an API in order to add some data to a newsletter list which is stored as XML. Here is the documentation for the request I am trying to complete: https://api.dotmailer.com/v2/help/wadl#PostAddressBookContacts
When visiting the API url and authenticating I see data laid out in the following:
<ArrayOfApiContact xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://apiconnector.com">
<ApiContact>
<Id>1058905201</Id>
<Email>email#email.co.uk</Email>
<OptInType>Unknown</OptInType>
<EmailType>Html</EmailType>
<DataFields i:nil="true"/>
<Status>Subscribed</Status>
</ApiContact>
</ArrayOfApiContact>
So I have attempted to replicate as such, using a variable instead of a hardcoded email:
$xml = array(
'id' => urlencode('123456789'),
'email' => urlencode($useremail),
'optInType' => urlencode('Unknown'),
'emailType' => urlencode('Html'),
'dataFields' => urlencode(':null'),
'status' => urlencode('Subscribed')
);
I am combining the array here:
foreach($xml as $key=>$value) { $xml_string .= $key.'='.$value.'&'; }
And then using cURL to POST the request to the API, with my authentication (apipassword and apiemail which the username)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$auth_url);
curl_setopt($ch,CURLOPT_POST, count($xml));
curl_setopt($ch,CURLOPT_POSTFIELDS, $xml_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_USERPWD, "$apiemail:$apipassword");
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); //get status code
$result = curl_exec ($ch);
curl_close ($ch);
I have already tested this without the POST and I do indeed get the data back in this form:
[{"id":1058905201,"email":"email#email.co.uk","optInType":"Unknown","emailType":"Html","dataFields":null,"status":"Subscribed"}]
So I know the auth is working and the data is coming back correctly, but now when I try to POST my data I get:
{"message":"Content type \"application/x-www-form-urlencoded\" is not supported. Please use one of: application/json,application/xml ERROR_CONTENT_TYPE_IS_NOT_SUPPORTED"}
So how do I convert my array which is URL encoded into something readable by the API?
Try a content type of application/json like so:
curl_setopt($ch, CURLOPT_HTTPHEADER, array('content-type: application/json'));
Two things here (a) content-type header JSON missing and (b) payload not JSON.
// setup request to send json via POST.
$payload = json_encode(array("json"=> $data)); // or json_encode(array($data));
curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
// set proper content-type for sending JSON
curl_setopt( $ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));

I need to do what Postman does in PHP

Postman is an API testing interface that I can use to make my specific API call successfully. I'm trying to do the same thing in PHP using CURL, but am having an issue with the way the API expects the format...
Let's say my URL is /post. The only variable after /post is "auth=asdf1234etc", and then XML data. So using postman I put
/post?auth=asdf1234etc
That goes in the URL section, and it has a raw XML data post section, where I put My XML:
<?xml version="1.0" encoding="UTF-8"?><usageReports><usageReport></usageReport></usageReports>
My problem is that the XML is not a variable, where asdf1234etc is the value of the key auth, my XML is a value with no key -- so I don't know how to make the post using CURL.
Here's my PHP that doesn't work -- I get 401 unauthorized because the program thinks everything after ?auth="..." is the auth token. But it also doesn't work if I put an & after the auth token, because after an & I would have to do key=value, where I don't have a key.
$URL = "/post?auth=asdf1234etc";
$usage_report = '<?xml version="1.0" encoding="UTF-8"?><usageReports><usageReport></usageReport></usageReports>';
$fields = array("Host" => "/post",
"Content-type" => "application/xml",
"Content-length" => strlen($usage_report),
"data" => $usage_report);//note that my xml is not a variable and I don't expect this specific line to work.
$fields_string = "&";
foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
rtrim($fields_string,'&');
//open connection
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $URL);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $fields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
//execute post
$result = curl_exec($ch);
//close connection
curl_close($ch);
$array_data = json_decode(json_encode(simplexml_load_string($result)), true);
//view response
echo "\n\n";
print_r($array_data);
echo "\n";
What would be really helpful is if postman had an output that was a literal URL with everything in it, post URL base + authtoken + the XML -- because I can do it in postman(200 OK), just can't get the PHP to do the same.
I turned on CURLOPT_VERBOSE, and here might be some useful information:
POST /post?asdf123etc HTTP/1.1
Host: hidden.domain.com
Accept: */*
Content-Length: 335 //Why 335? strlen($usage_report) is 248?
Content-Type: application/x-www-form-urlencoded //why is it changing from application/xml to this?
-it’s not a certificate issue
-it’s not a connection to host issue
-it’s kindof an auth issue, because it thinks that everything after ? is the auth token
-it’s an interesting problem because I can’t set the xml to a key in the fields array
-the xml constructed and urls I use work when using postman
-not an issue of xml format
p.s. if you think you've found a duplicate, make sure they are also asking what to do when POSTing XML that isn't a httpheader. (not something like this)
Remove these lines from your code:
$fields = array("Host" => "/post",
"Content-type" => "application/xml",
"Content-length" => strlen($usage_report),
"data" => $usage_report);//note that my xml is not a variable and I don't expect this specific line to work.
$fields_string = "&";
foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
rtrim($fields_string,'&');
Now, first fix your xml string. You didn't escape the quotes from your xml. So use this one:
$usage_report = '<?xml version="1.0" encoding="UTF-8"?><usageReports><usageReport></usageReport></usageReports>';
Replace these two curl options with your existing one. Here its posting XML data:
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: application/xml")); // or text/xml
curl_setopt($ch, CURLOPT_POSTFIELDS, $usage_report);
Your verbose output says you have used a proper url. But still to double check, make sure the url is correct. Currently its using URI instead of URL in your code:
$URL = "/post?auth=asdf1234etc";
You can get postman code for all languages and use it:
for example for your question you can copy the php-guzzle code easily and use it.
On the right side of the page, there is a button to show the code.
in the below photo, you can find it.
#postman #PHP #Guzzle #Laravel

Type of "application/json" prevents post variables from being sent

I've found that if I try a PHP POST curl, the postvars are sent fine. Once I add the httpheader of content-type: application/json the postvars don't go across any more. I've tried the postvars as a JSON string and as a query string.
Showing some code:
$ch = curl_init();
$post = json_encode(array('p1' => 'blahblahblah', 'p2' => json_encode(array(4,5,6))));
$arr = array();
array_push($arr, 'Content-Type: application/json; charset=utf-8');
curl_setopt($ch, CURLOPT_HTTPHEADER, $arr);
curl_setopt($ch, CURLOPT_URL, 'https://example.com/file.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_exec($ch);
curl_close($ch);
A real HTTP Post, that gets broken into an array automagically by PHP must be formatted in name=value pairs, the best way to do this in PHP is using the http_build_query function.
http://ca2.php.net/manual/en/function.http-build-query.php
There is an example that works in the PHP Manual using curl:
http://ca2.php.net/manual/en/function.curl-exec.php#98628
What you're doing is a 'RAW' post, see this other question:
How to post JSON to PHP with curl
A quick snippet to get the RAW Json data.
<?php
print_r(json_decode(file_get_contents('php://input')));

Categories