enisz / igdb

IGDB PHP API Wrapper
GNU General Public License v3.0
27 stars 8 forks source link

_exec_query returns object, not array #14

Closed sparcbr closed 3 years ago

sparcbr commented 3 years ago

_exec_query() is returning an object, not an array..

to fix, add true as second argument to json_decode: $result = json_decode(curl_exec($this->curl_handler), true);

enisz commented 3 years ago

Hi @sparcbr ,

Did you face any specific issue? Do you have an example code?

The method _exec_query() is a private method and it is working as it should. It does return an array. An array of objects actually. Check out this example:

<?php

    $igdb = new IGDB($client_id, $access_token);

    // Example query with zero results
    var_dump(
        $igdb->game(
            array(
                "search" => "abcdefghijklm",
                "fields" => array("id", "name")
            )
        )
    );

    // Example query with 1 result
    var_dump(
        $igdb->game(
            array(
                "search" => "uncharted 4",
                "fields" => array("id", "name"),
                "limit" => 1
            )
        )
    );

    // Example query with 2 results
    var_dump(
        $igdb->game(
            array(
                "search" => "uncharted 4",
                "fields" => array("id", "name"),
                "limit" => 2
            )
        )
    );

?>

This script executes 3 different queries with 0, 1 and 2 items in their results. The output of the script is:

C:\wamp64\www\igdb\index.php:16:
array (size=0)
  empty

C:\wamp64\www\igdb\index.php:25:
array (size=1)
  0 => 
    object(stdClass)[2]
      public 'id' => int 7331
      public 'name' => string 'Uncharted 4: A Thief's End' (length=26)

C:\wamp64\www\igdb\index.php:35:
array (size=2)
  0 => 
    object(stdClass)[2]
      public 'id' => int 7331
      public 'name' => string 'Uncharted 4: A Thief's End' (length=26)
  1 => 
    object(stdClass)[3]
      public 'id' => int 136686
      public 'name' => string 'Uncharted 4: A Thief's End - Deluxe Edition' (length=43)

You can see in case of any number of results, the return value of the endpoint method is always an array. No matter how many matches are in the results.

h4eDashboard commented 3 years ago

Hello, unfortunately I have the same problem.

$igdb = new IGDB("key", "secret"); $result = $igdb->game( array( "search" => "abcdefghijklm", "fields" => array("id", "name") ) );

var_dump($result);

PHP then reports:

Fatal error: Uncaught Error: Cannot use object of type stdClass as array in igdb.php:314 Stack trace: #0 igdb.php(688): IGDB->_exec_query('https://api.igd...', Array) #1 {main} thrown in igdb.php on line 314

If I change line 305 as sparcbr wrote, the error is eliminated afterwards.

enisz commented 3 years ago

I did some investigation on this issue and I found that it seems the guys at IGDB has changed the the way the endpoint responds in case of errors. Changing the code a bit, printing the contents of the$result variable in the _exec_query method like this:

// Executing the request
$result = json_decode(curl_exec($this->curl_handler));

// Getting request information
$this->request_info = curl_getinfo($this->curl_handler);

// If there were errors
if($this->request_info['http_code'] != 200) {
    var_dump($result); // a var_dump here to see what response we got from IGDB, removed the rest of the response processing
    throw new Exception('Error ' . $this->request_info['http_code'] . ': ' . $result->message . ": " . $result->Docs);
}

Using this example code:

<?php

    require_once "src/class.igdb.php";

    // invalid key and access token
    $igdb = new IGDB("key", "secret");

    try {
        $result = $igdb->game(
            array(
                "search" => "abcdefghijklm",
                "fields" => array("id", "name")
            )
        );

        var_dump($result);
    } catch (Exception $e) {
        echo $e->getMessage();
    }

?>

Will result this output:

C:\wamp64\www\igdb-original\src\class.igdb.php:312:
object(stdClass)[2]
  public 'message' => string 'Authorization Failure. Have you tried:' (length=38)
  public 'Tip 1' => string 'Ensure you are sending Authorization and Client-ID as headers.' (length=62)
  public 'Tip 2' => string 'Ensure Authorization value starts with 'Bearer ', including the space' (length=69)
  public 'Tip 3' => string 'Ensure Authorization value ends with the App Access Token you generated, NOT your Client Secret.' (length=96)
  public 'Docs' => string 'https://api-docs.igdb.com/#authentication' (length=41)
  public 'Discord' => string 'https://discord.gg/FrvfwQg' (length=26)
Error 401: Authorization Failure. Have you tried:: https://api-docs.igdb.com/#authentication

This is an object indeed, and this is why the parsing throws error. Executing the request from postman I will get this result:

{
    "message": "Authorization Failure. Have you tried:",
    "Tip 1": "Ensure you are sending Authorization and Client-ID as headers.",
    "Tip 2": "Ensure Authorization value starts with 'Bearer ', including the space",
    "Tip 3": "Ensure Authorization value ends with the App Access Token you generated, NOT your Client Secret.",
    "Docs": "https://api-docs.igdb.com/#authentication",
    "Discord": "https://discord.gg/FrvfwQg"
}

So it seems the problem is, that the failed request is recognised without an issue (catching the error code 401) but parsing the response should be updated for this new format.

@h4eDashboard , your example must have been executed without proper client id and access token. When I executed the same code I got the error message you mentioned, but when I replaced the "key" and "secret" strings with my valid credentials, the game method returned with a simple empty array.

C:\wamp64\www\igdb-original\index.php:20:
array (size=0)
  empty

Can you confirm this?

h4eDashboard commented 3 years ago

With the changed code, the error message "Authorization Failure" now appears.

However, I get a PHP error again with full login data that was up before the change you described:

I read a list of games, write the result to $result and then want to check directly if isset($result[0]["name"]). At this point PHP reports again Fatal error: Uncaught Error: Cannot use object of type stdClass as array. So according to PHP the return of $igdb->game($query) is still not an array. The error disappears only as soon as the command in the class json_decode(curl_exec($this->curl_handler), true); means, without comes perma the message that it is not an array.

h4eDashboard commented 3 years ago

// Executing the request $result = json_decode(curl_exec($this->curl_handler), true);

This way the error will no longer appear.

enisz commented 3 years ago

Okay, I think I can see the problem now. You are trying to access the data using $result[0]["name"]. This is incorrect. This way you are trying to access an element in an associative array. The response from the endpoint methods are - if count is not present - always array of objects. Let's check this code for example:

<?php

    require_once "src/class.igdb.php";

    $client_id = "xxx";
    $access_token = "xxx";

    try {
        $result = (new IGDB($client_id, $access_token))->game(
            array(
                "search" => "uncharted",
                "fields" => array("id", "name"),
                "limit" => 3
            )
        );

        var_dump($result);

        echo $result[0]->name; // this is the proper way of accessing properties
    } catch (Exception $e) {
        echo $e->getMessage();
    }

?>

The output of this command code is:

C:\wamp64\www\igdb-original\index.php:18:
array (size=3)
  0 => 
    object(stdClass)[2]
      public 'id' => int 126126
      public 'name' => string 'Uncharted Ocean' (length=15)
  1 => 
    object(stdClass)[3]
      public 'id' => int 82008
      public 'name' => string 'Uncharted Waters' (length=16)
  2 => 
    object(stdClass)[4]
      public 'id' => int 4459
      public 'name' => string 'Uncharted Waters' (length=16)
Uncharted Ocean

The var_dump here shows that the the $result is an array with objects in it, and you can access the first elements' name property with the object operator: $result[0]->name.

The php documentation on object properties: https://www.php.net/manual/en/language.oop5.properties.php

If you try to access it like $result[0]["name"] it will throw an exception saying: Fatal error: Cannot use object of type stdClass as array.

h4eDashboard commented 3 years ago

Oh, I must have been dreaming, thanks for pointing that out.

enisz commented 3 years ago

The changes are merged to the master branch, version 4.0.2 contains the fix for this issue.