Unable to parse JSON web service response with json_decode() - php

I'm struggling with parsing a web service response JSON in cases where the service returns an error.
Example JSON - success flow:
{
"Response": [{
"iconPath" : "/img/theme/destiny/icons/icon_psn.png",
"membershipType": 2,
"membershipId": "4611686018429261138",
"displayName": "Spuff_Monkey"
}],
"ErrorCode": 1,
"ThrottleSeconds": 0,
"ErrorStatus": "Success",
"Message": "Ok",
"MessageData":{}
}
Example JSON - error flow:
{
"ErrorCode": 7,
"ThrottleSeconds": 0,
"ErrorStatus": "ParameterParseFailure",
"Message": "Unable to parse your parameters. Please correct them, and try again.",
"MessageData": {}
}
Now my PHP:
function hitWebservice($endpoint) {
$curl = curl_init($endpoint);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
$json_response = curl_exec($curl);
if(curl_exec($curl) === false) {
echo "Curl error: " . curl_error($curl);
}
curl_close($curl);
$array_response = json_decode($json_response, true);
$function_response = array();
if (!isset($array_response['Response'])) {
$function_response = $array_response;
} else {
$function_response = $array_response['Response'];
}
return $function_response;
}
What I'm trying to achieve is when the JSON includes the "Response" block I put that in a new array and return only that detail from the function, where "Response" isn't present I want to return the full JSON as an array.
However at present, where there is no "Response" I get an empty array.
There's something wrong with my logic and I can't get past it in my tiny mind, so it's time to reach out for help!

Judging from the fact that Response is an array of objects in the JSON, I suspect that the error-flow response may also contain a Response-field, but with an empty array as value ([]). That would explain your current result.
Therefore, do not check for the existence of Response. It may just be an empty array. Instead, check for the ErrorCode, ErrorStatus or ErrorMessage (whichever you think is most suitable). For example:
if ($array_response['ErrorStatus'] != "Success") {
$function_response = $array_response;
} else {
if (!isset($array_response['Response'])) {
$function_response = null;
} else {
$function_response = $array_response['Response'];
}
}
In the Success-case, you want to check for existence of Response, so that if it does not exist (but it is expected), you can raise some error).
Another possible solution is to count the number of responses:
if (!isset($array_response['Response'])) {
$function_response = $array_response;
} else {
if (count($array_response['Response']) > 0) {
$function_response = $array_response['Response'];
} else {
$function_response = $array_response;
}
}

If you notice, both a good and a bad response contain an ErrorCode
You would be better designing your code to work from this field rather than test a field that may or may not exist.
So try this instead :-
$array_response = json_decode($json_response, true);
switch ( $array_response['ErrorCode'] ) {
case 1 :
do_errorCode_1_processing($array_response)
break;
case 2 :
do_errorCode_2_processing($array_response)
break;
// etc etc
}

isset () is not the right function for checking if the key in an array is present or not.
Use array_key_exists () instead.
http://php.net/manual/en/function.array-key-exists.php
so your code should look like this:
$array_response = json_decode($json_response, true);
$function_response = array();
if (array_key_exists('Response', $array_response)) {
$function_response = $array_response['Response'];
} else {
$function_response = $array_response;
}
return $function_response;
The above should do the trick.

Related

Abstract Api Validation for Mobile number

Hello everyone I am trying to validate the mobile number using abstract api validation but I am stuck to check which number is valid and which number is not valid for this I write a code.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://phonevalidation.abstractapi.com/v1/?api_key=my_api&phone=14152007986');
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$check = (string)$data;
if (strpos($check, 'true') == true)
{
echo "PhoneNo is valid";
}
if (strpos($check, 'false') == false)
{
echo "PhoneNo is invalid";
}
In the above code the phone number is correct as I am giving the phone number as example but still its showing me PhoneNo is invalid can any one help me to create a logic for it
strpos — Find the position of the first occurrence of a substring in a string
So if the result is "true" string your validation with strpos should look like
if (strpos($check, 'true') !== false)
{
echo "PhoneNo is valid";
} else
echo "PhoneNo is invalid";
}
Because function returns false if the needle was not found.
The Problem what i see is: You get a string then you parse the string into an array to cast it back into a string. so you can use your response right away. I don't know what the response looks like when it succeeds or fails. I only get api key missing.
curl_setopt($ch, CURLOPT_URL, 'https://phonevalidation.abstractapi.com/v1/?api_key=my_api&phone=14152007986');
$response = curl_exec($ch);
curl_close($ch);
if (strpos($response, 'true') == true)
{
echo "PhoneNo is valid";
} else {
echo "PhoneNo is invalid";
}

Checking for error in foreach with valid but changed json with different stucture

I am pulling data from a 3rd party web service and when the api goes under maintenance the json document no longer has the same structure (but is still a valid json document). So when I go to parse, I get an error: "Invalid argument supplied for foreach()" Makes sense, but how do I test for this and then call a separate function to log the error with the contents of the json document?
I tried adding set_error_handler("customError"); and that works but I don't know how to access the contents of $response or $json from the error function
$response = file_get_contents($url);
$json = json_decode($response, TRUE);
foreach($json['workers'] as $item) {
echo $item['address']; //address does not exist when in maintenance mode!
}
function customError($errno, $errstr) {
$link=Connection();
echo "<b>Error:</b> [$errno] $errstr ";
$sql="Insert into myErrors (response, description) values ('".$response."','.$errstr.');";
//echo $sql;
$result = mysqli_query( $link,$sql) or die('Error; ' . mysqli_error($link));
}
Test the result to see if the array exists before trying to loop over it.
$response = file_get_contents($url);
$json = json_decode($response, TRUE);
if (isset($json['workers'])) {
foreach($json['workers'] as $item) {
echo $item['address']; //address does not exist when in maintenance mode!
}
} else {
// insert something into myErrors table
}

PHP: Assign array to variable

i've tried a lot of things to achieve this but none of them seem to
work. Im using PHP 7.4
Let's say i have this:
$othervar = array();
$var = array(1, 2, 3);
$othervar = $var;
THIS doesn't work for me, var_dump($othervar) returns
array(1) { [0]=> string(5) "Array" }
I've tried using array_push, i DON'T WANT to use array_merge because i
need to assign two arrays to one variable. This is what i need to do:
$variable = array();
$variable["type1"] = $data; //Array 1
$variable["type2"] = $otherData; //Array 2
This doesn't work either.
Barmar showed me here that this works so i must be doing it wrong somewhere else.
I'll explan the whole code:
To login to my webpage, i send a request trough AJAX request with jQuery.
function SendData(data, btn, actionOnSuccess, shouldReplace = false, elementToReplace = "", getServerData = true, htmlData = "") {
if (!loading)
{
ToggleLoading();
data.push({name: "action", value: $(btn).data("action")});
data.push({name: "attr", value: JSON.stringify($(btn).data("attr"))});
$.post("SendRequest.php", data)
.done(function (r) {
if (!r.success)
//ajax sent and received but it has an error
else
//ajax sent and it was successfull
})
.fail(function () {
//ajax call failed
});
}
else {
//This determines if some request is already executing or not.
}
}
"action" and "attr" are encrypted values that i send to reference some actions on the system (i'll show more here):
The code goes from AJAX to SendRequest.php where it executes an action let's say, login.
The first lines of SendRequest.php are:
require "Functions.php";
$apiAction = Decrypt($_POST["action"]); //Action
$actionData = json_decode(Decrypt($_POST["attr"])); //Attr
$finalPost = $_POST;
foreach ($actionData as $key => $value) { $finalPost[$key] = $value; }
$finalPost["loggedin_ip"] = $_SERVER['REMOTE_ADDR'];
$result = APICall($apiAction, $finalPost);
Then, this is what i want to achieve to communicate with my API:
function APICall($option, $data = array())
{
session_start();
$post = array("uData" => ArrayToAPI($_SESSION), "uPost" => ArrayToAPI($data));
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_URL, "https://apiurl?" . $option); //option is the ACTION to perform on API (let's say "login") it is an encrypted word on a data-attr atribute on every form/button that creates a communication with API.
$returned = curl_exec($ch);
curl_close ($ch);
$newData = json_decode($returned, true);
return $newData;
}
function ArrayToAPI($array)
{
$toApiData = array();
foreach ($array as $key=>$value) {
if (is_array($value))
$toApiData[$key] = ArrayToAPI($value);
else
$toApiData[$key] = Encrypt($value);
}
return $toApiData;
}
This is what i have on API side:
ob_start();
var_dump($_POST);
$result = ob_get_clean();
$api->EndRequest(false, array("errorDesc" => "a - " . $result));
function EndRequest(bool $task_completed, array $data = array())
{
$jsonData = array();
$jsonData['success'] = $task_completed;
$jsonData['data'] = $data;
header('Content-type: application/json; charset=utf-8');
echo json_encode($jsonData, JSON_FORCE_OBJECT);
exit;
}
This ALWAYS returns
array(2) { ["uData"]=> string(5) "Array" ["uPost"]=> string(5) "Array" }
I hope im more clear now, thanks.
The problem is with the request being sent out from your code because of this line:
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
CURLOPT_POSTFIELDS doesn't support multi-level arrays. Your array values (which the keys are pointing to) are cast to string, which ends up as Array. Use:
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
.. instead to "properly" serialize multidimensional arrays in a post request ("properly" as there are many ways to do just that, but it matches the expected PHP format - with [] to denote arrays).

cURL request in a loop sometimes returning nothing at all

The issue:
I'm working with PHP, cURL and a public API to fetch json strings.
These json strings are formatted like this (simplified, average size is around 50-60 kB):
{
"data": {},
"previous": "url",
"next": "url"
}
What am trying to do is fetch all the json strings starting from the first one by checking for the "next" attribute. So I have a while loop and as long as there's a "next" attribute, I fetch the next URL.
The problem is sometimes, randomly the loop stops before the end and I cannot figure out why after many tests.
I say randomly because sometimes the loop goes through to the end and no problem occurs. Sometimes it crashes after N loops.
And so far I couldn't extract any information to help me debug it.
I'm using PHP 7.3.0 and launching my code from the CLI.
What I tried so far:
Check the headers:
No headers are returned. Nothing at all.
Use curl_errno() and curl_error():
I tried the following code right after executing the request (curl_exec($ch)) but it never triggers.
if(curl_errno($ch)) {
echo 'curl error ' . curl_error($ch) . PHP_EOL;
echo 'response received from curl error :' . PHP_EOL;
var_dump($response); // the json string I should get from the server.
}
Check if the response came back null:
if(is_null($response))
or if my json string has an error:
if(!json_last_error() == JSON_ERROR_NONE)
Though I think it's useless because it will never be valid if the cURL response is null or empty. When this code triggers, the json error code is 3 (JSON_ERROR_CTRL_CHAR)
The problematic code:
function apiCall($url) {
...
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
}
$inc = 0;
$url = 'https://api.example.com/' . $id;
$jsonString = apiCall($url);
if(!is_null($jsonString)) {
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
while ($nextUrl) {
$jsonString = apiCall($url . '?page=' . $nextUrl);
if(!is_null($jsonString)) {
$inc++;
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
}
}
}
What I'm expecting my code to do:
Not stop randomly, or at least give me a clear error code.
The problem is that your API could be returning an empty response, a malformed JSON, or even a status code different of 200 and you would stop execution imediately.
Since you do not control the API responses, you know that it can fail randomly, and you do not have access to the API server logs (because you don't, do you?); you need to build some kind of resilience in your consumer.
Something very simple (you'd need to tune it up) could be
function apiCall( $url, $attempts = 3 ) {
// ..., including setting "$headers"
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
for ( $i = 0; $i < $attempts; $i ++ ) {
$response = curl_exec( $ch );
$curl_info = curl_getinfo( $ch );
if ( curl_errno( $ch ) ) {
// log your error & try again
continue;
}
// I'm only accepting 200 as a status code. Check with your API if there could be other posssible "good" responses
if ( $curl_info['http_code'] != 200 ) {
// log your error & try again
continue;
}
// everything seems fine, but the response is empty? not good.
if ( empty( $response ) ) {
// log your error & and try again
continue;
}
return $response;
}
return null;
}
This would allow you to do something like (pulled from your code):
do {
$jsonString = apiCall($url . '?page=' . $nextUrl);
$nextUrl = false;
if(!is_null($jsonString)) {
$inc++;
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
}
}
while ($nextUrl);
I'm not checking if the return from the API is non-empty, not a connection error, a status different from '200' and yet an invalid JSON.
You may want to check for these things as well, depending on how brittle the API you are consuming is.

Record the result of a curl GET request using php & mysql

I'm trying to understand how to record the result of a curl GET request using php. I'm looking at outputing part or all of the result to mysql.
https://github.com/cloudtrax/docs/blob/master/api/code/php/simple_api_server_test_harness.php
function invoke_curl($method, $endpoint, $headers, $json) {
$api_server = 'https://api.cloudtrax.com';
try {
// get a curl handle then go to town on it
$ch = curl_init($api_server . $endpoint);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
if ($result == FALSE) {
if (curl_errno($ch) == 0)
echo "#### NOTE ####: nil HTTP return: This API call appears to be broken" . "\n";
else
throw new Exception(curl_error($ch), curl_errno($ch));
}
else
echo "RESULT: \n" . $result . "\n";
}
The $result shows like this:
{
"clients": {
"ssid2": 4,
"ssid1": 10
},
"rows": [
{
"time": "2016-03-23T02:45:00Z",
"ssid2": {
"traffic": {
"unclassified": {
// etc...
How can I associate each part of the result too a variable so I can then input too mysql?
It looks like this result in json format. You can use json_decode to decode it:
$resultObject = json_decode($result);
$clients = $resultObject->clients;
// ... get other data from result
The code below will convert the json into a PHP array. You can then use the indexes of the array to pull out values.
$result = json_decode($result);
$clients = $result->clients;
// Some MySQL queries
If your response is a JSON response then you can simply use php's json_decode to get parsed object.
$result = curl_exec($ch);
//to get associative array passing true
$jsonObj = json_decode($result, true);
$clients = $jsonObj['clients'];
$rows = $jsonObj['rows'];
You can refer to these answers for more detail:
Parsing JSON object in PHP using json_decode and
Parsing JSON file with PHP

Categories