Guzzle 6 - Get request total time - php

I'm searching to retrieve the request total time in Guzzle 6, just after a simple GET request :
$client = new GuzzleHttp\Client();
$response = client->get('http://www.google.com/');
But can't find anything in the docs about that. Any idea ?
Thanks a lot.

In Guzzle 6.1.0 You can use the 'on_stats' request option to get transfer time etc.
More information can be found at Request Options - on_stats
https://github.com/guzzle/guzzle/releases/tag/6.1.0

You can use setter and getter.
private $totaltime = 0;
public function getTotaltime(){
return $this->totaltime;
}
public function setTotaltime($time){
$this->totaltime = $time;
}
$reqtime= new self();
$response = $client->post($endpointLogin, [
'json' => $payload,
'headers' => $this->header,
'on_stats' => function (TransferStats $stats) use ($reqtime) {
$stats->getTransferTime();
//** set it here **//
$reqtime->setTotaltime($stats->getTransferTime());
}
]);
dd($reqtime->getTotaltime());

An specific example based on the #Michael post.
$client = new GuzzleHttp\Client();
$response = $client->get('http://www.google.com/', [
'on_stats' => function (\GuzzleHttp\TransferStats $stats) {
echo $stats->getEffectiveUri() . ' : ' . $stats->getTransferTime();
}
]);

$client = new GuzzleHttp\Client();
$one = microtime(1);
$response = $client->get('http://www.google.com/');
$two = microtime(1);
echo 'Total Request time: '. ( $two - $one );

I had a similar problem although it's still Guzzle 5.3.
See Guzzle 5.3 - Get request duration for asynchronous requests
Maybe listening to an event in Guzzle6 and retrieving the TransferInfo will do the trick for you too.
This works for synchronous and asynchronous requests alike.

Related

Are all existing endpoints listed in documentation really still working for v4.9? (i.e. those not replaced by v1 so far)

I have tried using old v4.9 endpoints that haven't been replaced by v1 so far such as:
https://developers.google.com/my-business/reference/rest/v4/accounts.locations/reportInsights
https://developers.google.com/my-business/reference/rest/v4/accounts.locations.reviews
However, none of these endpoints work anymore.
I am using PHP client that had these endpoints missing, but using the official v4.9 library listed here: https://developers.google.com/my-business/samples/previousVersions I have been able to reach some of the old endpoints such as reviews.
However they no longer return any data or data object is empty.
Anyone has experienced similar issues?
The v4.9 (not yet deprecated) endpoints such as reviews, insights etc. are working, but the official library is broken and botched.
I had to code a replacement using Guzzle client reaching to the endpoints directly instead. So you need to code the API library yourself from scratch for these v4.9 endpoints as the official library does not work.
How to fetch reviews:
public static function listReviews($client, $params, $account, $location)
{
$response = $client->authorize()->get('https://mybusiness.googleapis.com/v4/' . $account . '/' . $location . '/reviews', ['query' => $params]);
return json_decode((string) $response->getBody(), false);
}
How to fetch insights:
/** v4.9 working 02/2022 **/
public static function reportInsights($client, $params, $account)
{
try {
$response = $client->authorize()->post('https://mybusiness.googleapis.com/v4/' . $account . '/locations:reportInsights', [
\GuzzleHttp\RequestOptions::JSON => $params,
]);
} catch (\GuzzleHttp\Exception\RequestException $ex) {
return $ex->getResponse()->getBody()->getContents();
}
return json_decode((string) $response->getBody(), false);
}
How to prepare payload for insights:
$params = new \stdClass();
$params->locationNames = $account->name . '/' . $location->name;
$time_range = new \stdClass();
$time_range->startTime = Carbon::parse('3 days ago 00:00:00')->toISOString();
$time_range->endTime = Carbon::parse('2 days ago 00:00:00')->toISOString();
if ($force == 'complete') {
$time_range->startTime = Carbon::parse('17 months ago 00:00:00')->toIso8601ZuluString();
$time_range->endTime = Carbon::parse('3 days ago 00:00:00')->toIso8601ZuluString();
}
$params->basicRequest = new \stdClass();
$params->basicRequest->timeRange = $time_range;
$params->basicRequest->metricRequests = new \stdClass();
$metric_request = new \stdClass();
$metric_request->metric = 'ALL';
$metric_request->options = ['AGGREGATED_DAILY'];
$params->basicRequest->metricRequests = [
$metric_request,
];
Note: if you are getting empty insights response, you have to check verification using new v1 API call such as:
$verifications = \Google_Service_MyBusinessVerifications($client)->locations_verifications->listLocationsVerifications($location->getName());
$verification = '0';
if ($verifications->getVerifications()) {
$verification = $verifications->getVerifications()[0]->getState();
}
Using official API client with an existing token (needs to be fetched via OAuth2):
$provider = new GoogleClientServiceProvider(true);
$client = $provider->initializeClient($known_token, ['https://www.googleapis.com/auth/plus.business.manage', 'https://www.googleapis.com/auth/drive']);

Guzzle does not send a post request

i'm using PHP with Guzzle.
I have this code:
$client = new Client();
$request = new \GuzzleHttp\Psr7\Request('POST', 'http://localhost/async-post/tester.php',[
'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
'form_params' => [
'action' => 'TestFunction'
],
]);
$promise = $client->sendAsync($request)->then(function ($response) {
echo 'I completed! ' . $response->getBody();
});
$promise->wait();
For some reason Guzzle Doesn't send the POST Parameters.
Any suggestion?
Thanks :)
I see 2 things.
The parameters have to go as string (json_encode)
And you were also including them as part of the HEADER, not the BODY.
Then i add a function to handle the response as ResponseInterface
$client = new Client();
$request = new Request('POST', 'https://google.com', ['Content-Type' => 'application/x-www-form-urlencoded'], json_encode(['form_params' => ['s' => 'abc',] ]));
/** #var Promise\PromiseInterface $response */
$response = $client->sendAsync($request);
$response->then(
function (ResponseInterface $res) {
echo $res->getStatusCode() . "\n";
},
function (RequestException $e) {
echo $e->getMessage() . "\n";
echo $e->getRequest()->getMethod();
}
);
$response->wait();
In this test Google responds with a
Client error: POST https://google.com resulted in a 405 Method Not Allowed
But is ok. Google doesn't accepts request like this.
Guzzle isn't truely asynchronous. It's more of multi-threading. That is why you have the wait() line to prevent the your current PHP script from closing until all multiple spun threads finish. If you remove the wait() line, the PHP process spun by the script ends immediately with all it's threads and your request is never sent.
Ergo, you need Guzzle (and Curl) for multi-processing(concurrent) I/O and not for asynchronous I/O. In your case, you are processing one request and Guzzle promises are simply an overkill.
To send a request with Guzzle, simply do this:
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
$client = new Client();
$header = ['Content-Type' => 'application/x-www-form-urlencoded'];
$body = json_encode(['id' => '2', 'name' => 'dan']);
$request = new Request('POST', 'http://localhost/async-post/tester.php', $header, $body);
$response = $client->send($request);
Also, it seems you are using the form action attribute rather than the actual form data in form-params.
I'm posting this answer because I tried to achieve something really asynchronous with php - Schedule I/O processing as a background task, continue processing script and serve the page; I/O continues in background and completes without disrupting the client. Laravel Queues was the best thing I could find.

PHPUnit and mock request from Guzzle

I have a class with the following function :
public function get(string $uri) : stdClass
{
$this->client = new Client;
$response = $this->client->request(
'GET',
$uri,
$this->headers
);
return json_decode($response->getBody());
}
How can I mock the request method from PHPUnit? I tried different ways but it always tries to connect to the uri specified.
I tried with :
$clientMock = $this->getMockBuilder('GuzzleHttp\Client')
->setMethods('request')
->getMock();
$clientMock->expects($this->once())
->method('request')
->willReturn('{}');
But this didn't work. What can I do? I just need to mock the response to be empty.
Thanks
PD : Client comes from (use GuzzleHttp\Client)
I think as suggested is better to use http://docs.guzzlephp.org/en/stable/testing.html#mock-handler
as it looks like the most elegant way to do it properly.
Thank you all
The mocked Response doesn't need to be anything in particular, your code just expects it to be an object with a getBody method. So you can just use a stdClass, with a getBody method which returns some json_encoded object. Something like:
$jsonObject = json_encode(['foo']);
$uri = 'path/to/foo/bar/';
$mockResponse = $this->getMockBuilder(\stdClass::class)->getMock();
$mockResponse->method('getBody')->willReturn($jsonObject);
$clientMock = $this->getMockBuilder('GuzzleHttp\Client')->getMock();
$clientMock->expects($this->once())
->method('request')
->with(
'GET',
$uri,
$this->anything()
)
->willReturn($mockResponse);
$result = $yourClass->get($uri);
$expected = json_decode($jsonObject);
$this->assertSame($expected, $result);
I prefer this way to mock a Client in PHP. In this example I am using Guzzle Client.
Clone the code or install it via composer
$ composer require doppiogancio/mocked-client
And then...
$builder = new HandlerStackBuilder();
// Add a route with a response via callback
$builder->addRoute(
'GET', '/country/IT', static function (ServerRequestInterface $request): Response {
return new Response(200, [], '{"id":"+39","code":"IT","name":"Italy"}');
}
);
// Add a route with a response in a text file
$builder->addRouteWithFile('GET', '/country/IT/json', __DIR__ . '/fixtures/country.json');
// Add a route with a response in a string
$builder->addRouteWithFile('GET', '{"id":"+39","code":"IT","name":"Italy"}');
// Add a route mocking directly the response
$builder->addRouteWithResponse('GET', '/admin/dashboard', new Response(401));
$client = new Client(['handler' => $builder->build()]);
Once you have mocked the client you can use it like this:
$response = $client->request('GET', '/country/DE/json');
$body = (string) $response->getBody();
$country = json_decode($body, true);
print_r($country);
// will return
Array
(
[id] => +49
[code] => DE
[name] => Germany
)
In addition to the current answer about using MockHandler, it's possible to process the request so that you can validate the calls.
The following example passes a callable which just tests the request method and throws an exception if not POST, if that is OK it returns the response. The principle can be expanded to test other details about the request...
$mock = new MockHandler([
function ($request) {
$this->assertEquals('POST', $request->getMethod());
return new Response(
200,
[],
json_encode([ "access_token" => '1234e' ])
);
},
new Response(
200,
[],
json_encode([ "details" =>
[
[
"orderID" => 229783,
],
[
"orderID" => 416270,
],
],
])
),
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
So the first call to the client has the test included, the second call just returns a response.
Just noticed that any time you use a callable to process the request, you MUST return a Response object if you expect the process to continue.

GuzzlePHP mock response content

I want to mock a response to the Guzzle request:
$response = new Response(200, ['X-Foo' => 'Bar']);
//how do I set content of $response to--> "some mocked content"
$client = Mockery::mock('GuzzleHttp\Client');
$client->shouldReceive('get')->once()->andReturn($response);
I noticed I need to add as third parameter the interface:
GuzzleHttp\Stream\StreamInterface
but there are so many implementations of it, and I want to return a simple string. Any ideas?
Edit: now I use this:
$response = new Response(200, [], GuzzleHttp\Stream\Stream::factory('bad xml here'));
but when I check this:
$response->getBody()->getContents()
I get an empty string. Why is this?
Edit 2: this happened to me only when I used xdebug, when it runs normally it works great!
We'll just keep doing this. The previous answer is for Guzzle 5, this is for Guzzle 6:
use GuzzleHttp\Psr7;
$stream = Psr7\stream_for('{"data" : "test"}');
$response = new Response(200, ['Content-Type' => 'application/json'], $stream);
The previous answer is for Guzzle 3. Guzzle 5 uses the following:
<?php
$body = GuzzleHttp\Stream\Stream::factory('some mocked content');
$response = new Response(200, ['X-Foo' => 'Bar'], $body);
Using #tomvo answer and the comment from #Tim - this is what I did for testing Guzzle 6 inside my Laravel app:
use GuzzleHttp\Psr7\Response;
$string = json_encode(['data' => 'test']);
$response = new Response(200, ['Content-Type' => 'application/json'], $string);
$guzzle = Mockery::mock(GuzzleHttp\Client::class);
$guzzle->shouldReceive('get')->once()->andReturn($response);
Guzzle\Http\Message\Response allows you to specify the third parameter as a string.
$body = '<html><body>Hello world!</body></html>';
$response = new Response(200, ['X-Foo' => 'Bar'], $body);
If you'd prefer a solution that implements Guzzle\Stream\StreamInterface, then I recommend using Guzzle\Http\EntityBody for the most straightforward implementation:
$body = Guzzle\Http\EntityBody::fromString('<html><body>Hello world!</body></html>');
$response = new Response(200, ['X-Foo' => 'Bar'], $body);
For Guzzle 7, you can use the GuzzleHttp\Psr7\Utils::streamFor() method as follows:
$data = json_encode(['X-Foo' => 'Bar']);
$stream = Utils::streamFor($data);
And then you can pass the $stream object to the andReturn method of the mocked client.

How to dynamically add extra requests to a Guzzle Pool?

I'm using Guzzle (http://guzzlephp.org) to GET a large number of urls (~300k) . The urls are retrieved from an Elastic Search instance, and I would like to keep adding urls to a Pool so the Pool stays rather small instead of adding them all at once.
Is this possible? I looked at the Pool.php, but did not find a way to do this. Is there a way?
Use while and generator (yield).
$client = new GuzzleHttp\Client();
$client = new Client();
$requests = function () {
$uris = ['http://base_url'];
$visited_uris = []; // maybe database instead of array
while(len($uris)>0)
yield new Request('GET', array_pop($uris));
}
};
$pool = new Pool($client, $requests(), [
'concurrency' => 5,
'fulfilled' => function ($response, $index) {
$new_uri = get_new_uri(); // implement function to get new $uri
if(in_array($new_uri, $visited_uris)) {
array_push($uris, $uri);
}
array_push($visited_uris, $uri);
}
]);
$promise = $pool->promise();
$promise->wait();

Categories