XF 2.1 Guzzle Change Information

Snog

Well-known member
If you're using Guzzle to get json information from another site, you need to do this in XenForo 2.1...

As an object:
Code:
$values = \GuzzleHttp\json_decode($response->getBody());

As an array:
Code:
$values = \GuzzleHttp\json_decode($response->getBody(), true);
 
So If I'm using this:
PHP:
$request = $client->get(self::DATA_URL);
$data = (string) $request->getBody();
$response = json_decode($data, true);

I just need to do this?
PHP:
$request = $client->get(self::DATA_URL);
$data = (string) $request->getBody();
$response = \GuzzleHttp\json_decode($data, true);
 
If you're getting a file (as it appears from the string modifier) keep using the first one.

Normally I used $response->json(); which WAS a Guzzle function, but because Guzzle changed I switched to \GuzzleHttp\json_decode($response->getBody(), true);.

If you're getting a json response you only need $response = \GuzzleHttp\json_decode($response->getBody(), true);. So it would be...

Code:
$request = $client->get(self::DATA_URL);
$response = \GuzzleHttp\json_decode($request->getBody(), true);

That will put the json response into an array.
 
Is there an advantage to doing:
Code:
\GuzzleHttp\json_decode($response->getBody(), true);
over just...
Code:
json_decode($response->getBody(), true);
 
None that I know of other than keeping with the Developer guidelines and using functions built in to XenForo.

My IDE (PHP Storm) automatically selected the Guzzle function when I used json_decode on XF 2.1. ;)
 
Just for people who don't know this, I'll add that you don't need to write separate add-ons for 2.0 and 2.1...

Code:
if(\XF::options()->currentVersionId >= '2010031')
{
    $yourInfo = \GuzzleHttp\json_decode($response->getBody(), true);
}else{
    $yourInfo = $response->json();
}
 
If you're getting a file (as it appears from the string modifier) keep using the first one.

Normally I used $response->json(); which WAS a Guzzle function, but because Guzzle changed I switched to \GuzzleHttp\json_decode($response->getBody(), true);.

If you're getting a json response you only need $response = \GuzzleHttp\json_decode($response->getBody(), true);. So it would be...

Code:
$request = $client->get(self::DATA_URL);
$response = \GuzzleHttp\json_decode($request->getBody(), true);

That will put the json response into an array.

I'm afraid I'm getting an error with the change you provided. Do you have any ideas? Thank you!

Code:
GuzzleHttp\Exception\ServerException: Error gathering data Server error: `GET https://api.developertracker.com/anthem/posts` resulted in a `502 Bad Gateway` response: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML><HEAD><META (truncated...) src\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php:113
Generated by: Unknown account Nov 22, 2018 at 9:39 AM

Stack trace
#0 src\vendor\guzzlehttp\guzzle\src\Middleware.php(66): GuzzleHttp\Exception\RequestException::create(Object(GuzzleHttp\Psr7\Request), Object(GuzzleHttp\Psr7\Response))
#1 src\vendor\guzzlehttp\promises\src\Promise.php(203): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Response))
#2 src\vendor\guzzlehttp\promises\src\Promise.php(156): GuzzleHttp\Promise\Promise::callHandler(1, Object(GuzzleHttp\Psr7\Response), Array)
#3 src\vendor\guzzlehttp\promises\src\TaskQueue.php(47): GuzzleHttp\Promise\Promise::GuzzleHttp\Promise\{closure}()
#4 src\vendor\guzzlehttp\promises\src\Promise.php(246): GuzzleHttp\Promise\TaskQueue->run(true)
#5 src\vendor\guzzlehttp\promises\src\Promise.php(223): GuzzleHttp\Promise\Promise->invokeWaitFn()
#6 src\vendor\guzzlehttp\promises\src\Promise.php(267): GuzzleHttp\Promise\Promise->waitIfPending()
#7 src\vendor\guzzlehttp\promises\src\Promise.php(225): GuzzleHttp\Promise\Promise->invokeWaitList()
#8 src\vendor\guzzlehttp\promises\src\Promise.php(62): GuzzleHttp\Promise\Promise->waitIfPending()
#9 src\vendor\guzzlehttp\guzzle\src\Client.php(131): GuzzleHttp\Promise\Promise->wait()
#10 src\vendor\guzzlehttp\guzzle\src\Client.php(89): GuzzleHttp\Client->request('get', 'https://api.dev...', Array)
#11 src\addons\AH\DevTracker\Repository\Entry.php(65): GuzzleHttp\Client->__call('get', Array)
#12 src\addons\AH\DevTracker\Cron\DevTracker.php(15): AH\DevTracker\Repository\Entry->importDevData()
#13 [internal function]: AH\DevTracker\Cron\DevTracker::run(Object(XF\Entity\CronEntry))
#14 src\XF\Job\Cron.php(35): call_user_func(Array, Object(XF\Entity\CronEntry))
#15 src\XF\Job\Manager.php(253): XF\Job\Cron->run(G)
#16 src\XF\Job\Manager.php(195): XF\Job\Manager->runJobInternal(Array, G)
#17 src\XF\Job\Manager.php(79): XF\Job\Manager->runJobEntry(Array, G)
#18 job.php(42): XF\Job\Manager->runQueue(false, 8)
#19 {main}

Request state
array(4) {
  ["url"] => string(8) "/job.php"
  ["referrer"] => string(26) "http://localhost/index.php"
  ["_GET"] => array(0) {
  }
  ["_POST"] => array(0) {
  }
}
 
That's nothing to do with your code, that's an error coming from the request.

Thanks Chris. That makes sense, because my add-on on my production site stopped getting new data around 2 days ago, but if I go to the json url, I can see that there's new data. But I'm not getting any errors on my production site, so that's weird.
 
Actually if I go to https://api.developertracker.com/anthem/posts' (with the single quote at the end), I get this:

188691

The server error I posted above shows a 502 error. I double checked in my code to see if I added a single quote by mistake, but there isn't one, which you can see here: const DATA_URL = 'https://api.developertracker.com/anthem/posts';

I'm at a loss as for what's going on.
 
Interestingly enough, this code works for me without a problem in XF 2.1:

Code:
try
{
    $client = \XF::app()->http()->client(['headers' => ['Accept' => 'application/json']]);
    $response = $client->get("https://api.developertracker.com/anthem/posts");
}
catch (\GuzzleHttp\Exception\RequestException $e)
{
    if (null !== $e->getResponse())
    {
        $error = 'Tracker Error ' . $e->getResponse()->getStatusCode() . ': ' . $e->getResponse()->getReasonPhrase();
    }
    else
    {
        $error = $e->getMessage();
    }

    return $this->error($error);
}

$yourInfo = \GuzzleHttp\json_decode($response->getBody(), true);
print_r($yourInfo);
die();

Result:
Code:
Array
(
    [data] => Array
        (
            [0] => Array
                (
                    [content] => <blockquote><div><b><a href="https://twitter.com/billigtgarn/status/1065588516980211713/">@billigtgarn</a></b></div><a href="https://twitter.com/devilkitten">@devilkitten</a> Möjligen om jutevävssäcken har två "parallella linjer från halsen till fållen fram och bak". Måste dela upp bredden!</blockquote><a href="https://twitter.com/billigtgarn">@billigtgarn</a> Ja gudbevare sig från breda damer!!!
                    [id] => arrrnega
                    [section] =>
                    [timestamp] => 1542891125
                    [topic] => tweeted
                    [topicUrl] =>
                    [url] => https://twitter.com/devilkitten/status/1065588730855976960/
                    [urlHash] => 50b479ba6fd64c4a28e2fc11e5e72fa9bcf2f558
                    [account] => Array
                        (
                            [identifier] => devilkitten
                            [service] => Twitter
                            [developer] => Array
                                (
                                    [group] => BioWare
                                    [name] =>
                                    [nick] => Imperator Furiåsa
                                    [role] => UX Designer
                                )

                        )

                )

            [1] => Array
                (.....
 
Last edited:
Just for people who don't know this, I'll add that you don't need to write separate add-ons for 2.0 and 2.1...
Honestly, going forward I'm requiring 2.1 for all my future updates anyways. The share/bookmark/reaction functions are just too useful to ignore.
 
Honestly, going forward I'm requiring 2.1 for all my future updates anyways. The share/bookmark/reaction functions are just too useful to ignore.
Same, especially given how Composer dependencies are sneaking into a lot of my mods these days. Anything but the smallest mods where no changes are needed is getting a minver bump.


Fillip
 
Interestingly enough, this code works for me without a problem in XF 2.1:

Code:
try
{
    $client = \XF::app()->http()->client(['headers' => ['Accept' => 'application/json']]);
    $response = $client->get("https://api.developertracker.com/anthem/posts");
}
catch (\GuzzleHttp\Exception\RequestException $e)
{
    if (null !== $e->getResponse())
    {
        $error = 'Tracker Error ' . $e->getResponse()->getStatusCode() . ': ' . $e->getResponse()->getReasonPhrase();
    }
    else
    {
        $error = $e->getMessage();
    }

    return $this->error($error);
}

$yourInfo = \GuzzleHttp\json_decode($response->getBody(), true);
print_r($yourInfo);
die();

Result:
Code:
Array
(
    [data] => Array
        (
            [0] => Array
                (
                    [content] => <blockquote><div><b><a href="https://twitter.com/billigtgarn/status/1065588516980211713/">@billigtgarn</a></b></div><a href="https://twitter.com/devilkitten">@devilkitten</a> Möjligen om jutevävssäcken har två "parallella linjer från halsen till fållen fram och bak". Måste dela upp bredden!</blockquote><a href="https://twitter.com/billigtgarn">@billigtgarn</a> Ja gudbevare sig från breda damer!!!
                    [id] => arrrnega
                    [section] =>
                    [timestamp] => 1542891125
                    [topic] => tweeted
                    [topicUrl] =>
                    [url] => https://twitter.com/devilkitten/status/1065588730855976960/
                    [urlHash] => 50b479ba6fd64c4a28e2fc11e5e72fa9bcf2f558
                    [account] => Array
                        (
                            [identifier] => devilkitten
                            [service] => Twitter
                            [developer] => Array
                                (
                                    [group] => BioWare
                                    [name] =>
                                    [nick] => Imperator Furiåsa
                                    [role] => UX Designer
                                )

                        )

                )

            [1] => Array
                (.....

Thank you Snog, that worked perfectly.
 
Updated code:

Code:
try
{
    $client = \XF::app()->http()->client();
    $response = $client->get("https://api.developertracker.com/anthem/posts", ['headers' => ['Accept' => 'application/json']]);
}
catch (\GuzzleHttp\Exception\RequestException $e)
{
    if (null !== $e->getResponse())
    {
        $error = 'Tracker Error ' . $e->getResponse()->getStatusCode() . ': ' . $e->getResponse()->getReasonPhrase();
    }
    else
    {
        $error = $e->getMessage();
    }

    return $this->error($error);
}

$yourInfo = \GuzzleHttp\json_decode($response->getBody(), true);
print_r($yourInfo);
die();
 
Hey Snog, doing that gives the following error:

Code:
Error: Cannot use object of type GuzzleHttp\Psr7\Response as array in src\addons\AH\DevTracker\Repository\Entry.php at line 65
AH\DevTracker\Repository\Entry->importDevData() in src\addons\AH\DevTracker\Cron\DevTracker.php at line 15
AH\DevTracker\Cron\DevTracker::run()
call_user_func() in src\XF\Admin\Controller\CronEntry.php at line 112
XF\Admin\Controller\CronEntry->actionRun() in src\XF\Mvc\Dispatcher.php at line 321
XF\Mvc\Dispatcher->dispatchClass() in src\XF\Mvc\Dispatcher.php at line 248
XF\Mvc\Dispatcher->dispatchFromMatch() in src\XF\Mvc\Dispatcher.php at line 100
XF\Mvc\Dispatcher->dispatchLoop() in src\XF\Mvc\Dispatcher.php at line 50
XF\Mvc\Dispatcher->run() in src\XF\App.php at line 2170
XF\App->run() in src\XF.php at line 392
XF::runApp() in admin.php at line 13
 
Hey Snog, doing that gives the following error:

Code:
Error: Cannot use object of type GuzzleHttp\Psr7\Response as array in src\addons\AH\DevTracker\Repository\Entry.php at line 65
AH\DevTracker\Repository\Entry->importDevData() in src\addons\AH\DevTracker\Cron\DevTracker.php at line 15
AH\DevTracker\Cron\DevTracker::run()
call_user_func() in src\XF\Admin\Controller\CronEntry.php at line 112
XF\Admin\Controller\CronEntry->actionRun() in src\XF\Mvc\Dispatcher.php at line 321
XF\Mvc\Dispatcher->dispatchClass() in src\XF\Mvc\Dispatcher.php at line 248
XF\Mvc\Dispatcher->dispatchFromMatch() in src\XF\Mvc\Dispatcher.php at line 100
XF\Mvc\Dispatcher->dispatchLoop() in src\XF\Mvc\Dispatcher.php at line 50
XF\Mvc\Dispatcher->run() in src\XF\App.php at line 2170
XF\App->run() in src\XF.php at line 392
XF::runApp() in admin.php at line 13
Try removing this...
, ['headers' => ['Accept' => 'application/json']]

With my original example, the headers weren't being sent anyway. So removing it from the request should end up with the same result. ;)

EDIT: Yep, removing that works. :D
I discovered the headers weren't being sent here..

But you don't need them for your purpose.
 
Last edited:
Top Bottom