Array 2 string conversion while using CURLOPT_POSTFIELDS - php

I have following code:
// $postfields = array();
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
My $postfields variable is an array of parameters. And i have a notice there is array to string conversion. It works tho.
I could use http_build_query() function to nullify notice, however i use #path_to_file to include post files. and http_build_query() breaks file includes.
I'm wondering if there is more "proper" way to do this. Without generating a notice.

Are some values of $postfields arrays themselves? This is most likely what's causing the notice. curl_setops expects its third parameter to be an array whose keys and values are strings, as is stated in PHP's manual page for the function, though it might not be very clear:
This parameter can either be passed as a urlencoded string like 'para1=val1&para2=val2&...' or as an array with the field name as key and field data as value.
In this quote, the key point is that para1/2 and val1/2 are strings, and if you want, you can provide them as an array where keys are para1 and para2, and values are val1 and val2.
There are two ways to eliminate the notices.
The first is to use http_build_query() and replace your uses of #filepath by CURLFile objects. This is only possible if you're using PHP 5.5 or above, unfortunately. The manual's page has a pretty clear and simple example of use.
If using CURLFiles is not an option for you, then the second way is to json_encode() the values of your $postfields array which are arrays themselves. This isn't elegant, and it requires you to decode the JSON on the other side.

j11e's answer won't work if you want to send multidimensional arrays
Try this recursive function.
https://gist.github.com/yisraeldov/ec29d520062575c204be7ab71d3ecd2f
<?php
function build_post_fields( $data,$existingKeys='',&$returnArray=[]){
if(($data instanceof CURLFile) or !(is_array($data) or is_object($data))){
$returnArray[$existingKeys]=$data;
return $returnArray;
}
else{
foreach ($data as $key => $item) {
build_post_fields($item,$existingKeys?$existingKeys."[$key]":$key,$returnArray);
}
return $returnArray;
}
}
And you can use it like this.
curl_setopt($ch, CURLOPT_POSTFIELDS, build_post_fields($postfields));

Using Laravel one thing that worked for me was to use the tag 'Content-Type: application/json' in the request header, and sending my data json encoded like this:
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Accept: application/json'));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
In the function that receives the parameters in the request I had no need to use the json decode function, I access to the parameters just like
$request->something

After research for an hour, here the way i fixed my code:
$strVar = '';
if ($data) {
$ea = build_post_fields($data);
foreach($ea as $key=>$val) {
$strVar.= "$key=$val&";
}
}
/* eCurl */
$curl = curl_init($url);
/* Set Array data to POST */
curl_setopt( $curl, CURLOPT_POSTFIELDS, ($strVar) );
And here is the function I take from #Yisrael Dov below:
function build_post_fields( $data, $existingKeys='', $returnArray=[]){
if(($data instanceof CURLFile) or !(is_array($data) or is_object($data))){
$returnArray[$existingKeys]=$data;
return $returnArray;
}
else{
foreach ($data as $key => $item) {
build_post_fields($item,$existingKeys?$existingKeys."[$key]":$key,$returnArray);
}
return $returnArray;
}
}
That work perfectly! You can post a deep array like:
$post_var = array(
'people' => array('Lam', 'Hien', 'Nhi'),
'age' => array(12, 22, 25)
);
Good day!

Related

PHP - json_decode - issues decoding string

I'm playing with the API from deepl.com that provides automatic translations. I call the API through cURL and I get a json string in return which appears to be fine but cannot be decoded by PHP for some reason.
Let me show first how I make the cURL call :-
$content = "bonjour <caption>monsieur</caption> madame";
$url = 'https://api.deepl.com/v2/translate';
$fields = array(
'text' => $content,
'target_lang' => $lg,
'tag_handling' => 'xml',
'ignore_tags' => 'caption',
'auth_key' => 'my_api_key');
$fields_string = "";
foreach($fields as $key=>$value)
{
$fields_string .= $key.'='.$value.'&';
}
rtrim($fields_string, '&');
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded','Content-Length: '. strlen($fields_string)));
$result = curl_exec($ch);
curl_close($ch);
If at this stage I do
echo $result;
I get:
{"translations":[{"detected_source_language":"FR","text":"Hola <caption>monsieur</caption> Señora"}]}
Which seems ok to me. Then if I use code below -
echo gettype($result);
I get "string" which is still ok but now, the following code fails:
$result = json_decode($result,true);
print_r($result);
The output is empty!
If I now do something like this:
$test = '{"translations":[{"detected_source_language":"FR","text":"Hola <caption>monsieur</caption> Señora"}]}';
echo gettype($test);
$test = json_decode($test,true);
print_r($test);
I get a perfectly fine array:
(
[translations] => Array
(
[0] => Array
(
[detected_source_language] => FR
[text] => Hola <caption>monsieur</caption> Señora
)
)
)
I did nothing else than copy/pasting the content from the API to a static variable and it works but coming from the API, it doesn't. It's like the data coming from the API is not understood by PHP.
Do you have any idea of what's wrong?
Thanks!
Laurent
I've had very similar issues before and for me the issue was with the encoding of the data returned from an API being unicode. I'm guessing when you do your copy/paste the string you hard-code ends up being a different encoding so it works fine when passed into json_decode.
The PHP docs specify json_decode only works with UTF-8 encoded strings:
http://php.net/manual/en/function.json-decode.php
You may be able to use mb_convert_encoding() to convert to UTF-8:
http://php.net/manual/en/function.mb-convert-encoding.php
Try this before calling json_decode:
$result = mb_convert_encoding($result, "UTF-8");
Make sure to set CURLOPT_RETURNTRANSFER to true. Only then will curl_exec actually return the response, otherwise it will output the response and return a boolean, indicating success or failure.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
if ($result !== false) {
$response = json_decode($result, true);
// do something with $response
} else {
// handle curl error
}
Like said #Eilert Hjelmeseth you have some special char in your JSON string => "Señora"
Another way to encode a string to UTF8: utf8_encode() :
$result = json_decode(utf8_encode($result),true);

PHP adding backslashes to my CURL command for Facebook Offline Conversions

I'm trying to write a PHP script based on the instructions here:
https://developers.facebook.com/ads/blog/post/2016/12/18/lead-ads-offline/
But I'm having issues with a json string passed as a parameter to curl in PHP. It looks like it's adding backslashes ("match_keys":"Invalid keys \"email\" etc.) which are causing the API call to fail.
I've tried playing around with:
json_encode($array,JSON_UNESCAPED_SLASHES);
I've tried a bunch of SO answers already like Curl add backslashes but no luck.
<?php
$email = hash('sha256', 'test#gmail.com');
$data = array("match_keys" => '{"email": $email}',
"event_time" =>1477632399,
"event_name"=> "Purchase",
"currency"=> "USD",
"value"=> 2.00);
$fields = [
// 'upload_tag'=>'2016-10-28-conversions',
'access_token'=>'#######',
'data'=> $data
];
$url = 'https://graph.facebook.com/v2.8/#######/events';
echo httpPost($url, $fields);
function httpPost($url, $fields)
{
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($fields));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
?>
This is the response:
Array{"error":{"message":"(#100) Invalid parameter","type":"OAuthException","code":100,"error_data":{"match_keys":"Invalid keys \"email\" were found in param \"data[match_keys]\".","event_time":"Out of bounds array access: invalid index match_keys","event_name":"Out of bounds array access: invalid index match_keys","currency":"Out of bounds array access: invalid index match_keys","value":"Out of bounds array access: invalid index match_keys"},"fbtrace_id":"BrVDnZPR99A"}}%
You are mixing JSON in with PHP arrays. I doubt it's actually JSON you intend using to send your data, since you're using http_build_query. Try this:
$data = array("match_keys" => ["email" => $email],
"event_time" =>1477632399,
"event_name"=> "Purchase",
"currency"=> "USD",
"value"=> 2.00
);
With this, you define your data as an array, and leave http_build_query to do the encoding for you.

PHP (CI) cURL passed multidimensional array does not behave as one (Can't loop it)

I'm having an extrange issue when receiving parameters from a POST cURL request. No matter how I encode it (json, url, rawurl, utf8, base64...) before POSTing it, I am not able to perform any decoding operation through the array elements, via loop. I'm giving you the details.
From the consuming controller, in some other php (Yii) app, I build my request like this:
private function callTheApi($options)
{
$url = "http://api.call.com/url/api";
$params = array( 'api_key' => $this->api_key,
'domain' => $this->domain,
'date' => $options['date'],
'keys' => $options['keys'] // This is an array
);
// Following some good advice from Daniel Vandersluis here:
// http://stackoverflow.com/questions/3772096/posting-multidimensional-array-with-php-and-curl
if (is_array($params['keys'])
{
foreach ($params['keys'] as $id => $name)
{
$params['keys[' . $id . ']'] = $name;
}
unset($params['keys']);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: multipart/form-data; charset=utf-8'));
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_USERAGENT,
"Mozilla/5.0 (Windows; U; Windows NT 5.0; en; rv:1.9.0.4) "
. "Gecko/2009011913 Firefox/3.0.6");
$output = curl_exec($ch);
$error = curl_errno($ch);
$error_text = curl_error($ch);
curl_close($ch);
if (!$output || $error != 0)
die("<br><hr>Problems...<br>"
. "Line:" . __LINE__ . " dataExtractor.php<br>"
. "Error: " . $error . " - " . $error_text . "<hr>"
. $url . "<hr>");
sleep(1);
return json_decode($output, true);
}
And in the api itself, this is the function:
public function api()
{
$params = $_POST;
foreach($params as $k=>$v){
if($k=='domain') $domain = $v;
if($k=='date') $date = $v;
if($k=='api_key') $api_key = $v;
if($k=='keys') $keys = $v;
}
echo json_encode($keys);
// All my logic would be here, after parsing the array correctly.
}
Ok, now for the problems:
If i leave everything like stated before, it works. I have my $keys array in the api, and I can use it however I want. The "echo json_encode($keys)" sentence returns the array ALMOST as it should be. But the problem is some values of the array are corrupted in the cURL operation. Values such as spanish characters á, é ,í, ó, ú OR ü are simply not present in the array_values.
If some key in the $keys array was spanish word "alimentación" in the original array, once it's been cURLed to the api, it becomes "alimentacin". There, the ó is not there anymore.
So, my chances are encoding each value in the array to a safely transferred value, so that I can decode it later. But what do you know, I can't.
I've tried urlencoding, rawurlencoding, json_encoding, base64_encoding... each value of the array. And if I return the received array from the api, it contains the encoded values all right. BUT.
If I loop the array in the api for decoding, and then try to return it, no matter what decoding function I'm applying to its values, the output is ALWAYS "NULL".
I have no clue what I'm doing wrong here. Not even close.
So any help would be much appreciated. Thanks in advance, community.
When you create cUrl params array you should know that keys cannot be utf8.
And when you add some parameters in foreach loop
$params['keys[' . $id . ']'] = $name;
$id can be utf8 character.
To avoid that I recommend you to use json_encode
$params = array(
'api_key' => $this->api_key,
'domain' => $this->domain,
'date' => $options['date'],
'keys' => json_encode($options['keys']) // This is an array
);
In your api in this case you should change nothing.

Can't get data out of variable

I've made a curl request. I put the curl instructions in one class function:
class Curly {
var $data;
function GetRequest($url) {
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$this->data = curl_exec($ch);
curl_close($ch);
//this is what i was missing --> return $this->data;
}
I put the database query in another class function.
include('class.curly.php');
class mongoSearchForInfoOnEsd {
public function getEsdsFbInfo($esdID) {
$mongoApiKey = "xx";
$requestParams= "xx";
$url = "xx";
$fbInfo = (new Curly)->GetRequest($url);
//this is what i was missing --> return $fbInfo;
}
In the index.php, an HTTP post from a webhook is coming through, in which parsing through some strings to obtain 2 ids is handled. I then send one of those ids to the mongodb curl request, everything goes good. The correct response comes back, I only know this b/c of the var_dump of the var_dump in the curly class....BUT in the index file I'm struggling to get to get the data out of the var and assign its values to any variable I want.
How can I get the data out? I know its there, but where?
I'm so stuck.
# get ytID from http post
#get EsdID from http post
$httpPostData = file_get_contents('php://input');
$postDataDecoded = urldecode($httpPostData);
$ytID = substr($postDataDecoded, strpos($postDataDecoded, "docid=") + strlen("docid="), );
$esdID = substr($postDataDecoded, strpos($postDataDecoded, "m\": \"") + strlen ("m\": "),;
*$esdData = (new mongoSearchForInfoOnEsd)->getEsdsFbInfo("$esdID");*
$obj = json_decode($esdData, true);
echo $obj;
OK, I've added return and I can see the data, but no operations are working on returned data.
edit ---> put return in both classes, now its fully operational.
As lazyhammer said you need to write the following in the end of your method GetRequest($url)
return $this->data;
Also, in a class, a function is called a method.
To be more explicit.
var_dump doesn't return the data. it's only sending them to the client(your browser) which will display it.
to return the data computed in your method back to the caller, you need to use the keyword return at the end of your method.
When your computer will see return he bring back the data to the caller. it means everything you write after return won't be executed.
Just because you are assigning a value to the class variable data doesn't mean that value is being returned when you call the function getRequest. Therefore, in order to use the data from an outside class, you need to return the final value:
function GetRequest($url){
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$this->data = curl_exec($ch);
curl_close($ch);
return $this->data;
}
You may not even need to keep the variable $data around, unless there is more to your code that you are not showing, you could simply return curl_exec($ch)
To further answer your question from your comments below, this is from php.net:
This function displays structured information about one or more expressions that includes its type and value. Arrays and objects are explored recursively with values indented to show structure.
As you can see, var_dump is used for display purposes only.

Decoding JSON after sending using PHP cUrl

I've researched everywhere and cannot figure this out.
I am writing a test cUrl request to test my REST service:
// initialize curl handler
$ch = curl_init();
$data = array(
"products" => array ("product1"=>"abc","product2"=>"pass"));
$data = json_encode($data);
$postArgs = 'order=new&data=' . $data;
// set curl options
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postArgs);
curl_setopt($ch, CURLOPT_URL, 'http://localhost/store/rest.php');
// execute curl
curl_exec($ch);
This works fine and the request is accepted by my service and $_Post is populated as required, with two variables, order and data. Data has the encoded JSON object. And when I print out $_Post['data'] it shows:
{"products":{"product1":"abc","product2":"pass"}}
Which is exactly what is expected and identical to what was sent in.
When I try to decode this, json_decode() returns nothing!
If I create a new string and manually type that string, json_decode() works fine!
I've tried:
strip_tags() to remove any tags that might have been added in the http post
utf8_encode() to encode the string to the required utf 8
addslashes() to add slashes before the quotes
Nothing works.
Any ideas why json_decode() is not working after a string is received from an http post message?
Below is the relevant part of my processing of the request for reference:
public static function processRequest($requestArrays) {
// get our verb
$request_method = strtolower($requestArrays->server['REQUEST_METHOD']);
$return_obj = new RestRequest();
// we'll store our data here
$data = array();
switch ($request_method) {
case 'post':
$data = $requestArrays->post;
break;
}
// store the method
$return_obj->setMethod($request_method);
// set the raw data, so we can access it if needed (there may be
// other pieces to your requests)
$return_obj->setRequestVars($data);
if (isset($data['data'])) {
// translate the JSON to an Object for use however you want
//$decoded = json_decode(addslashes(utf8_encode($data['data'])));
//print_r(addslashes($data['data']));
//print_r($decoded);
$return_obj->setData(json_decode($data['data']));
}
return $return_obj;
}
Turns out that when JSON is sent by cURL inside the post parameters & quot; replaces the "as part of the message encoding. I'm not sure why the preg_replace() function I tried didn't work, but using html_entity_decode() removed the &quot and made the JSON decode-able.
old:
$return_obj->setData(json_decode($data['data']));
new:
$data = json_decode( urldecode( $data['data'] ), true );
$return_obj->setData($data);
try it im curious if it works.

Categories