As you probably know, website development can be broken up into 2 main areas:
- Front end (what the end user sees)
- Back end (what the server has to do in order to provide the requested data)
While front end development frequently uses several data sources to display a page, simple dynamic sites would only depend on data coming from a database hosted on the same server. As back end developers today, we are in need of retrieving data from a database server on a different host, or consuming an API from a third-party provider, while making it look like everything happened in our server.
PHP comes with the native cURL client (if enabled), that can be frightening to the newcomer or hard to use to consume SOAP services. Other frameworks implement their versions of HTTP, REST, and SOAP clients to some degree. However, if you do not wish to use cURL, you do not have a framework or you are not a fan of your framework’s solution, Guzzle to the rescue.
Guzzle is an independent HTTP client for PHP. Installing Guzzle is very easy: you’ll need to get composer first (https://www.sitepoint.com/php-dependency-management-with-composer/). Once you got composer installed and ready, create your basic composer.json
file, which would look like the following:
{
"name": "jd/guzzle demo",
"authors": [
{
"name": "John Doe",
"email": "john@doe.tst"
}
],
"require": {
}
}
Now just add the guzzle library to the composer.json
file in the require
section as follows:
{
"name": "jd/guzzle demo",
"authors": [
{
"name": "John Doe",
"email": "john@doe.tst"
}
],
"require": {
"guzzle/guzzle": "~3.7"
}
}
Having saved the file, you just need to run the composer install
command and you should be good to go!
Basics
As a cryptography enthusiast, searching for sources of entropy data for key generation is a hobby of mine. One good source is the ANU Quantum Random Numbers Server at https://qrng.anu.edu.au. Supposedly their RNG is based on quantum physics, so it is worth taking a look at when generating random data with a good entropy level.
Fortunately they have a public API we can consume, so here is where Guzzle will come in handy:
<?php
chdir(dirname(__DIR__));
require_once 'vendor/autoload.php';
use Guzzle\Http\Client;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\Request;
use Guzzle\Http\Message\Response;
/** @var $client Client */
$client = new Client("https://qrng.anu.edu.au");
The first two lines should be familiar to anyone using composer and its autoloading mechanisms. When we instantiate a Client object, we should pass as a constructor parameter the URL we want to connect to. Please notice that it does not use path and/or URL parameters – this will come in a minute.
/** @var $request Request */
$request = $client->get('/API/jsonI.php?length=10&type=uint8');
/** @var $response Response */
$response = $request->send();
/** @var $body EntityBody */
$body = $response->getBody(true);
We finally indicate the path (/API/jsonI.php
) and the URL parameters (length=10&type=uint8
) to the client, building an HTTP GET request via the $client->get()
method. If we wanted to build a POST request, the client object has post()
and many other HTTP request methods. For sending the request, the $request->send()
method should be invoked, catching the response in the $response
variable. Finally if we want to see what the response from the remote server was, we execute the $response->getBody()
method. TRUE can be sent as a parameter to the getBody()
method to get the result as a string.
Path and URL Parameters Manipulation
In the previous example, we used a string to indicate the path and URL parameters to build the request… This might not be the most elegant solution. We might decide to pass on certain URL parameters or use different paths according to certain conditions. We can easily accomplish proper handling of path and URL parameters by making some adjustments to the code.
/** @var $request Request */
$request = $client->createRequest();
$request->setPath('/API/jsonI.php');
$request->getQuery()
->set('length', 10)
->set('type', 'uint8');
First we create an empty request from the Guzzle client with the $client->getRequest()
method. The request object has several methods; in this case we’ll use the setPath()
and getQuery()
methods. setPath()
should be pretty straightforward; as for getQuery
, we use Guzzle’s fluent interface to set the URL parameters one by one.
Logging
When we develop applications that consume 3rd party services through a network, communication problems often arise, so it is very important to keep a log with all the requests and responses, at least during development stages so we can debug what is going on.
Guzzle provides several plugins. In the case of logging plugins, we have several adapters available at our disposal. For this example we’ll use monolog
To have monolog available in your project, you’ll have to modify your composer.json file and run the composer update command. Your composer.json file should look like the following:
{
"name": "jd/guzzle demo",
"authors": [
{
"name": "John Doe",
"email": "john@doe.tst"
}
],
"require": {
"guzzle/guzzle": "~3.7",
"monolog/monolog": "1.6.0"
}
}
To make Guzzle use monolog and write all requests and responses we send and receive, we need to make some adjustments to our code:
<?php
chdir(dirname(__DIR__));
require_once 'vendor/autoload.php';
//...
use Guzzle\Log\MessageFormatter;
use Guzzle\Log\MonologLogAdapter;
use Guzzle\Plugin\Log\LogPlugin;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
$logger = new Logger('client');
$logger->pushHandler(new StreamHandler('guzzle.log'));
$logAdapter = new MonologLogAdapter($logger);
$logPlugin = new LogPlugin($logAdapter, MessageFormatter::DEBUG_FORMAT);
/** @var $client Client */
$client = new Client("https://qrng.anu.edu.au");
$client->addSubscriber($logPlugin);
First we create a monolog instance with $logger = new Logger('client');
. Monolog posseses several handlers, in this case we will use the StreamHandler to write to a file named guzzle.log
. Then we create guzzle’s monolog adapter by instantiating a MonologLogAdapter object. Next we create a log plugin object using Guzzle’s log plugin manager LogPlugin. Please notice the MessageFormatter::DEBUG_FORMAT
constant which tells Guzzle to log full request and response messages. Please refer to Guzzle’s docs to see which message formatters are available. Last, we add the log plugin to the guzzle client by calling $client->addSubscriber($logPlugin);
Interacting with Github’s API
Let’s have a look at this code and then I´ll explain it line by line
<?php
chdir(dirname(__DIR__));
require_once 'vendor/autoload.php';
use Guzzle\http\Client;
use Guzzle\Log\MonologLogAdapter;
use Guzzle\Plugin\Log\LogPlugin;
use Guzzle\Log\MessageFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$client = new Client('https://api.github.com');
$log = new Logger('log');
$log->pushHandler(new StreamHandler('requests.log'));
$adapter = new MonologLogAdapter($log);
$logPlugin = new LogPlugin($adapter, MessageFormatter::DEBUG_FORMAT);
$client->addSubscriber($logPlugin);
$request = $client->post('authorizations', array(),
json_encode(
array(
'scopes' => array(
'public_repo',
'user',
'repo',
'gist'
),
'note' => 'auto client' . uniqid()
)));
$request->setAuth('john@doe.tst', 'yourpasswordhere');
$request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false);
$response = $request->send();
$body = json_decode($response->getBody(true));
$oauthToken = $body->token;
$request = $client->get('user/keys');
$query = $request->getQuery();
$query->add('access_token', $oauthToken);
$request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false);
try {
$response = $request->send();
$body = $response->getBody(true);
echo $body;
} catch (Exception $e) {
echo $request->getResponse()->getRawHeaders();
}
Detailed explanation follows.
$client = new Client('https://api.github.com');
We create a client which will use Github’s API base URL.
$log = new Logger('log');
$log->pushHandler(new StreamHandler('requests.log'));
$adapter = new MonologLogAdapter($log);
$logPlugin = new LogPlugin($adapter, MessageFormatter::DEBUG_FORMAT);
$client->addSubscriber($logPlugin);
As we previously saw, we set up logging capabilities, so we are able to debug communication issues.
$request = $client->post('authorizations', array(),
json_encode(
array(
'scopes' => array(
'public_repo',
'user',
'repo',
'gist'
),
'note' => 'auto client' . uniqid()
)));
First we want to request authorization to use Github’s API (http://developer.github.com/v3/oauth/) without having to use our username and password every time, so we need to perform an HTTP POST verb to https://api.github.com/authorizations
. Remember that we set up the base URL when we created our client, so we only need to set the path. To see a list of possible parameters that can be sent to Github’s API, please refer to their documentation.
$request->setAuth('john@doe.tst', 'yourpasswordhere');
$request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false);
For this one time, we will use HTTP Basic Authentication. Remember that the only mechanism preventing the sending of your credentials in plain text is HTTPS (as shown in the API url) – performing the request through something as insecure as HTTP could lead to someone intercepting your data. In the second line, we are configuring cURL not to verify the SSL certificate, as this can solve several communication issues.
$response = $request->send();
$body = json_decode($response->getBody(true));
Finally we send the request. The response is gotten through the getBody() method, the TRUE flag is used to configure Guzzle to return a plain string. Remember that Github’s API is RESTful, so everything will be JSON encoded, and thus we will decode it to an object using json_decode().
$oauthToken = $body->token;
The Oauth token will be contained inside the ‘token’ attribute. Please refer to Github’s API documentation for a detailed explanation of the response format.
$request = $client->get('user/keys');
$query = $request->getQuery();
$query->add('access_token', $oauthToken);
We will be performing a new request. This time we want to list our authorized keys that are configured in our account, mostly for accessing our repositories through SSH. This time an HTTP GET request has to be sent, but instead of using our credentials in plain text, we will use the Oauth token we were given; this token has to be added as a query parameter to the URL we are requesting.
$response = $request->send();
$body = $response->getBody(true);
echo $body;
Finally we send the request and get the response body, ready to be parsed and used for whichever purpose you like.
Conclusion
This little demonstration is just the tip of the iceberg of what Guzzle is capable of. With a nicely structured plugin system, request and response handling mechanism, you can finally tame the wide quantity of API’s the internet has to offer.
If you have a specific use case of Guzzle you’d like to demonstrate or just some general feedback, let us know!
Frequently Asked Questions (FAQs) about Guzzle PHP HTTP Client
What is the Guzzle PHP HTTP Client and why is it important?
Guzzle PHP HTTP Client is a robust and flexible library in PHP that aids in sending HTTP requests and integrating with web services. It’s a powerful tool that provides a simple interface for sending HTTP requests and processing HTTP responses, supporting many features like HTTP cookies, entity bodies, streaming, and more. It’s important because it simplifies the process of integrating your PHP application with RESTful web services.
How do I install Guzzle PHP HTTP Client?
Guzzle PHP HTTP Client can be installed via Composer, a tool for dependency management in PHP. You can install Guzzle by running the command composer require guzzlehttp/guzzle
. This command will install the latest version of Guzzle and its dependencies.
How do I send a GET request using Guzzle?
To send a GET request using Guzzle, you first need to create a new Guzzle HTTP client. Then, you can use the get
method to send a GET request. Here’s a basic example:$client = new \GuzzleHttp\Client();
$response = $client->get('http://httpbin.org/get');
How do I handle errors in Guzzle?
Guzzle throws exceptions for HTTP protocol errors. If you want to handle these exceptions, you can wrap your request in a try/catch block. For example:try {
$client = new \GuzzleHttp\Client();
$response = $client->get('http://httpbin.org/get');
} catch (\GuzzleHttp\Exception\RequestException $e) {
echo $e->getMessage();
}
How can I send asynchronous requests with Guzzle?
Guzzle supports sending asynchronous HTTP requests. This means you can send a request that doesn’t block the execution of your script and then handle the response when it’s ready. You can do this using the requestAsync
method. Here’s an example:$client = new \GuzzleHttp\Client();
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(
function ($response) {
echo 'Got a response! ' . $response->getStatusCode();
},
function ($exception) {
echo 'There was an error: ' . $exception->getMessage();
}
);
How do I send a POST request with Guzzle?
To send a POST request with Guzzle, you can use the post
method. You can also send data with your POST request by passing an array as the second argument to the post
method. Here’s an example:$client = new \GuzzleHttp\Client();
$response = $client->post('http://httpbin.org/post', [
'form_params' => [
'field_name' => 'abc',
'other_field' => '123',
]
]);
How do I send a request with custom headers using Guzzle?
You can send a request with custom headers by passing an array of headers as the second argument to the request method. Here’s an example:$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'http://httpbin.org/get', [
'headers' => [
'User-Agent' => 'testing/1.0',
]
]);
How do I handle responses in Guzzle?
When you send a request with Guzzle, you get a response object that you can interact with. You can get the status code, body, and headers of the response. Here’s an example:$client = new \GuzzleHttp\Client();
$response = $client->get('http://httpbin.org/get');
echo $response->getStatusCode(); // 200
echo $response->getBody(); // The body of the response
How do I send multiple requests concurrently with Guzzle?
Guzzle provides a way to send multiple requests concurrently using promises. You can use the each
function from the GuzzleHttp\Promise
namespace to send multiple requests and handle their responses as they complete. Here’s an example:$client = new \GuzzleHttp\Client();
$promises = [
$client->getAsync('http://httpbin.org/get'),
$client->getAsync('http://httpbin.org/get'),
];
$results = \GuzzleHttp\Promise\unwrap($promises);
How do I use Guzzle with a proxy?
You can use Guzzle with a proxy by passing the ‘proxy’ request option to the request method. Here’s an example:$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'http://httpbin.org/get', [
'proxy' => 'tcp://localhost:8125'
]);
Web application developer, database administrator, project lead in a variety of enterprise apps and now article author. Interested in database design and optimization. Amazed and involved in distributed systems development. Cryptography and information security fan.