SlideShare a Scribd company logo
1 of 164
Download to read offline
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

More Related Content

Similar to Diving into guzzle

Nuts and Bolts of WebSocket Devoxx 2014
Nuts and Bolts of WebSocket Devoxx 2014Nuts and Bolts of WebSocket Devoxx 2014
Nuts and Bolts of WebSocket Devoxx 2014Arun Gupta
 
Clug 2014-09 - chef community resources
Clug 2014-09 - chef community resourcesClug 2014-09 - chef community resources
Clug 2014-09 - chef community resourcesZachary Stevens
 
Start! ATS programming
Start! ATS programmingStart! ATS programming
Start! ATS programmingKiwamu Okabe
 
Webapp security testing
Webapp security testingWebapp security testing
Webapp security testingTomas Doran
 
Webapp security testing
Webapp security testingWebapp security testing
Webapp security testingTomas Doran
 
How to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & PushHow to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & PushMu Chun Wang
 
Developing client themes for theme review for WordCamp Edmonton
Developing client themes for theme review for WordCamp EdmontonDeveloping client themes for theme review for WordCamp Edmonton
Developing client themes for theme review for WordCamp EdmontonCurtis McHale
 
Free The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainFree The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainKen Collins
 
Build and Deploy Pipelines, or How I Learned to Stop Worrying and Love Deplo...
Build and Deploy Pipelines, or How I Learned to Stop Worrying  and Love Deplo...Build and Deploy Pipelines, or How I Learned to Stop Worrying  and Love Deplo...
Build and Deploy Pipelines, or How I Learned to Stop Worrying and Love Deplo...Paul Everton
 
Artem Denysov "Easy ways to speed up your web application"
Artem Denysov "Easy ways to speed up your web application"Artem Denysov "Easy ways to speed up your web application"
Artem Denysov "Easy ways to speed up your web application"OdessaJS Conf
 
atomPub, ruby y la api de 11870
atomPub, ruby y la api de 11870atomPub, ruby y la api de 11870
atomPub, ruby y la api de 11870David Calavera
 
Oracle REST Data Services: POUG Edition
Oracle REST Data Services: POUG EditionOracle REST Data Services: POUG Edition
Oracle REST Data Services: POUG EditionJeff Smith
 
WebSocket For Web Rubyists
WebSocket For Web RubyistsWebSocket For Web Rubyists
WebSocket For Web RubyistsMu-Fan Teng
 
Concurrent Ruby Application Servers
Concurrent Ruby Application ServersConcurrent Ruby Application Servers
Concurrent Ruby Application ServersLin Jen-Shin
 
Progressively Enhancing WordPress Themes
Progressively Enhancing WordPress ThemesProgressively Enhancing WordPress Themes
Progressively Enhancing WordPress ThemesDigitally
 
6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friend6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friendForrest Chang
 
HTML5 WebSocket for the Real-Time Web and the Internet of Things
HTML5 WebSocket for the Real-Time Weband the Internet of ThingsHTML5 WebSocket for the Real-Time Weband the Internet of Things
HTML5 WebSocket for the Real-Time Web and the Internet of ThingsPeter Moskovits
 

Similar to Diving into guzzle (20)

Nuts and Bolts of WebSocket Devoxx 2014
Nuts and Bolts of WebSocket Devoxx 2014Nuts and Bolts of WebSocket Devoxx 2014
Nuts and Bolts of WebSocket Devoxx 2014
 
Redis: Need for speed
Redis: Need for speedRedis: Need for speed
Redis: Need for speed
 
Clug 2014-09 - chef community resources
Clug 2014-09 - chef community resourcesClug 2014-09 - chef community resources
Clug 2014-09 - chef community resources
 
Start! ATS programming
Start! ATS programmingStart! ATS programming
Start! ATS programming
 
Webapp security testing
Webapp security testingWebapp security testing
Webapp security testing
 
Webapp security testing
Webapp security testingWebapp security testing
Webapp security testing
 
Hidden Features in HTTP
Hidden Features in HTTPHidden Features in HTTP
Hidden Features in HTTP
 
How to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & PushHow to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & Push
 
Developing client themes for theme review for WordCamp Edmonton
Developing client themes for theme review for WordCamp EdmontonDeveloping client themes for theme review for WordCamp Edmonton
Developing client themes for theme review for WordCamp Edmonton
 
Social Bookmark Api
Social Bookmark ApiSocial Bookmark Api
Social Bookmark Api
 
Free The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainFree The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own Domain
 
Build and Deploy Pipelines, or How I Learned to Stop Worrying and Love Deplo...
Build and Deploy Pipelines, or How I Learned to Stop Worrying  and Love Deplo...Build and Deploy Pipelines, or How I Learned to Stop Worrying  and Love Deplo...
Build and Deploy Pipelines, or How I Learned to Stop Worrying and Love Deplo...
 
Artem Denysov "Easy ways to speed up your web application"
Artem Denysov "Easy ways to speed up your web application"Artem Denysov "Easy ways to speed up your web application"
Artem Denysov "Easy ways to speed up your web application"
 
atomPub, ruby y la api de 11870
atomPub, ruby y la api de 11870atomPub, ruby y la api de 11870
atomPub, ruby y la api de 11870
 
Oracle REST Data Services: POUG Edition
Oracle REST Data Services: POUG EditionOracle REST Data Services: POUG Edition
Oracle REST Data Services: POUG Edition
 
WebSocket For Web Rubyists
WebSocket For Web RubyistsWebSocket For Web Rubyists
WebSocket For Web Rubyists
 
Concurrent Ruby Application Servers
Concurrent Ruby Application ServersConcurrent Ruby Application Servers
Concurrent Ruby Application Servers
 
Progressively Enhancing WordPress Themes
Progressively Enhancing WordPress ThemesProgressively Enhancing WordPress Themes
Progressively Enhancing WordPress Themes
 
6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friend6 reasons Jubilee could be a Rubyist's new best friend
6 reasons Jubilee could be a Rubyist's new best friend
 
HTML5 WebSocket for the Real-Time Web and the Internet of Things
HTML5 WebSocket for the Real-Time Weband the Internet of ThingsHTML5 WebSocket for the Real-Time Weband the Internet of Things
HTML5 WebSocket for the Real-Time Web and the Internet of Things
 

Recently uploaded

WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentationphoebematthew05
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 

Recently uploaded (20)

WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentation
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 

Diving into guzzle

  • 1. Diving Into Guzzle: Getting the 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