Chapter 11. Talking to Other Websites and Services

Previous chapters discussed external sources of data such as databases and files. This chapter is about another important external source of data: other websites. PHP programs are often clients of other sites or APIs that offer up data that you need. Your website could itself serve up data to another site that needs it. This chapter shows how to retrieve external URLs and access APIs. It also explains what you need to do to serve API requests to others.

The first section shows how to use PHP’s built-in file access functions with URLs instead of filenames. This is a convenient option for quick and easy remote URL access. For more power and flexibility, though, use PHP’s cURL extension, discussed in “Comprehensive URL Access with cURL”. The cURL functions let you control many different aspects of the requests you’re making.

Serving up API responses instead of web pages from your PHP program is the focus of “Serving API Requests”. These responses are similar to standard HTML pages but have some important differences.

Simple URL Access with File Functions

An extremely convenient aspect of file access functions like file_get_contents() is that they understand URLs in addition to local filenames. Grabbing a remote URL and putting it into a string is just a matter of handing that URL to file_get_contents().

Example 11-1 uses file_get_contents() to display an interesting fact from the website numbersapi.com about September 27th.

Example 11-1. Retrieving a URL with file_get_contents()
Did you know that <?= file_get_contents('http://numbersapi.com/09/27') ?>

The Numbers API knows a lot of facts about each day, but for me the result of Example 11-1 looked like this:

Did you know that September 27th is the day in 1961 that Sierra Leone 
joins the United Nations.

The http_build_query() function is useful when you need to build an API URL that includes query string parameters. Give http_build_query() an associative array of parameter names and values and it gives you back a string of key=value pairs joined by & and properly encoded—exactly what you need for a URL.

The United States Department of Agriculture has a nifty API on top of its National Nutrient Database. This NDB API is free and easy to use.

Warning

The NDB API used for examples in this chapter requires that requests have a parameter named api_key whose value is a distinct API key that you get by signing up for the API. To get your own API key, visit https://api.data.gov/signup/. It’s free, quick, and requires you to provide minimal information—just your name and email address.

The examples in this chapter use the constant NDB_API_KEY in place of an actual API key. To run the examples on your own, you can either replace the NDB_API_KEY with a string containing your own API key, or use define() to set the NDB_API_KEY constant equal to the value of your API key. For example, if your API key were 273bqhebrfkhuebf, you’d put this line at the top of your code:

define('NDB_API_KEY','273bqhebrfkhuebf');

Example 11-2 uses the NDB search API to find some information about black pepper. This API returns information about each food in the database whose name matches what’s in the q query string parameter.

Example 11-2. Putting query string parameters in an API URL
$params = array('api_key' => NDB_API_KEY,
                'q' => 'black pepper',
                'format' => 'json');

$url = "http://api.nal.usda.gov/ndb/search?" . http_build_query($params);

The $url variable in Example 11-2 is set to something like the following (the actual value depends on your API key):

http://api.nal.usda.gov/ndb/search?
api_key=j724nbefuy72n4&q=black+pepper&format=json

Each key and value in the $params array has been put together with = and &, and special characters, such as the space in black pepper, have been encoded.

Passing such a URL to file_get_contents() makes the API call. In this case, the API returns JSON, so the return value of file_get_contents() is this string:

{
    "list": {
        "q": "black pepper",
        "sr": "27",
        "start": 0,
        "end": 1,
        "total": 1,
        "group": "",
        "sort": "r",
        "item": [
            {
                "offset": 0,
                "group": "Spices and Herbs",
                "name": "Spices, pepper, black",
                "ndbno": "02030"
            }
        ]
    }
}

Because file_get_contents() returns the response from retrieving the URL as a string, it’s a snap to pass that string to other functions that further transform it. For example, pass the API response above to json_decode() to transform the JSON into a PHP data structure you can manipulate. Example 11-3 prints out the NDB ID number for each matching food item.

Example 11-3. Decoding a JSON API response
$params = array('api_key' => NDB_API_KEY,
                'q' => 'black pepper',
                'format' => 'json');

$url = "http://api.nal.usda.gov/ndb/search?" . http_build_query($params);
$response = file_get_contents($url);
$info = json_decode($response);

foreach ($info->list->item as $item) {
    print "The ndbno for {$item->name} is {$item->ndbno}.\n";
}

The json_decode() function turns JSON objects into PHP objects and JSON arrays into PHP arrays. The top-level item in the response is an object. This is the return value from json_decode() and is assigned to $info. That object has a list property that is another object. The list object can be referred to as $info->list. That list object has an array property named item whose elements hold the details about the matching foods. So, the array that foreach() iterates over is $info->list->item. Each $item inside the foreach() loop is one object from that array. Example 11-3 prints:

The ndbno for Spices, pepper, black is 02030.

The NDB API calls made so far return JSON because of the format=json query string parameter. The API also supports specifying the response format by sending a Content-Type header.1 A header value of application/json tells the server to format the response as JSON.

To add headers to your HTTP request with file_get_contents(), you create a stream context. The PHP engine’s underlying mechanisms for flowing data into and out of your programs are called streams. A stream can be a local file, a remote URL, or another exotic place that produces or consumes data. The first argument to file_get_contents() is the stream’s target: the file or URL to read from or write to. Additional information about the reading or writing operation is expressed through the stream context, created by passing an associative array of the additional information to the stream_context_create() function.

Different kinds of streams support different kinds of options for their contexts. For the http stream, a header option gets a string value containing the names and values of any headers to send with the HTTP request. Example 11-4 shows how to create a stream context that includes an HTTP header and use it with file_get_contents().

Example 11-4. Sending HTTP headers with a stream context
// Just key and query term, no format specified in query string
$params = array('api_key' => NDB_API_KEY,
                'q' => 'black pepper');
$url = "http://api.nal.usda.gov/ndb/search?" . http_build_query($params);

// Options are to set a Content-Type request header
$options = array('header' => 'Content-Type: application/json');
// Create a context for an 'http' stream
$context = stream_context_create(array('http' => $options));

// Pass the context as the third argument to file_get_contents
print file_get_contents($url, false, $context);

In Example 11-4, the $options array contains the key/value pairs of options to set. The stream_context_create() function needs to be told which kind of stream it’s creating a context for, so its argument is an array whose key is the stream type (http) and the value is the options to set.

The second argument of file_get_contents() indicates whether the function should pay attention to the PHP engine’s include path when looking for a file. This is irrelevant with HTTP, so false is supplied for a value. The context is the third argument to file_get_contents().

The context is also how you send a POST request with file_get_contents(). The method context option controls the request method, and the content context option contains any request body to send. The content must be formatted properly for the content type you specify as a header.

We can’t use the NDB API for POST, since it just tells us nutrition data via GET. It doesn’t allow us to send new data in. Example 11-5 uses file_get_contents() to send a POST request to an example URL. This request acts just like a form submission sending two form variables: name and smell.

Example 11-5. Sending a POST request with file_get_contents()
$url = 'http://php7.example.com/post-server.php';

// Two variables to send via POST
$form_data = array('name' => 'black pepper',
                   'smell' => 'good');

// Set the method, content type, and content
$options = array('method' => 'POST',
                 'header' => 'Content-Type: application/x-www-form-urlencoded',
                 'content' => http_build_query($form_data));
// Create a context for an 'http' stream
$context = stream_context_create(array('http' => $options));

// Pass the context as the third argument to file_get_contents.
print file_get_contents($url, false, $context);

In Example 11-5, the method stream context option ensures that this is a POST request. The value you supply here is used verbatim by the PHP engine to make the request, so be sure to make it all capital letters. The value for the Content-Type header is the standard value that web browsers use for regular form data. It corresponds to data formatted like query string parameters but sent in the request body. Conveniently, this lets us use http_build_query() to construct the properly formatted request body.

Tip

In Example 11-5, as in other examples in this section, php7.example.com is used as a sample hostname. You must change this to a real hostname (preferably of your own web server!) to make the code work.

If you need to send a different kind of data in your POST request, just change the value of the Content-Type header and how you format the request content. For example, to send JSON, change the header option to Content-Type: application/json and change the content option to json_encode($form_data).

More information about the PHP engine’s different stream types and other supported context options is available at http://www.php.net/context.

Although the simplicity of retrieving URLs with built-in file access functions is fantastic, these functions do not make life simple if there is an error when making the request. When that happens, file_get_contents() returns false and the PHP engine generates an error message that looks like failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found. Having more control over what to do when a request is not successful is one good reason to use the cURL functions instead.

Comprehensive URL Access with cURL

The file_get_contents() function, especially when combined with context options, lets you make a wide variety of HTTP requests. But when you really need control over the details of your HTTP requests and responses, turn to PHP’s cURL functions. By using a powerful underlying library, libcurl, these functions give you access to all aspects of your HTTP requests and responses.

Retrieving URLs via GET

Accessing a URL with cURL begins by passing the URL you want to access to curl_init(). This function doesn’t immediately go out and retrieve the URL; it returns a handle, which is a variable that you pass to other functions to set options and configure how cURL should work. You can have multiple handles in different variables at the same time. Each handle controls a different request.

The curl_setopt() function controls the PHP engine’s behavior when retrieving the URL, and the curl_exec() function actually causes the request to be retrieved. Example 11-6 uses cURL to retrieve the numbersapi.com URL from Example 11-1.

Example 11-6. Retrieving a URL with cURL
<?php

$c = curl_init('http://numbersapi.com/09/27');
// Tell cURL to return the response contents as a string
// rather then printing them out immediately
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
// Execute the request
$fact = curl_exec($c);

?>
Did you know that <?= $fact ?>

In Example 11-6, the call to curl_setopt() sets the CURLOPT_RETURNTRANSFER option. This tells cURL that when it makes the HTTP request, it should return the response as a string. Otherwise, it prints out the response as it is retrieved. The curl_exec() function makes the request and returns the result.

Other cURL options let you set headers. Example 11-7 uses cURL functions to make the request from Example 11-4.

Example 11-7. Using cURL with query string parameters and headers
// Just key and query term, no format specified in query string
$params = array('api_key' => NDB_API_KEY,
                'q' => 'black pepper');
$url = "http://api.nal.usda.gov/ndb/search?" . http_build_query($params);

$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
print curl_exec($c);

In Example 11-7, the URL is constructed in a familiar way with http_build_query(). The query string parameters are part of the URL, so they go into the URL string passed to curl_init(). The new CURLOPT_HTTP_HEADER option sets the HTTP header to be sent with the request. If you have multiple headers, put multiple items in this array.

There are two kinds of errors to deal with from cURL requests. The first is an error from cURL itself. This could be something such as not finding the hostname, or not being able to make a connection to the remote server. If this kind of thing happens, curl_exec() returns false and curl_errno() returns an error code. The curl_error() function returns the error message that corresponds to the code.

The second kind of error is an error from the remote server. This happens if the URL you ask for isn’t found or the server has a problem producing a response to your request. cURL still considers this a successful request because the server returned something, so you need to check the HTTP response code to see if there’s a problem. The curl_getinfo() function returns an array of information about the request. One of the elements in that array is the HTTP response code.

Example 11-8 shows cURL request-making code that handles both kinds of errors.

Example 11-8. Handling errors with cURL
// A pretend API endpoint that doesn't exist 
$c = curl_init('http://api.example.com');
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($c);
// Get all the connection info, whether or not it succeeded
$info = curl_getinfo($c);

// Something went wrong with the connection
if ($result === false) {
    print "Error #" . curl_errno($c) . "\n";
    print "Uh-oh! cURL says: " . curl_error($c) . "\n";
}
// HTTP response codes in the 400s and 500s mean errors
else if ($info['http_code'] >= 400) {
    print "The server says HTTP error {$info['http_code']}.\n";
}
else {
    print "A successful result!\n";
}
// The request info includes timing statistics as well
print "By the way, this request took {$info['total_time']} seconds.\n";

Example 11-8 starts out with a standard cURL request. After making the request, it stores the request info from curl_getinfo() into $info. The curl_getinfo() function needs to be passed the cURL handle it should operate on, just like curl_errno() and curl_error(). This is necessary in order to return information about the correct request.

The host api.example.com doesn’t actually exist, so cURL can’t connect to it to make a request. So, curl_exec() returns false. Example 11-8 prints:

Error #6
Uh-oh! cURL says: Could not resolve host: api.example.com
By the way, this request took 0.000146 seconds.

The PHP manual page about curl_errno() has a list of all the cURL error codes.

If the request made it to the server but the server returned an error, then $result is not false, but holds whatever response the server sent back. This response code is in the http_code element of the $info array. If Example 11-8 encountered an HTTP 404 error, which means that the server couldn’t find the page the request asked for, then the example would print:

The server says HTTP error 404.
By the way, this request took 0.00567 seconds.

Both outputs from the example also include the total time it took to make the request. This is another handy bit of request data in the $info array. The PHP manual page for curl_getinfo() lists all the elements of this array.

Retrieving URLs via POST

To use the POST method with cURL, adjust the settings to change the request method and supply the request body data. The CURLOPT_POST setting tells cURL you want a POST request, and the CURLOPT_POSTFIELDS setting holds the data you want to send. Example 11-9 shows how to make a POST request with cURL.

Example 11-9. Making a POST request with cURL
$url = 'http://php7.example.com/post-server.php';

// Two variables to send via POST
$form_data = array('name' => 'black pepper',
                   'smell' => 'good');

$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
// This should be a POST request
curl_setopt($c, CURLOPT_POST, true);
// This is the data to send
curl_setopt($c, CURLOPT_POSTFIELDS, $form_data);

print curl_exec($c);

In Example 11-9, you don’t need to set the Content-Type header or format the data you’re sending. cURL takes care of that for you.

However, if you want to send a different content type than regular form data, you need to do a little more work. Example 11-10 shows how to send JSON via a POST request with cURL.

Example 11-10. Sending JSON via POST with cURL
$url = 'http://php7.example.com/post-server.php';

// Two variables to send as JSON via POST
$form_data = array('name' => 'black pepper',
                   'smell' => 'good');

$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
// This should be a POST request
curl_setopt($c, CURLOPT_POST, true);
// This is a request containing JSON
curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
// This is the data to send, formatted appropriately
curl_setopt($c, CURLOPT_POSTFIELDS, json_encode($form_data));

print curl_exec($c);

In Example 11-10, the CURLOPT_HTTPHEADER setting tells the server that the request body is JSON, not regular form data. Then, the value of CURLOPT_POSTFIELDS is set to json_encode($form_data) so that the request body is indeed JSON.

Using Cookies

If the response to a cURL request includes a header that sets a cookie, cURL doesn’t do anything special with that header by default. But cURL does give you a few configuration settings that let you track cookies, even across different PHP programs or executions of the same program.

Example 11-11 is a simple page that maintains a cookie, c. Each time the page is requested, the response includes a c cookie whose value is one greater than whatever value is supplied for the c cookie in the request. If no c cookie is sent, then the response sets the c cookie to 1.

With no additional configuration, cURL doesn’t keep track of the cookie sent back in Example 11-11. In Example 11-12, curl_exec() is called twice on the same handle, but the cookie sent back in the response to the first request is not sent on the second request.

Tip

In Example 11-12, as in the other client examples in this section, the program is accessible at http://php7.example.com/cookie-server.php. To run the code yourself, change the URL to point to your PHP server.

Example 11-12 prints:

Cookies: 0
Cookies: 0

Both requests get a response of Cookies: 0 because cURL sent no Cookie header with the request.

Enabling cURL’s cookie jar tells it to keep track of cookies. To keep track of cookies within the lifetime of a specific cURL handle, set CURLOPT_COOKIEJAR to true, as in Example 11-13.

Example 11-13 prints:

Cookies: 0
Cookies: 1
c: 1

In Example 11-13, cURL keeps track of cookies sent in response to a request as long as the handle for that cURL request exists in your program. The second time curl_exec() is called for the handle $c, the cookie set in the first response is used.

In this mode, the cookie jar only tracks cookies within a handle. Changing the value of CURLOPT_COOKIEJAR to a filename tells cURL to write the cookie values to that file. Then you can also provide that filename as the value for CURLOPT_COOKIEFILE. Before sending a request, cURL reads in any cookies from the CURLOPT_COOKIEFILE file and uses them in subsequent requests. Example 11-14 shows the cookie jar and cookie file in action.

The first time Example 11-14 is run, it prints:

Cookies: 0

The second time Example 11-14 is run, it prints:

Cookies: 1
c: 1

The third time Example 11-14 is run, it prints:

Cookies: 1
c: 2

And so forth. Each time the program runs, it looks for a saved.cookies file, loads up any cookies stored in the file, and uses them for the request. After the request, it saves any cookies back to the same file because the CURLOPT_COOKIEFILE setting has the same value as the CURLOPT_COOKIEJAR setting. This updates the saved cookies file so it’s ready with the new value the next time the program runs.

If you’re writing a program that mimics a user logging in with a web browser and then making requests, the cookie jar is a very convenient way to have all of the server-sent cookies accompany the requests cURL makes.

Retrieving HTTPS URLs

To retrieve URLs that use the https protocol, you do all the same things with cURL that you’d do with regular http URLs. However, there are some security settings that are important to get right. Usually, your defaults will be OK. This section explains those values so you can be sure they’re correct and understand why you shouldn’t change them.

When a web client retrieves an https URL, the security provided has two separate features. One is identity verification: the server asserts that it is really the server that should be handling the URL (based on the hostname). The other is protection from eavesdropping: anybody who scoops up the conversation between the client and the server sees just a meaningless jumble of characters instead of the real request and response.

The CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST settings control whether cURL is strict about identify verification. If CURLOPT_SSL_VERIFYPEER is set to false, or CURLOPT_SSL_VERIFYHOST is set to something other than 2, then cURL will skip essential steps that make sure the server is who it says it is.

If you’re using cURL version 7.10 or later, then CURLOPT_SSL_VERIFYPEER is turned on by default. If you’re using cURL version 7.28.1 or later, then cURL won’t let you change CURLOPT_SSL_VERIFYHOST to a value other than 2.

Use the curl_version() function to find out what version your PHP installation has. This function returns an associative array of information about the installed version of cURL’s capabilities. The version element of the array contains the version of cURL that the PHP engine is relying on.

There are also different versions of the secure protocol that web clients and servers use to implement HTTPS URLs. Which protocol version cURL uses is controlled by the CURLOPT_SSLVERSION setting. There should be no need to change this value. The default value (which you can explicitly set with the constant CURL_SSLVERSION_DEFAULT) uses the most up-to-date and secure version of the protocol available to the version of cURL you have.

Serving API Requests

Your PHP program can serve API requests to clients as well. Instead of generating a plain HTML page, you generate whatever data is appropriate to the API call. Additionally, you may need to manipulate the HTTP response code and response headers you send.

To send an HTTP header along with your response, use the header() function. Example 11-15 is a tiny clock API in PHP. It serves up the current time in JSON format.

Example 11-15. Serving a JSON response
$response_data = array('now' => time());
header('Content-Type: application/json');
print json_encode($response_data);

The call to header() in Example 11-15 adds a header line to the HTTP response that the PHP engine generates. The header line is whatever you pass to the function. The code uses the json_encode() function to generate the JSON that makes up the response. This function is the opposite of json_decode(). You give it a PHP data type (string, number, object, array, etc.) and it returns a string containing the JSON representation of what was passed in. Example 11-15 generates an HTTP response that looks like this:

HTTP/1.1 200 OK
Host: www.example.com
Connection: close
Content-Type: application/json

{"now":1962258300}

The first four lines are the response headers. A few get added automatically by your web server. The Content-Type: application/json line is from the call to header(). After the blank line comes the request body. This is the stuff you see in your web browser (or, if it’s HTML, you see the results of your web browser rendering the HTML). In this case, the request body is a JSON object with one property, now, whose value is a timestamp corresponding to the current time. The value you get when you run this code will almost certainly be different because you are almost certainly running this code at a different time than the timestamp printed here. For more details on time handling in PHP, see Chapter 15.

The first line of the response headers contains the response code, which in this case is 200—which is HTTP-speak for “everything was fine.” To send a different response code, use the http_response_code() function. Example 11-16 is like Example 11-15 except it sends back a 403 status code (“Forbidden”) and an error response body unless a query string parameter named key is supplied with a value of pineapple.

Example 11-16. Changing the response code
if (! (isset($_GET['key']) && ($_GET['key'] == 'pineapple'))) {
    http_response_code(403);
    $response_data = array('error' => 'bad key');
}
else {
    $response_data = array('now' => time());
}
header('Content-Type: application/json');
print json_encode($response_data);

Without the proper query string parameter supplied, Example 11-16’s response looks like this:

HTTP/1.1 403 Forbidden
Host: www.example.com
Connection: close
Content-Type: application/json

{"error":"bad key"}

Example 11-4 used a Content-Type header to tell the NDB API to send back a response as JSON. To access incoming request headers from your PHP program, look in the $_SERVER array. Each incoming header is there. A header’s array key is HTTP_ followed by the header’s name in all uppercase and with any dashes (-) converted to underscores (_). For example, the value of an incoming Content-Type header would be in $_SERVER['HTTP_CONTENT_TYPE']. Example 11-17 examines the value of an incoming Accept header to determine how to format the output data.

Example 11-17. Examining a request header
<?php 

// Formats we want to support
$formats = array('application/json','text/html','text/plain');
// Response format if not specified
$default_format = 'application/json';

// Was a response format supplied?
if (isset($_SERVER['HTTP_ACCEPT'])) {
    // If a supported format is supplied, use it
    if (in_array($_SERVER['HTTP_ACCEPT'], $formats)) {
        $format = $_SERVER['HTTP_ACCEPT'];
    }
    // An unsupported format was supplied, so return an error
    else {
        // 406 means "You want a response in a format I can't generate"
        http_response_code(406);
        // Exiting now means no response body, which is OK
        exit();
    }
} else {
    $format = $default_format;
}

// Figure out what time it is
$response_data = array('now' => time());
// Tell the client what kind of content we're sending
header("Content-Type: $format");
// Print the time in a format-appropriate way
if ($format == 'application/json') {
    print json_encode($response_data);
}
else if ($format == 'text/html') { ?>
<!doctype html>
  <html>
    <head><title>Clock</title></head>
    <body><time><?= date('c', $response_data['now']) ?></time></body>
  </html>
<?php 
} else if ($format == 'text/plain') {
    print $response_data['now'];
}

If the incoming Accept header is application/json, text/html, or text/plain, then $format is set to the proper format to use. This value gets put in the Content-Type header of the response and is used to generate a format-appropriate output. If no Accept header is supplied, a default of application/json is used. If any other value is supplied in the Accept header, then the program returns an empty body with a 406 error code. This tells the client that an invalid format was asked for.2

The $_SERVER array is also where you look to determine if the current request is a secure request; that is, if it was made with HTTPS. If the current request is secure, then $_SERVER['HTTPS'] is set to on. Example 11-18 checks if the current request was made over HTTPS and redirects to an HTTPS version of the current request’s URL if not.

Example 11-18. Checking for HTTPS
$is_https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on'));
if (! $is_https) {
    $newUrl = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    header("Location: $newUrl");
    exit();
}
print "You accessed this page over HTTPS. Yay!";

In Example 11-18, the first line determines whether the current request was over HTTPS by ensuring two things: first, that there is a value set for $_SERVER['HTTPS'] and second, that the value is on. If both of those things are not true, then an HTTPS version of the current URL is built by combining the right protocol (https://) with the current request’s hostname (the value of $_SERVER['HTTP_HOST']) and the current request’s path (the value of $_SERVER['REQUEST_URI']). If the request included any query string parameters, those are included in $_SERVER['REQUEST_URI'] as well. The Location header, sent by the header() function, redirects a web client to a new URL.

Chapter Summary

This chapter covered:

  • Retrieving URLs with file_get_contents()
  • Retrieving a URL that includes query string parameters
  • Decoding a JSON HTTP response
  • Understanding PHP’s stream contexts
  • Including additional headers when retrieving a URL
  • Retrieving URLs via the POST method with file_get_contents()
  • Retrieving URLs with cURL
  • Using query string parameters with cURL
  • Adding request headers with cURL
  • Handling errors from cURL requests
  • Retrieving URLs via the POST method with cURL
  • Keeping track of HTTP cookies with cURL
  • Using cURL securely with HTTPS
  • Serving non-HTML responses
  • Changing the HTTP response code.
  • Using values from HTTP request headers
  • Checking if a request is made with HTTPS

Exercises

  1. http://php.net/releases/?json is a JSON feed of the latest PHP releases. Write a program that uses file_get_contents() to retrieve this feed and print out the latest version of PHP released.
  2. Modify your program from the previous exercise to use cURL instead of file_get_contents().
  3. Write a web page that uses a cookie to tell the user when he last looked at the web page (you may find the date-and-time-handling functions described in Chapter 15 useful).
  4. A GitHub gist is a snippet of text or code that is easy to share. The GitHub API allows you to create gists without logging in. Write a program that creates a gist whose contents are the program you’re writing to create a gist. Note that the GitHub API requires you to set a User-Agent header in your HTTP API requests. The CURLOPT_USERAGENT setting can be used to set this header.

1 The HTTP specification says to use an Accept header for this, but that’s not how this particular API works.

2 Parsing real Accept headers is a bit more complicated because clients are allowed to send multiple formats and indicate which one they prefer. Look on GitHub for a complete solution to this process, which is called content negotiation.