Consuming REST with PHP and Streams
With RESTful services becoming ever more popular as a way of sharing information between systems, and PHP still widely adopted as the language of the web, these two technologies are regular bedfellows. As always with PHP, there’s more than one way to work with a RESTful service, but a great option is to use streams. The streams interface is more elegant than PHP’s clunky cURL extension, and is always included in PHP. Best of all, this is stream handling, so if a response is very large it can be processed in bite-sized chunks. Let’s look at some examples of consuming a real REST service with PHP streams. We’ll use GitHub as an example since they have a good RESTful service, great documentation, and are widely known.
Starting Simple with a GET Request
Let’s begin by grabbing a list of the gists associated with my GitHub account (a gist is like a pastebin, if you haven’t seen one before):
The response is an array of the publicly-visible gists on my account, each represented by an array and including information about the user that created them (me). The documentation for working with gists using GitHub’s API is here:http://developer.github.com/v3/gists/ The
file_get_contents() stream wrapper is by far the easiest and quickest way to grab content from a URL in PHP. There is so much more we can do with it though!
Write Operations with Stream Contexts
All the stream functions in PHP have support for a $context argument, which allows us to send more information about the stream we’re sending. For an HTTP or HTTPS stream like the ones in these examples, that means we can set the headers, verbs and body to send with our request. To try this out, we’ll create a gist on GitHub, and do so we need to be logged in. In API terms, that means we need to identify ourself when we make the request, and since GitHub uses Oauth2, we can just send a header containing a valid access token that I acquired by following their excellentation documentation which you can find at http://developer.github.com/v3/#authentication Our next request also needs to send some body data as well as auth information; this is the content for the new gist which we’ll POST to Github. We can set the verb, the body data, and the headers we need all in the context of the stream. Take a look at this example:
First of all I’m pulling in my access token from a separate include file (to avoid oversharing or having to revoke tokens). Then we set the URL and assemble the data we want to send. This will be different on different systems but I’m working off GitHub’s documentation for creating a gist: http://developer.github.com/v3/gists/#create-a-gist. Setting the context is probably the trickiest bit, and even then you can see the pieces clearly. Set that this should be a POST request, then give some extra headers; we set the
Content-Type because we’re sending JSON in the body of this request (the GitHub API works only in JSON), and the
Authorization header contains our access token so GitHub knows who we are. Finally we set the data we prepared earlier as the content for the stream. When the gist is created successfully, the response will give full information about this gist and its new URL, along with a 201 status code to tell you it was created (inspect this by checking the
$http_response_header> variable). If the first code sample was run again now, we’d see a new entry appear in our list, and it’s also visible on the website: [gists.png] We can work with gists and other types of API data programmatically, and PHP is a great tool for this.
Going Beyond GET and POST
The streams solution is a more friendly interface than the more traditional PHP curl, and it’s equally powerful as we’ve seen in the examples so far. It can be used to make requests using any HTTP verb, the only requirement is that both client and server should understand it. For example, if we wanted to update the gist that we just created, then we’d make a request to GitHub using the PATCH verb. PATCH isn’t supported everywhere, but GitHub have adopted it as a great way of updating records, including partial records, and this is becoming more popular in RESTful services. Here’s an example of how we might do that using the stream context:
The changes are accepted by GitHub and the response contains the updated gist, in this case the script just changes the description field.
PHP and Streams
The streams extension is core to PHP and so it will always be available, making it a great choice for code that needs to be deployed to a number of platforms. The interface is simple and elegant, so as a developer it’s easy to work with while at the same time being completely configurable, allowing even the more complex kinds of requests as we saw here. Best of all, if you’re dealing with very large responses, you can handle them in segments rather than loading the entire response into memory, as well as getting all the other features of streams such as being able to filter them as needed. There are many ways to make HTTP requests from PHP but streams are definitely one of the best, combining power with flexibility.
Lorna Jane Mitchell is a web development consultant and trainer from Leeds in the UK, specialising in open source technologies, data-related problems, and APIs. She is also an open source project lead, regular conference speaker, prolific blogger, and author of PHP Web Services, published May 2013 by O’Reilly.
There is one problem with using file_get_contents(), it does not allow you access to the response headers, as it returns only response content as a string, which are very important in a RESTful web service as you need to determine whether you hit a 404 (resource not found) or other error code. Using fopen solves that but puts all header information within an ugly array.
I was wondering if anyone has found anyway to have this header information stored in a nice object rather than force someone to go item by item through the array and parse the text entries?
What you probably want to do here is either use the curl extension (http://php.net/curl) or a library such as Guzzle (http://guzzlephp.org/index.html) – personally I prefer the Pecl_Http extension – but any of these will give a much more flexible interface for more complex requests and responses than the streams example I showed here.
Hope that answers your question