I'm trying to do long polling with reactphp.
I have a function getNotifications that stay in long polling waiting a response from Telegram.
This telegram api can hold the request open until timeout or
can send a response before the end of the 50 seconds if there is a notification.
How I can recall getNotifications after a response from Telegram?
Basically I want that getNotifications is called again when there is a response.
This is my code, thank you all
<?php
require "vendor/autoload.php";
use Clue\React\Buzz\Browser;
use Psr\Http\Message\ResponseInterface;
use React\EventLoop\LoopInterface;
$loop = React\EventLoop\Factory::create();
$browser = new Browser($loop);
$method = "getUpdates";
$timeout = 50;
$params = [
"offset" => 550495530,
"limit" => 1000,
"timeout" => $timeout
];
$bot_key = "your bot token";
$query = http_build_query($params);
$url = "https://api.telegram.org/bot" . $bot_key . "/" . $method . "?" . $query;
$browser = $browser->withOptions(array(
'timeout' => $timeout
));
function callback(){
echo "done";
}
function getNotifications($url, $browser, LoopInterface $loop){
$browser->get($url)->then(function (ResponseInterface $response) {
// response received within 50 seconds. Telegram longpolling
var_dump((string)$response->getBody());
callback();
});
}
function timer($start, LoopInterface $loop)
{
//Timer only used to count seconds
$loop->addPeriodicTimer(1.0, function ($timer) use (&$start, $loop) {
echo "tick ". $start++ . "\n";
});
}
timer(0, $loop);
getNotifications($url, $browser, $loop);
$loop->run();
Ok I find the solution. To pass parameter to the anonymous function I need to use the use keyword. Then inside the function I can access correctly the variables.
function getNotifications($url, $browser, LoopInterface $loop){
$browser->get($url)->then(function (ResponseInterface $response) use ($url, $browser,$loop) {
// response received within 50 seconds. Telegram longpolling
var_dump((string)$response->getBody());
callback();
getNotifications($url, $browser,$loop);
});
}
Related
test.php
<?php
//simulate different blocking times
if(isset($_GET['appid'])) {
sleep(rand(3, 10));
echo $_GET['appid'];
exit;
}
client.php
<?php
require './vendor/autoload.php';
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Client;
$client = new Client();
$promise = null;
$loop = function ($appId) use($client, &$loop, &$promise) {
$promise = $client->getAsync("http://127.0.0.1/test.php?appid={$appId}");
$promise->then(
function (ResponseInterface $res) use(&$loop, $appId) {
echo $res->getBody();
//After completing the current request, initiate the request again to simulate EventLoop
$loop($appId);
},
function (RequestException $e) use(&$loop, $appId) {
//Ignore the error and continue to simulate EventLoop
$loop($appId);
}
);
};
foreach(range(1, 10) as $appId) {
$loop($appId);
}
$promise->wait();
I try to use promise to simulate EventLoop. As shown in my example code, I have 10 application IDs. I want to launch 10 requests concurrently. At the same time, the response time of each request is different. I want to launch the request of the current application ID immediately after each request is completed to simulate EventLoop.
But I find it doesn't seem to work. How can I simulate EventLoop
I have run in the situation where i'm handling incoming call using PHP/laravel, so when client calls to the company number the response is this method :
public function respondToUser()
{
$response = new Twiml();
$audio_file_path = trans('ivr_file_paths.welcome');
$response->play($audio_file_path);
$response->redirect('/ivr/call/enqueue', ['method' => 'POST']);
return $response;
}
But what I want to achieve next is to put incoming call in queue and then run the music in background if the operator (one operator /agent only) is busy, if not then connect to him.
this is what it looks like now
public function enqueueCall(Request $request)
{
$please_wait_audio_file = trans('paths.please_wait');
$please_wait_audio_file = trans('ivr_file_paths.please_wait');
$response = new Twiml();
$dial = $response->dial();
$dial->number('+number');
$response->enqueue('support', ['waitUrl' => $please_wait_audio_file]);
Log::info($response);
echo $response;
}
I know there is no queue right now, but this method just ends up the call..
Any suggestions? Thank you very much!
Twilio developer evangelist here.
I recommend you start by looking at the <Enqueue> TwiML verb which queues a caller up, followed by <Queue> which you can use within <Dial> to pop the next user off from the queue and talk to them.
If you need anything more complicated than that, then start reading into TaskRouter.
edit some example code:
Enqueue the caller and dial your agent.
public function enqueueCall(Request $request)
{
// build up the TwiML
$please_wait_audio_file = trans('ivr_file_paths.please_wait');
$response = new Twiml();
$response->enqueue('support', ['waitUrl' => $please_wait_audio_file]);
// make the call to your agent
$client = new Client($yourTwilioAccountSid, $yourTwilioAuthToken);
$call = $client->calls->create(
$yourAgentNumber,
$yourTwilioNumber,
array("url" => "http://example.com/ivr/call/queue")
);
Log::info($response);
echo $response;
}
When the agent connects, dial the queue:
public function dialQueue(Request $request)
{
$response = new Twiml();
$dial = $response->dial();
$dial->queue('support');
echo $response;
}
Problem
We are trying to do concurrent asynchronous requests using guzzle. After going through a few resources, like this and this, we came up with some code that is shared below. However it is not working as expected.
It looks like Guzzle is doing these request synchronously rather than async.
Expectation
Just for test purposes, we are hitting an internal url, which does a 5 second sleep. With a concurrency of 10 we expect that all 10 requests will initially be queued and send to the server almost simultaneously, where they will wait for 5 seconds, and will then almost all of those will finish nearly at the same time. Which would make the guzzle client to pick up 10 new requests from iterator and so on.
Code
$iterator = function() {
$index = 0;
while (true) {
$client = new Client(['timeout'=>20]);
$url = 'http://localhost/wait/5' . $index++;
$request = new Request('GET',$url, []);
echo "Queuing $url # " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;
yield $client
->sendAsync($request)
->then(function(Response $response) use ($request) {
return [$request, $response];
});
}
};
$promise = \GuzzleHttp\Promise\each_limit(
$iterator(),
10, /// concurrency,
function($result, $index) {
/** GuzzleHttp\Psr7\Request $request */
list($request, $response) = $result;
echo (string) $request->getUri() . ' completed '.PHP_EOL;
},
function(RequestException $reason, $index) {
// left empty for brevity
}
);
$promise->wait();
Actual Results
We find that that Guzzle never made a second request until the first one is finished. and so on.
Queuing http://localhost/wait/5/1 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/2 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/3 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/4 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/5 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/6 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/7 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/8 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/9 # 2017-09-01 17:15:28
Queuing http://localhost/wait/5/10 # 2017-09-01 17:15:28
http://localhost/wait/5/1 completed
Queuing http://localhost/wait/5/11 # 2017-09-01 17:15:34
http://localhost/wait/5/2 completed
Queuing http://localhost/wait/5/12 # 2017-09-01 17:15:39
http://localhost/wait/5/3 completed
Queuing http://localhost/wait/5/13 # 2017-09-01 17:15:45
http://localhost/wait/5/4 completed
Queuing http://localhost/wait/5/14 # 2017-09-01 17:15:50
OS / Version information
Ubuntu
PHP/7.1.3
GuzzleHttp/6.2.1
curl/7.47.0
The issue could be with \GuzzleHttp\Promise\each_limit .. which perhaps does not initiates or resolves the promise fast enough. It may be possible that we have to trick that into ticking externally.
In the example code, you're creating a new GuzzleHttp\Client instance for every request you want to make. This might not seem important, however, during instantiation of GuzzleHttp\Client it will set a default handler if none is provided. (This value is then passed down to any request being sent through the Client, unless it is overridden.)
Note: It determines the best handler to use from this function. Though, it'll most likely end up defaulting to curl_mutli_exec.
What's the importance of this? It's the underlying handler that is responsible for tracking and executing multiple requests at the same time. By creating a new handler every time, none of your requests are properly being grouped up and ran together. For some more insight into this take a gander into the curl_multi_exec docs.
So, you kind of have two ways of dealing with this:
Pass through the client through to the iterator:
$client = new GuzzleHttp\Client(['timeout' => 20]);
$iterator = function () use ($client) {
$index = 0;
while (true) {
if ($index === 10) {
break;
}
$url = 'http://localhost/wait/5/' . $index++;
$request = new Request('GET', $url, []);
echo "Queuing $url # " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;
yield $client
->sendAsync($request)
->then(function (Response $response) use ($request) {
return [$request, $response];
});
}
};
$promise = \GuzzleHttp\Promise\each_limit(
$iterator(),
10, /// concurrency,
function ($result, $index) {
/** #var GuzzleHttp\Psr7\Request $request */
list($request, $response) = $result;
echo (string)$request->getUri() . ' completed ' . PHP_EOL;
}
);
$promise->wait();
or create the handler elsewhere and pass it to the client: (Though I'm not sure why you'd do this, but it's there!)
$handler = \GuzzleHttp\HandlerStack::create();
$iterator = function () use ($handler) {
$index = 0;
while (true) {
if ($index === 10) {
break;
}
$client = new Client(['timeout' => 20, 'handler' => $handler])
$url = 'http://localhost/wait/5/' . $index++;
$request = new Request('GET', $url, []);
echo "Queuing $url # " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;
yield $client
->sendAsync($request)
->then(function (Response $response) use ($request) {
return [$request, $response];
});
}
};
$promise = \GuzzleHttp\Promise\each_limit(
$iterator(),
10, /// concurrency,
function ($result, $index) {
/** #var GuzzleHttp\Psr7\Request $request */
list($request, $response) = $result;
echo (string)$request->getUri() . ' completed ' . PHP_EOL;
}
);
$promise->wait();
I'm trying to understand the concept of Promises using ReactPHP
$app = function ($request, $response) use ($redis, $config) {
$promise = React\Promise\all(
array(
AsyncGetUser(),
AsyncGetDB(),
AsyncGetTemplate()
)
)->then(function ($res) {
$result = ParseTemplate($user, $template, $whatever);
}
\React\Promise\resolve($promise);
$response->writeHead(200, array('Content-Type' => 'text/plain'));
$response->end($result);
}
$http->on('request', $app);
But $response is sent before $result is ready.
How can do something like await for $promise so I send $result properly ?
I've tried to move $response->end to another->then() section but then I don't get any response in browser (i.e. the script gets a result when $app = function is finished already).
I don't know reactphp at all, but if promises work like promises in JS for example, seems like you need to write the response in the ->then where you have a result!
$app = function ($request, $response) use ($redis, $config) {
$promise = React\Promise\all(
array(
AsyncGetUser(),
AsyncGetDB(),
AsyncGetTemplate()
)
)->then(function ($res) {
$result = ParseTemplate($user, $template, $whatever);
$response->writeHead(200, array('Content-Type' => 'text/plain'));
$response->end($result);
}
}
$http->on('request', $app);
Note: the following line in your code
\React\Promise\resolve($promise);
makes no sense. \React\Promise\resolve doesn't "resolve the promise" as you seem to think, it creates and returns a resolved promise - which you discard!
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.