Exploring chained request handling with Guzzle 6.
Recently I had to handle some asynchronous requests with PHP to multiple RESTful endpoints where some of the response values would be processed and the data re-used to make even more requests. In short the name I have used for this is chained request handling.
After doing some research I came up with the idea of using Guzzle 6 as the HTTP client and leveraging the promise system it has with functional objects to integrate easy with my TDD workflow. Alternative solutions that I did not choose for various reasons where the PHP cURL Library and Stream Functions.
For demonstration purposes I came up with the following user story which I have build and now will share some of the interesting parts, the completed user story can be found on GitHub.
Create an overview of artist names and albums.
Use the Spotify REST API with provided artists to find related artists and display them together in a overview with artist name and album.
- Retrieve provided artists and extract the artist name and id.
- Retrieve related artists by id and extract the artist name and id.
- Retrieve artists albums previous retrieved artist ids extract album name.
- Create a overview with the following data artist name, album title.
Chained Promises in Guzzle 6.
Every asynchronous request that is created is a promise in Guzzle meaning it must satisfy the promise interface the most interesting method in my case is the then() method which can be chained and has the following signature.
then(callable $onFulfilled, callable $onRejected) : PromiseInterface
So to leverage the promise system I needed a nice way to create some onFulfilled and onRejected handlers. I opted for the function object for both cases.
The onFulfilled handlers implement a shared interface with two methods one for invocation and one for retrieving the data.
The onRejected handler is not implementing the interface because it handles the errors and returns an GuzzleHttp\Promise\RejectedPromise instance.
When this is done the chain will stop and will ignore further down the chain handlers.
Example implementations for onFulfilled and onRejected handlers.
When creating the asynchronous request a promise is created and when the wait() method is called, the chained handlers will be invoked the result of each of them will passed on to the next.
The actual unwrapping takes place in the handlers itself in my case due to the fact we have dependencies on the passed on value.
Inside of these handlers I am simply creating a promises array that stores all the asynchronous requests it should make and then I unwrap them to make concurrent requests by invoking the following method.
The client implementation that chains up all the handlers can be found here
Listening on the interface.
I was curious to see what was going on in the background and since I am a Ubuntu Linux user I could easily sniff out my network card.
To do this open up a terminal and issue the command below where <device> should be replaced by your network card.
$ sudo tcpdump -i <device> host api.spotify.com
The promises system provided by the Guzzle 6 package is a very elegant way to handle all kinds of nested and or chained requests to API endpoints.
Found any technical error? please submit it to me and I will correct the page as soon as possible also note I am not a English native so any grammatical errors please notify me and I will correct them.