PHP - json_decode - issues decoding string - php

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

Related

PHP API request by GET details

I'm trying to get the details from this example (i created the code right now).
But i'm very... confused... how can i get the details of the link, then separate and send to my MYSQL database..
<?php
$ch = curl_init();
$url = "https://reqres.in/api/users?page=2";
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$resp = curl_exec($ch);
if($e = curl_error($ch)) {
echo $e;
}
else {
$decoded = json_decode($resp, true);
//print_r($decoded);
foreach($decoded as $key => $item) {
$array = array(
'id' => ,
'email' => ,
'first_name' => ,
'last_name' => ,
);
print_r($array);
}
}
curl_close($ch);
?>
If you call the url in your browser then you will see that the result array is present in the data field.
You may check this by printing the whole result:
print_r($decoded);
So if you like to print_r the results it should be simply
print_r($decoded['data']);
If you like to store it in your database you may walk through the array and store each item
foreach($decoded['data'] as $item) {
storeItem($item);
}
To make this work you should implement the storeItem function which accepts the array $item and stores it into your database. There are various tutorials about doing that.

How to get access the properties in this json response?

I am executing a curl request and get a response which returns a json response. Below is the code after the response is sent back.
Response: "Zeros Replaced real token"
{"success":true,"result":{"token":"000000000","serverTime":1471365111,"expireTime":1471365411}}1
Code Used (For Testing) and accessing property:
$json = json_decode($result);
print_r($json); // Prints the Json Response
$firsttry = $json->result['token']; //Access Property results in error :Trying to get property of non-object
$secondtry = $json['token'];
echo $firsttry.'<br>';//Code can't continue because of error from $firsttry.
print_r( $secondtry.'<br>');//Nothing Prints at all
I did notice a weird anomaly where it prints a 1 at the end, where as if i do
json_encode($json);
The return response replaces the one at the end of the string with a "true"
Could the "1 or true" at the end be throwing of the json decode?
Maybe I am missing something simple?
As Requested full test code
$url = "https://website.com/restapi.php";
//username of the user who is to logged in.
$userName="adminuser"; //not real user
$fields_string; //global var
$fields = array( //array will have more in the future
'username' => urlencode($userName)
);
//url-ify the data for the POST
foreach($fields as $key=>$value) { global $fields_string;
$fields_string .= $key.'='.$value.'&'; }
rtrim($fields_string, '&');
//open connection
$ch = curl_init();
//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, $url.'?'.$fields_string.'operation=getchallenge');
curl_setopt($ch,CURLOPT_POST, count($fields));
//execute post
$result = curl_exec($ch);
//close connection
curl_close($ch);
json_decode(), by default makes child objects into stdClass objects rather than arrays unless they are explicitly arrays.
Try something like:
$firsttry = $json->result->token;
The var_dump shows you the data type. Since result itself is an object, access its token with -> rather than []
$response = '{"success":true...}'
$json = json_decode($response); //var_dumping this will show you it's an object
echo $json->result->token; // 000000000
I figured out the issue. In the Curl Options I did not have
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
Once i put this in #GentelmanMax solution worked for me, but the issue was in the curl response responding directly, where as the return transfer sends back a string that php can work with, which then allowed json_decode()to function as is should. I knew it was something simple.

Array 2 string conversion while using CURLOPT_POSTFIELDS

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!

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.

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