How to send multiple requests at once ReactPHP? - php

I am using guzzle to send some requests:
$response = $this->client->request(new SomeObject());
using the class below
...
public function request(Request $request)
{
return $this->requestAsync($request)->wait();
}
// Here I'm using Guzzle Async, but I can remove that is needed
public function requestAsync(Request $request)
{
$promise = $this->http->requestAsync($request->getMethod(), $request->getUri());
$promise = $promise->then(
function (ResponseInterface $response) use ($request) {
return $response;
}
);
return $promise;
}
...
I would like to use ReactPHP to send multiple requests at once in a foreach loop:
$requests = [];
foreach ($data as $value) {
$requests[] = $this->client->request(new SomeObject());
}
// pass $requests to ReactPHP here and wait on the response
Any ideas?

First of all, you don't need ReactPHP to use parallel HTTP requests with Guzzle. Guzzle itself provides this feature (if you use cURL handler, which is the default).
For example:
$promises = [];
foreach ($data as $value) {
$promises[] = $guzzleClient->getAsync(/* some URL */);
}
// Combine all promises
$combinedPromise = \GuzzleHttp\Promise\all($promises)
// And wait for them to finish (all requests are executed in parallel)
$responses = $combinedPromise->wait();
If you still want to use Guzzle with ReactPHP event loop, then, unfortunately, there are no straightforward solution. You cat take a look at https://github.com/productsupcom/guzzle-react-bridge (I'm the developer, so feel free to ask questions).

Related

How to use guzzle to simulate event loop HTTP pool

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

php - how to send multiple xml request (web-services)

I didn't find any example of how to make several web-service request at the same time (simultaneous).
For instant, this is request I send in order to get hotel details:
$s = new soapclient("http://www.wb-service-address.com",array('wsdl'));
$HotelInfo = new stdClass;
<HotelInfo softwareID="123" SessionId="153" Lang="EN" InfoType="">
<Hotel ID="103" />
</HotelInfo>
$HotelInfo->xmlRequest = $paramsStr;
$result = $s->__call("SubmitXmlString",array($HotelInfo));
$obj_pros = get_object_vars($result);
$hotel_full_xml = $obj_pros['SubmitXmlStringResult'];
$hotel_full_xml = simplexml_load_string($hotel_full_xml);
(The XML "HotelInfo" is the request)
I would like to send the same request to number of systems (urls) at the same time.
I'll appreciate your help
php by nature does not do that but you can using another library like GuzzleHttp
Exemple:
public function index()
{
$promises = call_user_func(function () {
foreach ($this->usernames as $username) {
(yield $this->client->requestAsync('GET', 'https://api.github.com/users/' . $username));
}
});
// Wait till all the requests are finished.
\GuzzleHttp\Promise\all($promises)->then(function (array $responses) {
$this->profiles = array_map(function ($response) {
return json_decode($response->getBody(), true);
}, $responses);
})->wait();
// Return JSON response
$response = new Response();
// StreamInterface objects are not immutable!
$response->getBody()->write($this->html());
return $response->withHeader('Content-type', 'text/html');
}
Also you can using this solution: https://github.com/amphp/amp
another solution with python: ThreadPoolExecutor
see that in python documentation: Concurrent Execution

Twilio add incoming call to queue and call to the agent

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;
}

Ping using Guzzle Http php

I want to use guzzle http for ping.
I dont care about the response.
Just request is enough.
No need of response.
After request the code need to exits. Dont wait for response.
I tried to do with async.
<?php
require_once "vendor/autoload.php";
$client = new GuzzleHttp\Client();
$promise1 = $client->requestAsync('GET', 'http://localhost/test/?id=from_async1');
$promise2 = $client->requestAsync('GET', 'http://localhost/test/?id=from_async2');
$promise3 = $client->requestAsync('GET', 'http://localhost/test/?id=from_async3');
$promise1->then(function (ResponseInterface $response) {
});
$promise2->then(function (ResponseInterface $response) {
});
$promise3->then(function (ResponseInterface $response) {
});
$promise1->wait();
$promise2->wait();
$promise3->wait();
But still this script waits for all response to come back.
How to get the script work without waiting for response? Any thoughts?

Created Request using die() also dies the Request caller

I don't know if it's the right terms to employ...
I made an API, in which the answer is sent by the die() function, to avoid some more useless calculations and/or functions calls.
example :
if (isset($authorize->refusalReason)) {
die ($this->api_return(true, [
'resultCode' => $authorize->resultCode,
'reason' => $authorize->refusalReason
]
));
}
// api_return method:
protected function api_return($error, $params = []) {
$time = (new DateTime())->format('Y-m-d H:i:s');
$params = (array) $params;
$params = ['error' => $error, 'date_time' => $time] + $params;
return (Response::json($params)->sendHeaders()->getContent());
}
But my website is based on this API, so I made a function to create a Request and return the contents of it, based on its URI, method, params, and headers:
protected function get_route_contents($uri, $type, $params = [], $headers = []) {
$request = Request::create($uri, $type, $params);
if (Auth::user()->check()) {
$request->headers->set('S-token', Auth::user()->get()->Key);
}
foreach ($headers as $key => $header) {
$request->headers->set($key, $header);
}
// things to merge the Inputs into the new request.
$originalInput = Request::input();
Request::replace($request->input());
$response = Route::dispatch($request);
Request::replace($originalInput);
$response = json_decode($response->getContent());
// This header cancels the one there is in api_return. sendHeaders() makes Content-Type: application/json
header('Content-Type: text/html');
return $response;
}
But now when I'm trying to call an API function, The request in the API dies but dies also my current Request.
public function postCard($token) {
$auth = $this->get_route_contents("/api/v2/booking/payment/card/authorize/$token", 'POST', Input::all());
// the code below is not executed since the API request uses die()
if ($auth->error === false) {
return Redirect::route('appts')->with(['success' => trans('messages.booked_ok')]);
}
return Redirect::back()->with(['error' => $auth->reason]);
}
Do you know if I can handle it better than this ? Any suggestion of how I should turn my code into ?
I know I could just use returns, but I was always wondering if there were any other solutions. I mean, I want to be better, so I wouldn't ask this question if I knew for sure that the only way of doing what I want is using returns.
So it seems that you are calling an API endpoint through your code as if it is coming from the browser(client) and I am assuming that your Route:dispatch is not making any external request(like curl etc)
Now There can be various approaches to handle this:
If you function get_route_contents is going to handle all the requests, then you need to remove the die from your endpoints and simply make them return the data(instead of echoing). Your this "handler" will take care of response.
Make your Endpoint function to have an optional parameter(or some property set in the $request variable), which will tell the function that this is an internal request and data should be returned, when the request comes directly from a browser(client) you can do echo
Make an external call your code using curl etc(only do this if there is no other option)

Categories