So I'm trying to use guzzle for a couple of concurrent requests. I've seen several examples online and this is what I came up with, but can't seem to get it to work. No errors, no warnings, nothing. I've tried logging inside each promise but nothing happens.
And I know for sure that nothing is happening because nothing is getting inserted in the DB. Any ideas what I'm missing? (I'm yielding each request with its respective then because at the end of each promise, the DB operations are specific to that user)
use GuzzleHttp\Promise\EachPromise;
use Psr\Http\Message\ResponseInterface;
$promises = (function () use($userUrls){
$userUrls->each(function($user) {
yield $this->client->requestAsync('GET', $user->pivot->url)
->then(function (ResponseInterface $response) use ($user) {
$this->dom->load((string)$response->getBody());
// ... some db stuff that inserts row in table for this
// $user with stuff from this request
});
});
});
$all = new EachPromise($promises, [
'concurrency' => 4,
'fulfilled' => function () {
},
]);
$all->promise()->wait();
Not sure wht you don't get an error, but your generator is definitely wrong.
use Psr\Http\Message\ResponseInterface;
use function GuzzleHttp\Promise\each_limit_all;
$promises = function () use ($userUrls) {
foreach ($userUrls as $user) {
yield $this->client->getAsync($user->pivot->url)
->then(function (ResponseInterface $response) use ($user) {
$this->dom->load((string)$response->getBody());
// ... some db stuff that inserts row in table for this
// $user with stuff from this request
});
};
};
$all = each_limit_all($promises(), 4);
$all->promise()->wait();
Note foreach instead of $userUrls->each(), it's important because in your version generator function is the function that is passes to ->each() call, not the one you assign to $promise.
Also note that you must activate the generator (call $promises() as pass the result, not pass the function itself to Guzzle).
Otherwise all looks good, try the code with my changes.
Related
I am using Redis as caching layer and I'd like to know what is the best practice or how to use it properly alongside with DB (in this case MySQL).
Here I have an example of user dashboard function:
public function updateDashboardUser(Request $request) {
$user = app('redis')->hGetAll($request->userID); //Get data from cache
if ($user) { //if there is data use this
$id = $user['id'];
$name = $user['name'];
} else { //otherwise use this
$user = User::select('id', 'name')->where('id', '=', $request->userID)->first();
$id = $user->id;
$name = $user->name;
}
return response()->json(['id' => $id, 'name' => $name], 200);
}
However this else statement is never reached somehow even though $user from cache might be empty. Is there a better way to do this?
Also while updating... Is there a better way to automatically update both (cache and DB) when data in one of them is changed.
public function editDashboard(Request $request) {
$user = Route::find($request->userID);
$user->name = $request->name;
$user->save();
$cacheEdit = app('redis')->hSet($user->id, 'name', $request->name);
return response()->json(['status' => '200'], 200);
}
At the moment I do it like this but sometimes only one of them might be changed and then cache data (or vice-versa, DB data) are not synchronized/updated.
This is my first experience with Redis and caching in general, so any help is appreciated.
Instead of directly using Redis API, you should use Laravel cache API instead, it allows some abstraction and you don't even need to know which is the underlying cache.
By using Eloquent instead of query builder, you may unlock some very powerful features such as model events. For example, in your User model:
protected static function booted()
{
parent::booted();
$cache = app('cache');
static::updated(function ($user) use ($cache) {
$cacheKey = $user->getCacheKey();
if ($cache->has($cacheKey) {
$cache->put($cacheKey, $user, 30); //30 is the life duration of this value in cache, you're free to change it
}
});
static::deleted(function ($user) use ($cache) {
$cache->forget($user->getCacheKey());
});
}
public function getCacheKey()
{
return 'users.' . $this->getKey();
}
These "event hooks" get automatically called by Laravel whenever you update or delete a User by using Eloquent.
It allows you then to fluently do this:
use App\User;
use Illuminate\Http\Request;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\Cache\Repository as Cache;
public function updateDashboardUser(Request $request, Cache $cache, ResponseFactory $responseFactory)
{
$id = $request->userID;
$user = $cache->remember('users.' . $id, 30, function () use ($id) {
return User::findOrFail($id);
});
return $responseFactory->json($user->only(['id', 'name']), 200);
}
Such as mentioned here https://laravel.com/docs/7.x/cache#retrieving-items-from-the-cache , you can use remember (or rememberForever) to retrieve something from the cache and automatically fallback into a closure if not found. The findOrFail will then retrieves it from the database and will eventually throw an Illuminate\Database\Eloquent\ModelNotFoundException because it makes no sense then to send a successful response. I also replaced your helpers such as response by using dependency injection on contracts (Laravel interfaces) which is the cleanest practice.
https://laravel.com/docs/7.x/contracts
Disclaimer: This is my first time working with ReactPHP and "php promises", so the solution might just be staring me in the face 🙄
I'm currently working on a little project where I need to create a Slack bot. I decided to use the Botman package and utilize it's Slack RTM driver. This driver utilizes ReactPHP's promises to communicate with the RTM API.
My problem:
When I make the bot reply on a command, I want to get the get retrieve the response from RTM API, so I can cache the ID of the posted message.
Problem is that, the response is being returned inside one of these ReactPHP\Promise\Promise but I simply can't figure out how to retrieve the data.
What I'm doing:
So when a command is triggered, the bot sends a reply Slack:
$response = $bot->reply($response)->then(function (Payload $item) {
return $this->reply = $item;
});
But then $response consists of an (empty?) ReactPHP\Promise\Promise:
React\Promise\Promise^ {#887
-canceller: null
-result: null
-handlers: []
-progressHandlers: & []
-requiredCancelRequests: 0
-cancelRequests: 0
}
I've also tried using done() instead of then(), which is what (as far as I can understand) the official ReactPHP docs suggest you should use to retrieve data from a promise:
$response = $bot->reply($response)->done(function (Payload $item) {
return $this->reply = $item;
});
But then $response returns as null.
The funny thing is, during debugging, I tried to do a var_dump($item) inside the then() but had forgot to remove a non-existing method on the promise. But then the var_dump actually returned the data 🤯
$response = $bot->reply($response)->then(function (Payload $item) {
var_dump($item);
return $this->reply = $item;
})->resolve();
So from what I can fathom, it's like I somehow need to "execute" the promise again, even though it has been resolved before being returned.
Inside the Bot's reply method, this is what's going on and how the ReactPHP promise is being generated:
public function apiCall($method, array $args = [], $multipart = false, $callDeferred = true)
{
// create the request url
$requestUrl = self::BASE_URL . $method;
// set the api token
$args['token'] = $this->token;
// send a post request with all arguments
$requestType = $multipart ? 'multipart' : 'form_params';
$requestData = $multipart ? $this->convertToMultipartArray($args) : $args;
$promise = $this->httpClient->postAsync($requestUrl, [
$requestType => $requestData,
]);
// Add requests to the event loop to be handled at a later date.
if ($callDeferred) {
$this->loop->futureTick(function () use ($promise) {
$promise->wait();
});
} else {
$promise->wait();
}
// When the response has arrived, parse it and resolve. Note that our
// promises aren't pretty; Guzzle promises are not compatible with React
// promises, so the only Guzzle promises ever used die in here and it is
// React from here on out.
$deferred = new Deferred();
$promise->then(function (ResponseInterface $response) use ($deferred) {
// get the response as a json object
$payload = Payload::fromJson((string) $response->getBody());
// check if there was an error
if (isset($payload['ok']) && $payload['ok'] === true) {
$deferred->resolve($payload);
} else {
// make a nice-looking error message and throw an exception
$niceMessage = ucfirst(str_replace('_', ' ', $payload['error']));
$deferred->reject(new ApiException($niceMessage));
}
});
return $deferred->promise();
}
You can see the full source of it here.
Please just point me in some kind of direction. I feel like I tried everything, but obviously I'm missing something or doing something wrong.
ReactPHP core team member here. There are a few options and things going on here.
First off then will never return the value from a promise, it will return a new promise so you can create a promise chain. As a result of that you do a new async operation in each then that takes in the result from the previous one.
Secondly done never returns result value and works pretty much like then but will throw any uncaught exceptions from the previous promise in the chain.
The thing with both then and done is that they are your resolution methods. A promise a merely represents the result of an operation that isn't done yet. It will call the callable you hand to then/done once the operation is ready and resolves the promise. So ultimately all your operations happen inside a callable one way or the other and in the broadest sense. (Which can also be a __invoke method on a class depending on how you set it up. And also why I'm so excited about short closures coming in PHP 7.4.)
You have two options here:
Run all your operations inside callable's
Use RecoilPHP
The former means a lot more mind mapping and learning how async works and how to wrap your mind around that. The latter makes it easier but requires you to run each path in a coroutine (callable with some cool magic).
I'm using v2 of Slim framework. Most of the APIs are of the format below:
https://abcXX.com/:version/customer/:CustomerID/XXX/....
So almost all of the APIs have :version and :CustomerID which is provided in the URL.
I'm writing a hookup like so, and wanted to know if there is any way to globally intercept it in hookup function
public function call()
{
$this->app->hook('slim.before.dispatch', array($this, 'onBeforeDispatch'));
$this->next->call();
}
public function onBeforeDispatch()
{
$this->app->is_development_environment = IS_DEVELOMPENT_ENVIRONMENT;
$route_params = $this->app->router()->getCurrentRoute()->getParams();
$param_version = ???
}
I don't know how to get the :version's value from URL parameters. If I could do that, I'd be able to do some global handling at the very top, instead of having to write the code in each API handler function.
Please suggest how to do this.
I recommend avoiding hooks for determining which version of your API is being called. Consider grouping routes instead.
And I suggest using middlewares instead of hooks unless you really need hooks, they are different in nature.
Anyway, if you want to use hooks, here is an example of how to do it:
<?php
require_once ('../vendor/autoload.php');
$app = new \Slim\Slim();
$app->hook('slim.before.dispatch', function() use($app){
$params = $app->router()->getCurrentRoute()->getParams();
// Use $params['version'] to do what you want
print "<pre>";
print_r($params);
print "</pre>";
});
$app->get('/:version/hello/:name', function ($version, $name) {
print "Hello, " . $name;
});
$app->get('/:version/customer/:CustomerID/XXX', function($version, $customerId){
print "Customers route";
});
$app->run();
Now if you open <host address>/2.1/hello/foo or <host address>/2.1/customer/1000/XXX you see in both cases you see ['version'] => 2.1 in $params.
But if you go with this approach, I think you should define all your route callbacks to accept a $version as their first parameter, which is absolutely unnecessary and redundant and makes code manipulation harder. But if you group routes, then you'll have something like:
$app->group('/2.1', function () use ($app {
$app->get('/hello/:name', function ($name) {});
$app->get('/customer/:CustomerID/XXX', function($customerId){});
});
which is cleaner and does not need extra effort.
Use Callback Function or Make a function to get params like it :-
$app->get('/books/:one/:two', function ($one, $two) {
echo "The first parameter is " . $one;
echo "The second parameter is " . $two;
});
I am facing a problem while implementing micro application in which i want a middle ware that is executed after response is send to browser. As mentioned in documentation of phalcon finish middle ware suppose to that work but its not working, browser still waiting to complete whole process then it return response.
my code is like:
$app->before(function() use ($app, $di) { $di->get('log')->log("This is a message before"); $di->get('log')->close(); });
$testCtrl = new testCtrl();
$app->get('/ctrltest', array($testCtrl, "indexAction"));
$app->after(function() use ($app, $di) {
echo "after";
$di->get('log')->log("This is a message after");
$di->get('log')->close();
});
$app->finish(function() use ($app, $di) {
$count = 100000;
while ($count) {`enter code here`
$di->get('log')->log("count " . $count);
$count--;
}
});
the response is coming after the whole loop is executed. Any sample code or some suggestion will be helpful
I need to return from a function call once a React/Promise has been resolved. The basic idea is to fake a synchronous call from an ansynchronous one. This means that the outer function must return a value once a promise has been resolved or rejected.
This is to create a driver for RedBeanPHP using React/Mysql. I am aware that this will likely lead to CPU starvation in the React event loop.
My initial idea was to use a generator then call yield inside a \React\Promise\Deferred::then callback.
function synchronous()
{
$result = asynchronous();
}
function asynchronous()
{
$deferred = new \React\Promise\Deferred;
$sleep = function() use ($deferred)
{
sleep(5);
$deferred->resolve(true);
};
$deferred->then(function($ret) {
yield $ret;
});
$sleep();
}
The PHP generator class, AFAICT, is only directly constructable by the PHP engine itself. The then callback would need to directly invoke send on the generator of the asynchronous function for this to work.
PHP lacks both continuations as well as generator delegation, which would make it possible to call yield from inside a nested callback, making this entirely impossible to achieve for the moment.
ReactPhp offers the async tools package which has an await function.
Code can then become:
function synchronous()
{
$result = \React\Async\await(asynchronous());
}
function asynchronous()
{
$deferred = new \React\Promise\Deferred;
$sleep = function() use ($deferred)
{
sleep(5);
$deferred->resolve(true);
};
$sleep();
return $deferred->promise();
}