Diving Into Guzzle:
Getting the Most from Your Requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
About me
Steven Wade

• Husband, father

• Founder/Organizer of UpstatePHP

• Engineer at Follow Up Boss

Twitter: @stevenwadejr

Email: stevenwadejr@gmail.com
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Austin, we have a problem
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
• URLs
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
• URLs
• Headers
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
• URLs
• Headers
• GET, POST, PUT, DELETE, ...
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
• URLs
• Headers
• GET, POST, PUT, DELETE, ...
• Status codes
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
• URLs
• Headers
• GET, POST, PUT, DELETE, ...
• Status codes
• Sending a request
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
• URLs
• Headers
• GET, POST, PUT, DELETE, ...
• Status codes
• Sending a request
• Parsing a response
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
HTTP Requests are Hard
• URLs
• Headers
• GET, POST, PUT, DELETE, ...
• Status codes
• Sending a request
• Parsing a response
• Handling errors
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
file_get_contents()
$apiKey = 'my_api_key:';
$people = file_get_contents(
'https://' . $apiKey . '@api.followupboss.com/v1/people'
);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
How do we make HTTP
requests in PHP?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Streams
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Streams
✓ Built-in to PHP - file_get_contents(), fopen()
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Streams
✓ Built-in to PHP - file_get_contents(), fopen()
๏ More complicated requests require stream contexts
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Streams
✓ Built-in to PHP - file_get_contents(), fopen()
๏ More complicated requests require stream contexts
๏ Don't support persistent connections
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Streams
✓ Built-in to PHP - file_get_contents(), fopen()
๏ More complicated requests require stream contexts
๏ Don't support persistent connections
๏ Don't support parallel requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL
✓ Supports persistent connections
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL
✓ Supports persistent connections
✓ Supports parallel requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL
✓ Supports persistent connections
✓ Supports parallel requests
✓ Most common way of making external requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL
✓ Supports persistent connections
✓ Supports parallel requests
✓ Most common way of making external requests
๏ Not built-in: requires library and extension
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL
✓ Supports persistent connections
✓ Supports parallel requests
✓ Most common way of making external requests
๏ Not built-in: requires library and extension
๏ Clunky and confusing API
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL
✓ Supports persistent connections
✓ Supports parallel requests
✓ Most common way of making external requests
๏ Not built-in: requires library and extension
๏ Clunky and confusing API
๏ Cumbersome error handling
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL - example
$baseUrl = 'https://api.followupboss.com/v1';
$headers = [
'Accept: application/json',
'Content-Type: application/json'
];
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $baseUrl . '/people');
curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
curl_setopt($handle, CURLOPT_USERPWD, 'my_api_key:');
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
$people = curl_exec($handle);
curl_setopt($handle, CURLOPT_URL, $baseUrl . '/tasks');
$tasks = curl_exec($handle);
curl_close($handle);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL - errors?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
cURL - error handling
$people = curl_exec($handle);
if(curl_errno($handle)){
echo 'Request Error:' . curl_error($handle);
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
• Understand the basics of making requests with Guzzle
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
• Understand the basics of making requests with Guzzle
• Understand the concept of middleware and how to use it
within your requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
• Understand the basics of making requests with Guzzle
• Understand the concept of middleware and how to use it
within your requests
• Be able to make asynchronous requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
• Understand the basics of making requests with Guzzle
• Understand the concept of middleware and how to use it
within your requests
• Be able to make asynchronous requests
• Know how to test your HTTP requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
GUZZLE
[angelic singing]
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is Guzzle?
"Guzzle is a PHP HTTP client that makes it easy to send HTTP requests
and trivial to integrate with web services."
http://docs.guzzlephp.org
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Guzzle Provides:
•Simple interface for building query strings, POST requests, uploading JSON data, etc...
•Can send both synchronous and asynchronous requests using the same interface.
•Uses PSR-7 interfaces for requests, responses, and streams.
•Abstracts away the underlying HTTP transport (can use streams, cURL, other).
•Middleware system allows you to augment and compose client behavior.
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Installing Guzzle
composer require guzzlehttp/guzzle
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Basic Request with Guzzle
$client = new GuzzleHttpClient;
$response = $client->get('http://example.com/foo');
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
"Describes common interfaces for representing HTTP messages as
described in RFC 7230 and RFC 7231, and URIs for use with HTTP
messages as described in RFC 3986."
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Huh?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Huh?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
Request
Method
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
Request
Method
Request
Target
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
Request
Method
Request
Target
Protocol
Version
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
Headers
Request
Method
Request
Target
Protocol
Version
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=batBody
Headers
Request
Method
Request
Target
Protocol
Version
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is PSR-7?
MessageInterface
- getHeaders()
- hasHeader()
- getHeader()
- getBody()
- withHeader()
- withBody()
RequestInterface
- getMethod()
- getUri()
- withMethod()
- withUri()
ResponseInterface
- getStatusCode()
- getReasonPhrase()
- withStatus()
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
• Slim Framework
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
• Slim Framework
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
• Slim Framework
Via a bridge:
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
• Slim Framework
Via a bridge:
• Laravel
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
• Slim Framework
Via a bridge:
• Laravel
• Symfony
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
• Slim Framework
Via a bridge:
• Laravel
• Symfony
• Standard representation of
HTTP requests and responses
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
PSR-7 - Why?
Who uses it? Why use it?
By default:
• Guzzle
• LeagueRoute
• Slim Framework
Via a bridge:
• Laravel
• Symfony
• Standard representation of
HTTP requests and responses
• Useful in an API proxy/gateway
and combined with a PSR-7
router
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is middleware?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is middleware?
Diagram stolen from Slim Framework showing how HTTP middleware layers wrap an application.
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is middleware?
Diagram stolen from Slim Framework showing how HTTP middleware layers wrap an application.
Request
Response
cURL
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware
Examples
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Defaults
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Defaults
• HTTP Errors
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Defaults
• HTTP Errors
• Redirect
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Defaults
• HTTP Errors
• Redirect
• Cookies
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Defaults
• HTTP Errors
• Redirect
• Cookies
• Prepare Body
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Defaults
• HTTP Errors
• Redirect
• Cookies
• Prepare Body
• HandlerStack::create()
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Tap
GuzzleHttpMiddleware::tap($before, $after);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Tap
use LeagueStatsDClient as StatsD;
$stack = GuzzleHttpHandlerStack::create();
$statsd = new StatsD;
$statsd->configure([...]);
$stack->push(GuzzleHttpMiddleware::tap(function () use ($statsd) {
$statsd->increment('apikey.usage');
}));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Tap
$stack->push(Middleware::tap(function ($request) use ($statsd) {
$authHeader = $request->getHeader('Authorization');
$encoded = str_replace('Basic ', '', $authHeader[0]);
$auth = base64_decode($encoded);
[$username] = explode(':', $auth);
$statsd->increment('apikey.usage.username.' . $username);
}));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Tap
$stack->push(Middleware::tap(app()->make(ApiKeyUsage::class)));
class ApiKeyUsage {
private $statsd;
public function __construct(StatsD $statsd) {
$this->statsd = $statsd;
}
public function __invoke(RequestInterface $request, array $options) {
$this->statsd->increment('apikey.usage');
}
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Log
GuzzleHttpMiddleware::log($logger, $messageFormatter);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Log
$logger = new MonologLogger('http');
$logger->pushHandler(
new MonologHandlerStreamHandler(
'path/to/your.log',
MonologLogger::INFO
)
);
$stack = GuzzleHttpHandlerStack::create();
$stack->push(GuzzleHttpMiddleware::log(
$logger,
new GuzzleHttpMessageFormatter
));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Log
$logger = new MonologLogger('http');
$logger->pushHandler(
new MonologHandlerStreamHandler(
'path/to/your.log',
MonologLogger::INFO
)
);
$stack = GuzzleHttpHandlerStack::create();
$stack->push(GuzzleHttpMiddleware::log(
$logger,
new GuzzleHttpMessageFormatter
));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Log
$logger = new MonologLogger('http');
$logger->pushHandler(
new MonologHandlerStreamHandler(
'path/to/your.log',
MonologLogger::INFO
)
);
$stack = GuzzleHttpHandlerStack::create();
$stack->push(GuzzleHttpMiddleware::log(
$logger,
new GuzzleHttpMessageFormatter
));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Log
$logger = new MonologLogger('http');
$logger->pushHandler(
new MonologHandlerStreamHandler(
'path/to/your.log',
MonologLogger::INFO
)
);
$stack = GuzzleHttpHandlerStack::create();
$stack->push(GuzzleHttpMiddleware::log(
$logger,
new GuzzleHttpMessageFormatter
));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Log
GuzzleHttpMiddleware::log($logger, $messageFormatter);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
GuzzleHttpMiddleware::retry($decider, $delay);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
$stack->push(GuzzleHttpMiddleware::retry(
function ($retries, $request, $response) {
return $response->getStatusCode() >= 500 && $retries < 4;
},
function ($retries) {
return ++$retries * 1000;
}
));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
class RetryMiddleware {
public function shouldRetry(int $retries, $request, $response): bool {
// normal checks go here
$body = json_encode($response->getBody()->getContents(), true);
$error = $body['error'] ?? null;
if ($response->getStatusCode() === 400
&& $error === 'expired_token'
) {
$this->accessToken->expiresAt = 0;
$this->accessToken->save();
return true;
}
return false;
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
class RetryMiddleware {
public function shouldRetry(int $retries, $request, $response): bool {
// normal checks go here
$body = json_encode($response->getBody()->getContents(), true);
$error = $body['error'] ?? null;
if ($response->getStatusCode() === 400
&& $error === 'expired_token'
) {
$this->accessToken->expiresAt = 0;
$this->accessToken->save();
return true;
}
return false;
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
class RetryMiddleware {
public function shouldRetry(int $retries, $request, $response): bool {
// normal checks go here
$body = json_encode($response->getBody()->getContents(), true);
$error = $body['error'] ?? null;
if ($response->getStatusCode() === 400
&& $error === 'expired_token'
) {
$this->accessToken->expiresAt = 0;
$this->accessToken->save();
return true;
}
return false;
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
class RetryMiddleware {
public function shouldRetry(int $retries, $request, $response): bool {
// normal checks go here
$body = json_encode($response->getBody()->getContents(), true);
$error = $body['error'] ?? null;
if ($response->getStatusCode() === 400
&& $error === 'expired_token'
) {
$this->accessToken->expiresAt = 0;
$this->accessToken->save();
return true;
}
return false;
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Retry
$retryMiddleware = new RetryMiddleware($accessToken);
$stack->push(Middleware::retry(
[$retryMiddleware, 'shouldRetry'],
[$retryMiddleware, 'getDelay']
));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Custom
$stack->push(function (callable $handler) {
return function (
RequestInterface $request,
array $options
) use ($handler) {
// do stuff
return $handler($request, $options);
};
});
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Custom (OAuth)
class OAuthTokenMiddleware {
public function __invoke(RequestInterface $request, array $options) {
if ($this->accessToken->hasExpired()) {
$this->accessToken = $this->provider->refreshToken(
$this->accessToken
);
}
$request = $request->withHeader(
'Authentication',
'Bearer ' . (string) $this->accessToken
);
return $this->nextHandler($request, $options);
}
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Custom (OAuth)
class OAuthTokenMiddleware {
public function __invoke(RequestInterface $request, array $options) {
if ($this->accessToken->hasExpired()) {
$this->accessToken = $this->provider->refreshToken(
$this->accessToken
);
}
$request = $request->withHeader(
'Authentication',
'Bearer ' . (string) $this->accessToken
);
return $this->nextHandler($request, $options);
}
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Custom (OAuth)
class OAuthTokenMiddleware {
public function __invoke(RequestInterface $request, array $options) {
if ($this->accessToken->hasExpired()) {
$this->accessToken = $this->provider->refreshToken(
$this->accessToken
);
}
$request = $request->withHeader(
'Authentication',
'Bearer ' . (string) $this->accessToken
);
return $this->nextHandler($request, $options);
}
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware - Custom (OAuth)
class OAuthTokenMiddleware {
public function __invoke(RequestInterface $request, array $options) {
if ($this->accessToken->hasExpired()) {
$this->accessToken = $this->provider->refreshToken(
$this->accessToken
);
}
$request = $request->withHeader(
'Authentication',
'Bearer ' . (string) $this->accessToken
);
return $this->nextHandler($request, $options);
}
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware
• Handling failed requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware
• Handling failed requests
• Modify responses
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware
• Handling failed requests
• Modify responses
• Cache
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Middleware
• Handling failed requests
• Modify responses
• Cache
• More...
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Let's build something
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
New Lead - cURL
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
New Lead - cURL
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
New Lead - cURL
$baseUrl = 'https://api.followupboss.com/v1';
$headers = [
'Accept: application/json',
'Content-Type: application/json'
];
$payload = json_encode([
'source' => 'longhornphp.com',
'system' => 'LonghornPHP',
'type' => 'General Inquiry',
'person' => [
'firstName' => 'Fox',
'lastName' => 'Mulder',
'source' => 'cURL',
'emails' => [
['value' => 'fox.mulder@fbi.gov'],
['value' => 'spooky@iwanttobelieve.org']
]
]
]);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
New Lead - cURL
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $baseUrl . '/events');
curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
curl_setopt($handle, CURLOPT_USERPWD, 'my_api_key:');
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handle, CURLINFO_HEADER_OUT, 1);
curl_setopt($handle, CURLOPT_POST, 1);
curl_setopt($handle, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($handle);
curl_close($handle);
echo $response;
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
New Lead - Guzzle
use GuzzleHttpClient;
$client = new Client([
'base_uri' => 'https://api.followupboss.com/v1/',
'auth' => ['my_api_key', '']
]);
try {
$response = $client->post(
'events',
['json' => $leadData, 'headers' => $headers]
);
$lead = json_decode($response->getBody()->getContents(), true);
print_r($lead);
} catch (Exception $e) {
echo $e->getMessage();
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Get /people - via Guzzle
$client = new GuzzleHttpClient([
'base_uri' => $baseUrl,
'auth' => ['my_api_key', '']
]);
try {
$response = $client->get('people', ['headers' => $headers]);
$people = json_decode($response->getBody()->getContents(), true);
} catch (Exception $e) {
// handle errors
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard
$request = new GuzzleHttpPsr7Request('GET', 'people', $headers);
try {
$response = $client->send($request);
$people = json_decode($response->getBody()->getContents(), true);
$response = $client->send($request->withUri(
new GuzzleHttpPsr7Uri('tasks')
));
$tasks = json_decode($response->getBody()->getContents(), true);
} catch (Exception $e) {
// handle errors
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
JsonStream
use GuzzleHttpPsr7StreamDecoratorTrait;
use PsrHttpMessageStreamInterface;
class JsonStream implements StreamInterface {
use StreamDecoratorTrait;
public function json(): ?array {
return json_decode((string) $this->getContents(), true);
}
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
JsonStream
$stack = HandlerStack::create();
$stack->push(Middleware::mapResponse(
function (ResponseInterface $response) {
$jsonStream = new JsonStream($response->getBody());
return $response->withBody($jsonStream);
}
));
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard
try {
$response = $client->send($request);
$people = $response->getBody()->json();
$response = $client->send($request->withUri(new Uri('tasks')));
$tasks = $response->getBody()->json();
} catch (Exception $e) {
// handle errors
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard
$request = new Request('GET', 'people', $headers);
$response = $client->send($request);
$people = $response->getBody()->json();
$response = $client->send($request->withUri(new Uri('tasks')));
$tasks = $response->getBody()->json();
$response = $client->send($request->withUri(new Uri('events')));
$events = $response->getBody()->json();
$response = $client->send($request->withUri(new Uri('calls')));
$calls = $response->getBody()->json();
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Async Requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Blocking Requests
$client = new GuzzleHttpClient;
$delays = [5000, 2500, 1000, 500, 0];
foreach ($delays as $delay) {
echo "Making request [delay = $delay]n";
$client->get('https://mockbin.org/delay/' . $delay);
echo "Response received [delay = $delay]n";
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Blocking Requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Blocking Requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Concurrent Requests
$client = new GuzzleHttpClient;
$delays = [5000, 2500, 1000, 500, 0];
$promises = [];
foreach ($delays as $delay) {
echo "Making request [delay = $delay]n";
$url = 'https://mockbin.org/delay/' . $delay;
$promises[] = $client->getAsync($url)
->then(function() use ($delay) {
echo "Response received [delay = $delay]n";
});
}
GuzzleHttpPromiseunwrap($promises);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Concurrent Requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Concurrent Requests
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
How do we send async requests?
$client->send($request)
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
How do we send async requests?
$client->send($request)
$client->sendAsync($request)
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is a Promise?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is a Promise?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
What is a Promise?
"A Promise is an object representing the eventual
result of an asynchronous operation"
https://amphp.org/amp/promises/
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Promises
$promise = $client->sendAsync($request);
$promise->then(
function (ResponseInterface $response) {
echo $response->getStatusCode() . "n";
},
function (RequestException $e) {
echo $e->getMessage() . "n";
echo $e->getRequest()->getMethod();
}
);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Promises
$promise = $client->sendAsync($request);
$promise = $promise->then(
function (ResponseInterface $response) {
return json_decode(
$response->getBody()->getContents(),
true
);
});
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Promises
$promise = $client->sendAsync($request);
$result = $promise->then(
function (ResponseInterface $response) {
return json_decode(
$response->getBody()->getContents(),
true
);
})
->wait();
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Promises
$promise = $client->sendAsync($request);
$result = $promise->then(
function (ResponseInterface $response) {
return json_decode(
$response->getBody()->getContents(),
true
);
})
->wait();
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Promises
$promise = $client->sendAsync($request);
$result = $promise->then(
function (ResponseInterface $response) {
return json_decode(
$response->getBody()->getContents(),
true
);
})
->wait();
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard
$request = new Request('GET', 'people', $headers);
$response = $client->send($request);
$people = $response->getBody()->json();
$response = $client->send($request->withUri(new Uri('tasks')));
$tasks = $response->getBody()->json();
$response = $client->send($request->withUri(new Uri('events')));
$events = $response->getBody()->json();
$response = $client->send($request->withUri(new Uri('calls')));
$calls = $response->getBody()->json();
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Async
$endpoints = ['people', 'tasks', 'events', 'calls'];
$promises = [];
foreach ($endpoints as $endpoint) {
$request = new Request('GET', $endpoint, $headers);
$promises[$endpoint] = $client->sendAsync($request)
->then(function (ResponseInterface $response) use ($endpoint) {
$json = $response->getBody()->json();
return $json[$endpoint] ?? [];
});
}
try {
$results = GuzzleHttpPromiseunwrap($promises);
} catch (Exception $e) {
echo $e->getMessage();
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Async
$endpoints = ['people', 'tasks', 'events', 'calls'];
$promises = [];
foreach ($endpoints as $endpoint) {
$request = new Request('GET', $endpoint, $headers);
$promises[$endpoint] = $client->sendAsync($request)
->then(function (ResponseInterface $response) use ($endpoint) {
$json = $response->getBody()->json();
return $json[$endpoint] ?? [];
});
}
try {
$results = GuzzleHttpPromiseunwrap($promises);
} catch (Exception $e) {
echo $e->getMessage();
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Async
$endpoints = ['people', 'tasks', 'events', 'calls'];
$promises = [];
foreach ($endpoints as $endpoint) {
$request = new Request('GET', $endpoint, $headers);
$promises[$endpoint] = $client->sendAsync($request)
->then(function (ResponseInterface $response) use ($endpoint) {
$json = $response->getBody()->json();
return $json[$endpoint] ?? [];
});
}
try {
$results = GuzzleHttpPromiseunwrap($promises);
} catch (Exception $e) {
echo $e->getMessage();
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Async
$endpoints = ['people', 'tasks', 'events', 'calls'];
$promises = [];
foreach ($endpoints as $endpoint) {
$request = new Request('GET', $endpoint, $headers);
$promises[$endpoint] = $client->sendAsync($request)
->then(function (ResponseInterface $response) use ($endpoint) {
$json = $response->getBody()->json();
return $json[$endpoint] ?? [];
});
}
try {
$results = GuzzleHttpPromiseunwrap($promises);
} catch (Exception $e) {
echo $e->getMessage();
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
GuzzleHttpPromisefunctions
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
GuzzleHttpPromisefunctions
// Waits on all of the provided promises
// and returns the fulfilled values.
unwrap($promises);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
GuzzleHttpPromisefunctions
// Waits on all of the provided promises
// and returns the fulfilled values.
unwrap($promises);
// Returns a promise that is fulfilled when
// all the items in the array are fulfilled.
all($promises);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
GuzzleHttpPromisefunctions
// Waits on all of the provided promises
// and returns the fulfilled values.
unwrap($promises);
// Returns a promise that is fulfilled when
// all the items in the array are fulfilled.
all($promises);
// Returns a promise that is fulfilled when
// all of the provided promises have
// been fulfilled or rejected.
settle($promises);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Coroutines
"Coroutines are a general control structure whereby flow control is
cooperatively passed between two different routines without returning."
- user21714
https://stackoverflow.com/questions/553704/what-is-a-coroutine
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Coroutines
"Coroutines are interruptible functions"
https://amphp.org/amp/coroutines/
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Coroutines
$endpoints = ['people', 'tasks', 'events', 'calls'];
$promises = [];
foreach ($endpoints as $endpoint) {
$promises[$endpoint] = new GuzzleHttpPromiseCoroutine(
function() use ($client, $endpoint, $headers) {
$request = new Request('GET', $endpoint, $headers);
$response = yield $client->sendAsync($request);
$json = $response->getBody()->json();
yield $json[$endpoint] ?? [];
}
);
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Coroutines
new GuzzleHttpPromiseCoroutine(
function() use ($client, $endpoint, $headers) {
$request = new Request('GET', $endpoint, $headers);
$response = yield $client->sendAsync($request);
$json = $response->getBody()->json();
yield $json[$endpoint] ?? [];
}
);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Coroutines
new GuzzleHttpPromiseCoroutine(
function() use ($client, $endpoint, $headers) {
$request = new Request('GET', $endpoint, $headers);
$response = yield $client->sendAsync($request);
$json = $response->getBody()->json();
yield $json[$endpoint] ?? [];
}
);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Coroutines
new GuzzleHttpPromiseCoroutine(
function() use ($client, $endpoint, $headers) {
$request = new Request('GET', $endpoint, $headers);
$response = yield $client->sendAsync($request);
$json = $response->getBody()->json();
yield $json[$endpoint] ?? [];
}
);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Dashboard - Coroutines
new GuzzleHttpPromiseCoroutine(
function() use ($client, $endpoint, $headers) {
$request = new Request('GET', $endpoint, $headers);
$response = yield $client->sendAsync($request);
$json = $response->getBody()->json();
yield $json[$endpoint] ?? [];
}
);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Why use coroutines?
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Why use coroutines?
• Avoid multiple/chained callbacks
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Why use coroutines?
• Avoid multiple/chained callbacks
• Code flows more naturally
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Why use coroutines?
• Avoid multiple/chained callbacks
• Code flows more naturally
• Use try/catch to handle errors
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Testing
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Mock Handler
// Create a mock and queue two responses.
$mock = new GuzzleHttpHandlerMockHandler([
new Response(200, ['X-Foo' => 'Bar']),
new Response(202, ['Content-Length' => 0]),
new RequestException('Server error', new Request('GET', 'test'))
]);
$handler = GuzzleHttpHandlerStack::create($mock);
$client = new GuzzleHttpClient(['handler' => $handler]);
// The first request is intercepted with the first response.
echo $client->request('GET', '/')->getStatusCode(); //> 200
// The second request is intercepted with the second response.
echo $client->request('GET', '/')->getStatusCode(); //> 202
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
History Middleware
$container = [];
$history = Middleware::history($container);
$mockHandler = new GuzzleHttpHandlerMockHandler([
new Response(200)
]);
$stack = HandlerStack::create($mockHandler);
$stack->push($history);
$client = new Client([
'base_uri' => $baseUrl,
'handler' => $stack
]);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
History Middleware
$client->request('GET', 'people', ['headers' => $headers]);
foreach ($container as $transaction) {
echo $transaction['request']->getMethod();
if ($transaction['response']) {
echo $transaction['response']->getStatusCode();
} elseif ($transaction['error']) {
echo $transaction['error'];
}
}
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
History Middleware
$this->assertEquals(
'Swade-Co',
$transaction['request']->getHeader('X-System')[0]
);
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Steven Wade - @stevenwadejrhttps://joind.in/talk/41978
Comments/Questions/Concerns/Hate Mail
Twitter: @stevenwadejr

Email: stevenwadejr@gmail.com
https://joind.in/talk/41978

Diving into guzzle

  • 1.
    Diving Into Guzzle: Gettingthe Most from Your Requests
  • 2.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 About me Steven Wade • Husband, father • Founder/Organizer of UpstatePHP • Engineer at Follow Up Boss Twitter: @stevenwadejr Email: stevenwadejr@gmail.com
  • 3.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Austin, we have a problem
  • 4.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard
  • 5.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard
  • 6.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard • URLs
  • 7.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard • URLs • Headers
  • 8.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard • URLs • Headers • GET, POST, PUT, DELETE, ...
  • 9.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard • URLs • Headers • GET, POST, PUT, DELETE, ... • Status codes
  • 10.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard • URLs • Headers • GET, POST, PUT, DELETE, ... • Status codes • Sending a request
  • 11.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard • URLs • Headers • GET, POST, PUT, DELETE, ... • Status codes • Sending a request • Parsing a response
  • 12.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 HTTP Requests are Hard • URLs • Headers • GET, POST, PUT, DELETE, ... • Status codes • Sending a request • Parsing a response • Handling errors
  • 13.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 file_get_contents() $apiKey = 'my_api_key:'; $people = file_get_contents( 'https://' . $apiKey . '@api.followupboss.com/v1/people' );
  • 14.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 How do we make HTTP requests in PHP?
  • 15.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Streams
  • 16.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Streams ✓ Built-in to PHP - file_get_contents(), fopen()
  • 17.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Streams ✓ Built-in to PHP - file_get_contents(), fopen() ๏ More complicated requests require stream contexts
  • 18.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Streams ✓ Built-in to PHP - file_get_contents(), fopen() ๏ More complicated requests require stream contexts ๏ Don't support persistent connections
  • 19.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Streams ✓ Built-in to PHP - file_get_contents(), fopen() ๏ More complicated requests require stream contexts ๏ Don't support persistent connections ๏ Don't support parallel requests
  • 20.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL
  • 21.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL ✓ Supports persistent connections
  • 22.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL ✓ Supports persistent connections ✓ Supports parallel requests
  • 23.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL ✓ Supports persistent connections ✓ Supports parallel requests ✓ Most common way of making external requests
  • 24.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL ✓ Supports persistent connections ✓ Supports parallel requests ✓ Most common way of making external requests ๏ Not built-in: requires library and extension
  • 25.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL ✓ Supports persistent connections ✓ Supports parallel requests ✓ Most common way of making external requests ๏ Not built-in: requires library and extension ๏ Clunky and confusing API
  • 26.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL ✓ Supports persistent connections ✓ Supports parallel requests ✓ Most common way of making external requests ๏ Not built-in: requires library and extension ๏ Clunky and confusing API ๏ Cumbersome error handling
  • 27.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL - example $baseUrl = 'https://api.followupboss.com/v1'; $headers = [ 'Accept: application/json', 'Content-Type: application/json' ]; $handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $baseUrl . '/people'); curl_setopt($handle, CURLOPT_HTTPHEADER, $headers); curl_setopt($handle, CURLOPT_USERPWD, 'my_api_key:'); curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 2); curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); $people = curl_exec($handle); curl_setopt($handle, CURLOPT_URL, $baseUrl . '/tasks'); $tasks = curl_exec($handle); curl_close($handle);
  • 28.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL - errors?
  • 29.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 cURL - error handling $people = curl_exec($handle); if(curl_errno($handle)){ echo 'Request Error:' . curl_error($handle); }
  • 30.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 31.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 32.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 33.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 34.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 • Understand the basics of making requests with Guzzle
  • 35.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 • Understand the basics of making requests with Guzzle • Understand the concept of middleware and how to use it within your requests
  • 36.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 • Understand the basics of making requests with Guzzle • Understand the concept of middleware and how to use it within your requests • Be able to make asynchronous requests
  • 37.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 • Understand the basics of making requests with Guzzle • Understand the concept of middleware and how to use it within your requests • Be able to make asynchronous requests • Know how to test your HTTP requests
  • 38.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 39.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 GUZZLE [angelic singing]
  • 40.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is Guzzle? "Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and trivial to integrate with web services." http://docs.guzzlephp.org
  • 41.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Guzzle Provides: •Simple interface for building query strings, POST requests, uploading JSON data, etc... •Can send both synchronous and asynchronous requests using the same interface. •Uses PSR-7 interfaces for requests, responses, and streams. •Abstracts away the underlying HTTP transport (can use streams, cURL, other). •Middleware system allows you to augment and compose client behavior.
  • 42.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Installing Guzzle composer require guzzlehttp/guzzle
  • 43.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Basic Request with Guzzle $client = new GuzzleHttpClient; $response = $client->get('http://example.com/foo');
  • 44.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7
  • 45.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? "Describes common interfaces for representing HTTP messages as described in RFC 7230 and RFC 7231, and URIs for use with HTTP messages as described in RFC 3986."
  • 46.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Huh?
  • 47.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Huh?
  • 48.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? POST /path HTTP/1.1 Host: example.com foo=bar&baz=bat
  • 49.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? POST /path HTTP/1.1 Host: example.com foo=bar&baz=bat Request Method
  • 50.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? POST /path HTTP/1.1 Host: example.com foo=bar&baz=bat Request Method Request Target
  • 51.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? POST /path HTTP/1.1 Host: example.com foo=bar&baz=bat Request Method Request Target Protocol Version
  • 52.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? POST /path HTTP/1.1 Host: example.com foo=bar&baz=bat Headers Request Method Request Target Protocol Version
  • 53.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? POST /path HTTP/1.1 Host: example.com foo=bar&baz=batBody Headers Request Method Request Target Protocol Version
  • 54.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is PSR-7? MessageInterface - getHeaders() - hasHeader() - getHeader() - getBody() - withHeader() - withBody() RequestInterface - getMethod() - getUri() - withMethod() - withUri() ResponseInterface - getStatusCode() - getReasonPhrase() - withStatus()
  • 55.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it?
  • 56.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default:
  • 57.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle
  • 58.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute
  • 59.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute • Slim Framework
  • 60.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute • Slim Framework
  • 61.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute • Slim Framework Via a bridge:
  • 62.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute • Slim Framework Via a bridge: • Laravel
  • 63.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute • Slim Framework Via a bridge: • Laravel • Symfony
  • 64.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute • Slim Framework Via a bridge: • Laravel • Symfony • Standard representation of HTTP requests and responses
  • 65.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 PSR-7 - Why? Who uses it? Why use it? By default: • Guzzle • LeagueRoute • Slim Framework Via a bridge: • Laravel • Symfony • Standard representation of HTTP requests and responses • Useful in an API proxy/gateway and combined with a PSR-7 router
  • 66.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware
  • 67.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is middleware?
  • 68.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is middleware? Diagram stolen from Slim Framework showing how HTTP middleware layers wrap an application.
  • 69.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is middleware? Diagram stolen from Slim Framework showing how HTTP middleware layers wrap an application. Request Response cURL
  • 70.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware Examples
  • 71.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Defaults
  • 72.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Defaults • HTTP Errors
  • 73.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Defaults • HTTP Errors • Redirect
  • 74.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Defaults • HTTP Errors • Redirect • Cookies
  • 75.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Defaults • HTTP Errors • Redirect • Cookies • Prepare Body
  • 76.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Defaults • HTTP Errors • Redirect • Cookies • Prepare Body • HandlerStack::create()
  • 77.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Tap GuzzleHttpMiddleware::tap($before, $after);
  • 78.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Tap use LeagueStatsDClient as StatsD; $stack = GuzzleHttpHandlerStack::create(); $statsd = new StatsD; $statsd->configure([...]); $stack->push(GuzzleHttpMiddleware::tap(function () use ($statsd) { $statsd->increment('apikey.usage'); }));
  • 79.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Tap $stack->push(Middleware::tap(function ($request) use ($statsd) { $authHeader = $request->getHeader('Authorization'); $encoded = str_replace('Basic ', '', $authHeader[0]); $auth = base64_decode($encoded); [$username] = explode(':', $auth); $statsd->increment('apikey.usage.username.' . $username); }));
  • 80.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Tap $stack->push(Middleware::tap(app()->make(ApiKeyUsage::class))); class ApiKeyUsage { private $statsd; public function __construct(StatsD $statsd) { $this->statsd = $statsd; } public function __invoke(RequestInterface $request, array $options) { $this->statsd->increment('apikey.usage'); } }
  • 81.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Log GuzzleHttpMiddleware::log($logger, $messageFormatter);
  • 82.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Log $logger = new MonologLogger('http'); $logger->pushHandler( new MonologHandlerStreamHandler( 'path/to/your.log', MonologLogger::INFO ) ); $stack = GuzzleHttpHandlerStack::create(); $stack->push(GuzzleHttpMiddleware::log( $logger, new GuzzleHttpMessageFormatter ));
  • 83.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Log $logger = new MonologLogger('http'); $logger->pushHandler( new MonologHandlerStreamHandler( 'path/to/your.log', MonologLogger::INFO ) ); $stack = GuzzleHttpHandlerStack::create(); $stack->push(GuzzleHttpMiddleware::log( $logger, new GuzzleHttpMessageFormatter ));
  • 84.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Log $logger = new MonologLogger('http'); $logger->pushHandler( new MonologHandlerStreamHandler( 'path/to/your.log', MonologLogger::INFO ) ); $stack = GuzzleHttpHandlerStack::create(); $stack->push(GuzzleHttpMiddleware::log( $logger, new GuzzleHttpMessageFormatter ));
  • 85.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Log $logger = new MonologLogger('http'); $logger->pushHandler( new MonologHandlerStreamHandler( 'path/to/your.log', MonologLogger::INFO ) ); $stack = GuzzleHttpHandlerStack::create(); $stack->push(GuzzleHttpMiddleware::log( $logger, new GuzzleHttpMessageFormatter ));
  • 86.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Log GuzzleHttpMiddleware::log($logger, $messageFormatter);
  • 87.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry GuzzleHttpMiddleware::retry($decider, $delay);
  • 88.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry $stack->push(GuzzleHttpMiddleware::retry( function ($retries, $request, $response) { return $response->getStatusCode() >= 500 && $retries < 4; }, function ($retries) { return ++$retries * 1000; } ));
  • 89.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry
  • 90.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry
  • 91.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry class RetryMiddleware { public function shouldRetry(int $retries, $request, $response): bool { // normal checks go here $body = json_encode($response->getBody()->getContents(), true); $error = $body['error'] ?? null; if ($response->getStatusCode() === 400 && $error === 'expired_token' ) { $this->accessToken->expiresAt = 0; $this->accessToken->save(); return true; } return false; }
  • 92.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry class RetryMiddleware { public function shouldRetry(int $retries, $request, $response): bool { // normal checks go here $body = json_encode($response->getBody()->getContents(), true); $error = $body['error'] ?? null; if ($response->getStatusCode() === 400 && $error === 'expired_token' ) { $this->accessToken->expiresAt = 0; $this->accessToken->save(); return true; } return false; }
  • 93.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry class RetryMiddleware { public function shouldRetry(int $retries, $request, $response): bool { // normal checks go here $body = json_encode($response->getBody()->getContents(), true); $error = $body['error'] ?? null; if ($response->getStatusCode() === 400 && $error === 'expired_token' ) { $this->accessToken->expiresAt = 0; $this->accessToken->save(); return true; } return false; }
  • 94.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry class RetryMiddleware { public function shouldRetry(int $retries, $request, $response): bool { // normal checks go here $body = json_encode($response->getBody()->getContents(), true); $error = $body['error'] ?? null; if ($response->getStatusCode() === 400 && $error === 'expired_token' ) { $this->accessToken->expiresAt = 0; $this->accessToken->save(); return true; } return false; }
  • 95.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Retry $retryMiddleware = new RetryMiddleware($accessToken); $stack->push(Middleware::retry( [$retryMiddleware, 'shouldRetry'], [$retryMiddleware, 'getDelay'] ));
  • 96.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Custom $stack->push(function (callable $handler) { return function ( RequestInterface $request, array $options ) use ($handler) { // do stuff return $handler($request, $options); }; });
  • 97.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Custom (OAuth) class OAuthTokenMiddleware { public function __invoke(RequestInterface $request, array $options) { if ($this->accessToken->hasExpired()) { $this->accessToken = $this->provider->refreshToken( $this->accessToken ); } $request = $request->withHeader( 'Authentication', 'Bearer ' . (string) $this->accessToken ); return $this->nextHandler($request, $options); } }
  • 98.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Custom (OAuth) class OAuthTokenMiddleware { public function __invoke(RequestInterface $request, array $options) { if ($this->accessToken->hasExpired()) { $this->accessToken = $this->provider->refreshToken( $this->accessToken ); } $request = $request->withHeader( 'Authentication', 'Bearer ' . (string) $this->accessToken ); return $this->nextHandler($request, $options); } }
  • 99.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Custom (OAuth) class OAuthTokenMiddleware { public function __invoke(RequestInterface $request, array $options) { if ($this->accessToken->hasExpired()) { $this->accessToken = $this->provider->refreshToken( $this->accessToken ); } $request = $request->withHeader( 'Authentication', 'Bearer ' . (string) $this->accessToken ); return $this->nextHandler($request, $options); } }
  • 100.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware - Custom (OAuth) class OAuthTokenMiddleware { public function __invoke(RequestInterface $request, array $options) { if ($this->accessToken->hasExpired()) { $this->accessToken = $this->provider->refreshToken( $this->accessToken ); } $request = $request->withHeader( 'Authentication', 'Bearer ' . (string) $this->accessToken ); return $this->nextHandler($request, $options); } }
  • 101.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware
  • 102.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware • Handling failed requests
  • 103.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware • Handling failed requests • Modify responses
  • 104.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware • Handling failed requests • Modify responses • Cache
  • 105.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Middleware • Handling failed requests • Modify responses • Cache • More...
  • 106.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Let's build something
  • 107.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 108.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 New Lead - cURL
  • 109.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 New Lead - cURL
  • 110.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 New Lead - cURL $baseUrl = 'https://api.followupboss.com/v1'; $headers = [ 'Accept: application/json', 'Content-Type: application/json' ]; $payload = json_encode([ 'source' => 'longhornphp.com', 'system' => 'LonghornPHP', 'type' => 'General Inquiry', 'person' => [ 'firstName' => 'Fox', 'lastName' => 'Mulder', 'source' => 'cURL', 'emails' => [ ['value' => 'fox.mulder@fbi.gov'], ['value' => 'spooky@iwanttobelieve.org'] ] ] ]);
  • 111.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 New Lead - cURL $handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $baseUrl . '/events'); curl_setopt($handle, CURLOPT_HTTPHEADER, $headers); curl_setopt($handle, CURLOPT_USERPWD, 'my_api_key:'); curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 2); curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); curl_setopt($handle, CURLINFO_HEADER_OUT, 1); curl_setopt($handle, CURLOPT_POST, 1); curl_setopt($handle, CURLOPT_POSTFIELDS, $payload); $response = curl_exec($handle); curl_close($handle); echo $response;
  • 112.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 New Lead - Guzzle use GuzzleHttpClient; $client = new Client([ 'base_uri' => 'https://api.followupboss.com/v1/', 'auth' => ['my_api_key', ''] ]); try { $response = $client->post( 'events', ['json' => $leadData, 'headers' => $headers] ); $lead = json_decode($response->getBody()->getContents(), true); print_r($lead); } catch (Exception $e) { echo $e->getMessage(); }
  • 113.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Get /people - via Guzzle $client = new GuzzleHttpClient([ 'base_uri' => $baseUrl, 'auth' => ['my_api_key', ''] ]); try { $response = $client->get('people', ['headers' => $headers]); $people = json_decode($response->getBody()->getContents(), true); } catch (Exception $e) { // handle errors }
  • 114.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard $request = new GuzzleHttpPsr7Request('GET', 'people', $headers); try { $response = $client->send($request); $people = json_decode($response->getBody()->getContents(), true); $response = $client->send($request->withUri( new GuzzleHttpPsr7Uri('tasks') )); $tasks = json_decode($response->getBody()->getContents(), true); } catch (Exception $e) { // handle errors }
  • 115.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 JsonStream use GuzzleHttpPsr7StreamDecoratorTrait; use PsrHttpMessageStreamInterface; class JsonStream implements StreamInterface { use StreamDecoratorTrait; public function json(): ?array { return json_decode((string) $this->getContents(), true); } }
  • 116.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 JsonStream $stack = HandlerStack::create(); $stack->push(Middleware::mapResponse( function (ResponseInterface $response) { $jsonStream = new JsonStream($response->getBody()); return $response->withBody($jsonStream); } ));
  • 117.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard try { $response = $client->send($request); $people = $response->getBody()->json(); $response = $client->send($request->withUri(new Uri('tasks'))); $tasks = $response->getBody()->json(); } catch (Exception $e) { // handle errors }
  • 118.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard $request = new Request('GET', 'people', $headers); $response = $client->send($request); $people = $response->getBody()->json(); $response = $client->send($request->withUri(new Uri('tasks'))); $tasks = $response->getBody()->json(); $response = $client->send($request->withUri(new Uri('events'))); $events = $response->getBody()->json(); $response = $client->send($request->withUri(new Uri('calls'))); $calls = $response->getBody()->json();
  • 119.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Async Requests
  • 120.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Blocking Requests $client = new GuzzleHttpClient; $delays = [5000, 2500, 1000, 500, 0]; foreach ($delays as $delay) { echo "Making request [delay = $delay]n"; $client->get('https://mockbin.org/delay/' . $delay); echo "Response received [delay = $delay]n"; }
  • 121.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Blocking Requests
  • 122.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Blocking Requests
  • 123.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Concurrent Requests $client = new GuzzleHttpClient; $delays = [5000, 2500, 1000, 500, 0]; $promises = []; foreach ($delays as $delay) { echo "Making request [delay = $delay]n"; $url = 'https://mockbin.org/delay/' . $delay; $promises[] = $client->getAsync($url) ->then(function() use ($delay) { echo "Response received [delay = $delay]n"; }); } GuzzleHttpPromiseunwrap($promises);
  • 124.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Concurrent Requests
  • 125.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Concurrent Requests
  • 126.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 How do we send async requests? $client->send($request)
  • 127.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 How do we send async requests? $client->send($request) $client->sendAsync($request)
  • 128.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is a Promise?
  • 129.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is a Promise?
  • 130.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 What is a Promise? "A Promise is an object representing the eventual result of an asynchronous operation" https://amphp.org/amp/promises/
  • 131.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Promises $promise = $client->sendAsync($request); $promise->then( function (ResponseInterface $response) { echo $response->getStatusCode() . "n"; }, function (RequestException $e) { echo $e->getMessage() . "n"; echo $e->getRequest()->getMethod(); } );
  • 132.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Promises $promise = $client->sendAsync($request); $promise = $promise->then( function (ResponseInterface $response) { return json_decode( $response->getBody()->getContents(), true ); });
  • 133.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Promises $promise = $client->sendAsync($request); $result = $promise->then( function (ResponseInterface $response) { return json_decode( $response->getBody()->getContents(), true ); }) ->wait();
  • 134.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Promises $promise = $client->sendAsync($request); $result = $promise->then( function (ResponseInterface $response) { return json_decode( $response->getBody()->getContents(), true ); }) ->wait();
  • 135.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Promises $promise = $client->sendAsync($request); $result = $promise->then( function (ResponseInterface $response) { return json_decode( $response->getBody()->getContents(), true ); }) ->wait();
  • 136.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard $request = new Request('GET', 'people', $headers); $response = $client->send($request); $people = $response->getBody()->json(); $response = $client->send($request->withUri(new Uri('tasks'))); $tasks = $response->getBody()->json(); $response = $client->send($request->withUri(new Uri('events'))); $events = $response->getBody()->json(); $response = $client->send($request->withUri(new Uri('calls'))); $calls = $response->getBody()->json();
  • 137.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Async $endpoints = ['people', 'tasks', 'events', 'calls']; $promises = []; foreach ($endpoints as $endpoint) { $request = new Request('GET', $endpoint, $headers); $promises[$endpoint] = $client->sendAsync($request) ->then(function (ResponseInterface $response) use ($endpoint) { $json = $response->getBody()->json(); return $json[$endpoint] ?? []; }); } try { $results = GuzzleHttpPromiseunwrap($promises); } catch (Exception $e) { echo $e->getMessage(); }
  • 138.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Async $endpoints = ['people', 'tasks', 'events', 'calls']; $promises = []; foreach ($endpoints as $endpoint) { $request = new Request('GET', $endpoint, $headers); $promises[$endpoint] = $client->sendAsync($request) ->then(function (ResponseInterface $response) use ($endpoint) { $json = $response->getBody()->json(); return $json[$endpoint] ?? []; }); } try { $results = GuzzleHttpPromiseunwrap($promises); } catch (Exception $e) { echo $e->getMessage(); }
  • 139.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Async $endpoints = ['people', 'tasks', 'events', 'calls']; $promises = []; foreach ($endpoints as $endpoint) { $request = new Request('GET', $endpoint, $headers); $promises[$endpoint] = $client->sendAsync($request) ->then(function (ResponseInterface $response) use ($endpoint) { $json = $response->getBody()->json(); return $json[$endpoint] ?? []; }); } try { $results = GuzzleHttpPromiseunwrap($promises); } catch (Exception $e) { echo $e->getMessage(); }
  • 140.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Async $endpoints = ['people', 'tasks', 'events', 'calls']; $promises = []; foreach ($endpoints as $endpoint) { $request = new Request('GET', $endpoint, $headers); $promises[$endpoint] = $client->sendAsync($request) ->then(function (ResponseInterface $response) use ($endpoint) { $json = $response->getBody()->json(); return $json[$endpoint] ?? []; }); } try { $results = GuzzleHttpPromiseunwrap($promises); } catch (Exception $e) { echo $e->getMessage(); }
  • 141.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 GuzzleHttpPromisefunctions
  • 142.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 GuzzleHttpPromisefunctions // Waits on all of the provided promises // and returns the fulfilled values. unwrap($promises);
  • 143.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 GuzzleHttpPromisefunctions // Waits on all of the provided promises // and returns the fulfilled values. unwrap($promises); // Returns a promise that is fulfilled when // all the items in the array are fulfilled. all($promises);
  • 144.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 GuzzleHttpPromisefunctions // Waits on all of the provided promises // and returns the fulfilled values. unwrap($promises); // Returns a promise that is fulfilled when // all the items in the array are fulfilled. all($promises); // Returns a promise that is fulfilled when // all of the provided promises have // been fulfilled or rejected. settle($promises);
  • 145.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Coroutines "Coroutines are a general control structure whereby flow control is cooperatively passed between two different routines without returning." - user21714 https://stackoverflow.com/questions/553704/what-is-a-coroutine
  • 146.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Coroutines "Coroutines are interruptible functions" https://amphp.org/amp/coroutines/
  • 147.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Coroutines $endpoints = ['people', 'tasks', 'events', 'calls']; $promises = []; foreach ($endpoints as $endpoint) { $promises[$endpoint] = new GuzzleHttpPromiseCoroutine( function() use ($client, $endpoint, $headers) { $request = new Request('GET', $endpoint, $headers); $response = yield $client->sendAsync($request); $json = $response->getBody()->json(); yield $json[$endpoint] ?? []; } ); }
  • 148.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Coroutines new GuzzleHttpPromiseCoroutine( function() use ($client, $endpoint, $headers) { $request = new Request('GET', $endpoint, $headers); $response = yield $client->sendAsync($request); $json = $response->getBody()->json(); yield $json[$endpoint] ?? []; } );
  • 149.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Coroutines new GuzzleHttpPromiseCoroutine( function() use ($client, $endpoint, $headers) { $request = new Request('GET', $endpoint, $headers); $response = yield $client->sendAsync($request); $json = $response->getBody()->json(); yield $json[$endpoint] ?? []; } );
  • 150.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Coroutines new GuzzleHttpPromiseCoroutine( function() use ($client, $endpoint, $headers) { $request = new Request('GET', $endpoint, $headers); $response = yield $client->sendAsync($request); $json = $response->getBody()->json(); yield $json[$endpoint] ?? []; } );
  • 151.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Dashboard - Coroutines new GuzzleHttpPromiseCoroutine( function() use ($client, $endpoint, $headers) { $request = new Request('GET', $endpoint, $headers); $response = yield $client->sendAsync($request); $json = $response->getBody()->json(); yield $json[$endpoint] ?? []; } );
  • 152.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Why use coroutines?
  • 153.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Why use coroutines? • Avoid multiple/chained callbacks
  • 154.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Why use coroutines? • Avoid multiple/chained callbacks • Code flows more naturally
  • 155.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Why use coroutines? • Avoid multiple/chained callbacks • Code flows more naturally • Use try/catch to handle errors
  • 156.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Testing
  • 157.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Mock Handler // Create a mock and queue two responses. $mock = new GuzzleHttpHandlerMockHandler([ new Response(200, ['X-Foo' => 'Bar']), new Response(202, ['Content-Length' => 0]), new RequestException('Server error', new Request('GET', 'test')) ]); $handler = GuzzleHttpHandlerStack::create($mock); $client = new GuzzleHttpClient(['handler' => $handler]); // The first request is intercepted with the first response. echo $client->request('GET', '/')->getStatusCode(); //> 200 // The second request is intercepted with the second response. echo $client->request('GET', '/')->getStatusCode(); //> 202
  • 158.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 History Middleware $container = []; $history = Middleware::history($container); $mockHandler = new GuzzleHttpHandlerMockHandler([ new Response(200) ]); $stack = HandlerStack::create($mockHandler); $stack->push($history); $client = new Client([ 'base_uri' => $baseUrl, 'handler' => $stack ]);
  • 159.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 History Middleware $client->request('GET', 'people', ['headers' => $headers]); foreach ($container as $transaction) { echo $transaction['request']->getMethod(); if ($transaction['response']) { echo $transaction['response']->getStatusCode(); } elseif ($transaction['error']) { echo $transaction['error']; } }
  • 160.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 History Middleware $this->assertEquals( 'Swade-Co', $transaction['request']->getHeader('X-System')[0] );
  • 161.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 162.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 163.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978
  • 164.
    Steven Wade -@stevenwadejrhttps://joind.in/talk/41978 Comments/Questions/Concerns/Hate Mail Twitter: @stevenwadejr Email: stevenwadejr@gmail.com https://joind.in/talk/41978