50

I've been searching for about 2 hours and I can't figure it out how to read the final response uri.

In previous versions of PHP Guzzle you just call $response->getEffectiveUrl() and you get it.

I expected to have something similar in the new version so the final code looks like this:

$response = $httpClient->post('http://service.com/login', [
    'form_params' => [
        'user'   => $user,
        'padss'  => $pass,
    ]
]);

$url = $response->getEffectiveUrl();

But in the latest version $response is now a GuzzleHttp\Psr7\Response and there is no method which allow me to retrieve the uri.

I read about the redirects here (http://guzzle.readthedocs.org/en/latest/quickstart.html#redirects) but it says nothing about

UPDATE: The 6.1 version now allows you to easily do this:

https://stackoverflow.com/a/35443523/1811887

Thanks @YauheniPrakopchyk

Community
  • 1
  • 1
joserobleda
  • 788
  • 1
  • 8
  • 12

7 Answers7

75

Guzzle 6.1 solution right from the docs.

use GuzzleHttp\Client;
use GuzzleHttp\TransferStats;

$client = new Client;

$client->get('http://some.site.com', [
    'query'   => ['get' => 'params'],
    'on_stats' => function (TransferStats $stats) use (&$url) {
        $url = $stats->getEffectiveUri();
    }
])->getBody()->getContents();

echo $url; // http://some.site.com?get=params
Gras Double
  • 15,901
  • 8
  • 56
  • 54
Yauheni Prakopchyk
  • 10,202
  • 4
  • 33
  • 37
25

You can check what redirects your request had byt setting track_redirects parameter:

$client = new \GuzzleHttp\Client(['allow_redirects' => ['track_redirects' => true]]);

$response = $client->request('GET', 'http://example.com');

var_dump($response->getHeader(\GuzzleHttp\RedirectMiddleware::HISTORY_HEADER));

If there were any redirects last one should be your effective url otherewise your initial url.

You can read more about allow_redirects here http://docs.guzzlephp.org/en/latest/request-options.html#allow-redirects.

bgaluszka
  • 1,033
  • 1
  • 9
  • 11
10

I am using middleware to track requests in the redirect chain and save the last one. The uri of the last request is what you want.

Try this code:

$stack = \GuzzleHttp\HandlerStack::create();
$lastRequest = null;
$stack->push(\GuzzleHttp\Middleware::mapRequest(function (\Psr\Http\Message\RequestInterface $request) use(&$lastRequest) {
    $lastRequest = $request;
    return $request;
}));

$client = new Client([
    'handler' => $stack,
    \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => true
]);

$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org/redirect-to?url=http://stackoverflow.com');
$response = $client->send($request);
var_dump($lastRequest->getUri()->__toString());

Result:

string(24) "http://stackoverflow.com"

Example with class:

class EffectiveUrlMiddleware
{
    /**
     * @var \Psr\Http\Message\RequestInterface
     */
    private $lastRequest;

    /**
     * @param \Psr\Http\Message\RequestInterface $request
     *
     * @return \Psr\Http\Message\RequestInterface
     */
    public function __invoke(\Psr\Http\Message\RequestInterface $request)
    {
        $this->lastRequest = $request;
        return $request;
    }

    /**
     * @return \Psr\Http\Message\RequestInterface
     */
    public function getLastRequest()
    {
        return $this->lastRequest;
    }
}

$stack = \GuzzleHttp\HandlerStack::create();
$effectiveYrlMiddleware = new EffectiveUrlMiddleware();
$stack->push(\GuzzleHttp\Middleware::mapRequest($effectiveYrlMiddleware));

$client = new Client([
    'handler' => $stack,
    \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => true
]);

$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org/redirect-to?url=http://stackoverflow.com');
$response = $client->send($request);
var_dump($effectiveYrlMiddleware->getLastRequest()->getUri()->__toString());
6

I think it's best to use response headers to store this information. I wrote a simple class that saves effective url in X-GUZZLE-EFFECTIVE-URL header:

https://gist.github.com/Thinkscape/43499cfafda1af8f606d

Usage:

<?php
use GuzzleHttp\Client;
use Thinkscape\Guzzle\EffectiveUrlMiddleware;

// Add the middleware to stack and create guzzle client
$stack = HandlerStack::create();
$stack->push(EffectiveUrlMiddleware::middleware());
$client = new Client(['handler' => $stack]);

// Test it out!
$response = $client->get('http://bit.ly/1N2DZdP');
echo $response->getHeaderLine('X-GUZZLE-EFFECTIVE-URL');
Artur Bodera
  • 1,662
  • 22
  • 20
5

Accepted answer didn't work for me but led me on the way:

$client = new \GuzzleHttp\Client();
$client->request('GET', $url, [
    'on_stats' => function (\GuzzleHttp\TransferStats $stats) {
        echo($stats->getHandlerStats()['redirect_url']);
    }
]);
Sebastien Horin
  • 10,803
  • 4
  • 52
  • 54
  • Accepted answer didn't work for me, neither did this one. I fail to understand **how** they can work, as CURL requests are asynchronous and will happen after the initial PHP request has ended (so you're doing a request to some URL and echoing/getting the variable after the request is done, but display/accessing of variable is from the initial thread...). What **did** work for me is simply changing echo to using Monolog. Maybe it depends upon Guzzle setup, idk – eithed Apr 25 '18 at 10:44
  • the requests are synchronous. The next line of code is executed after the request is complete. – Flame Aug 20 '18 at 18:25
0

For Guzzle 7:

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;

$onRedirect = function(
    RequestInterface $request,
    ResponseInterface $response,
    UriInterface $uri
) {
    echo 'Redirecting! ' . $request->getUri() . ' to ' . $uri . "\n";
};

$res = $client->request('GET', '/redirect/3', [
    'allow_redirects' => [
        'max'             => 10,        // allow at most 10 redirects.
        'strict'          => true,      // use "strict" RFC compliant redirects.
        'referer'         => true,      // add a Referer header
        'protocols'       => ['https'], // only allow https URLs
        'on_redirect'     => $onRedirect,
        'track_redirects' => true
    ]
]);

echo $res->getStatusCode();
// 200

echo $res->getHeaderLine('X-Guzzle-Redirect-History');
// http://first-redirect, http://second-redirect, etc...

echo $res->getHeaderLine('X-Guzzle-Redirect-Status-History');
// 301, 302, etc...
insign
  • 5,353
  • 1
  • 38
  • 35
-1

I'm not an expert in the subject but, from what I understand, the effective url is not something that is defined in a general HTTP message. I think is is something related to Curl and since Guzzle can use any HTTP handler to send requests (see here), the information is not necessarily present.

marcosh
  • 8,780
  • 5
  • 44
  • 74
  • I see, but there should be a mechanism, whatever, which allows me to get that informatión, isn't it? – joserobleda Jun 07 '15 at 15:26
  • you could probably use something like this: http://php.net/manual/en/function.curl-getinfo.php, or maybe try to open an issue in the guzzle repository and ask for help there – marcosh Jun 08 '15 at 08:04
  • 1
    For now I can disable redirects and read the Location header, but that's only valid in my current scenario. When you get more redirections this is not an option. – joserobleda Jun 08 '15 at 15:23