Using HTTP Sockets with RESTful Services

by sixfoottallrabbit | Posted in PHP

RESTful web services are all the rage when it comes to providing resources over the Internet. Facebook, Twitter, Last.fm and many more websites give public access to their data through a RESTful service. We’re going to look at using PHP to connect to such a service to fetch some data.

Here’s a live demo of what we’re going to make: LIVE DEMO

So what is REST?

We won’t go into too much detail in this tutorial but REST is a style of software architecture. I can guarantee that you already know a system that conforms to this architecture: the World Wide Web. RESTful services follow a few rules:

  • To request a resource, you need some way to identify the resource (on the web, this is a URI).
  • The resource itself is separate from its possible representations, so that you can specify the format for yourself.
  • The requested resource data must provide enough information for the resource to be manipulated.

When you accessed this tutorial (a resource), you sent our server a request. This request was a GET request with some metadata that described which particular tutorial you wanted. The representation we’ve given you is a HTML document by default, but you can also subscribe to our XML feed, which is another representation.

Which service?

So we’re going to connect to a RESTful service and send some requests. The service that I’ve chosen for this tutorial is Last.fm because it provides absolutely tons of information about what music people listen to without any kind of user authentication. I will use a fake API key in this tutorial (so people don’t abuse mine), so you will need to swap it out for one of your own. You can get one for yourself from the Last.fm API page. If you ever want to authenticate a user (for changing user settings or scrobbling etc.) then you should read the user authentication page.

Fake API key: 4970fd1e2e67c2f4d7k6d21288c8a8dc

Getting Connected: Sockets

PHP provides an assortment of methods for making requests to RESTful services, but since this is a tutorial about making HTTP requests through sockets, that’s what we’re going to do! It’s not the easiest option but we’ll definitely learn a lot from it. Plus, we sure love a challenge. 🙂

Let’s create our class. It should store a few attributes to handle our socket connection. The first attribute is $m_host, which is the domain (or IP address) for us to connect to. Second we have $m_port, which we’ll set to 80 since this is the port a server ‘listens to’ for requests. Then we’ll store the socket handler in $m_socket and the API key in $m_apiKey. Lastly, we have a couple of error properties which get filled by our socket connection if it fails to do something we ask it to.

class Lastfm {
    private $m_host = 'ws.audioscrobbler.com';
    private $m_port = 80;
    private $m_socket;
    private $m_apiKey;
    private $m_errorNumber;
    private $m_errorString;

    // Methods go here
}

Now it’s time to make the constructor. The constructor will do the connecting and set the API key attribute. Since we’ve specified the host and port by default, we only need one argument. This argument is the API key. See the code:

    function Lastfm($key) {
        $this->m_apiKey = $key;
    
        // Open the socket
        $this->m_socket = fsockopen($this->m_host, $this->m_port, $this->m_errorNumber, $this->m_errorString);
        if (!$this->m_socket) {
            echo "Connection failed";
        }
    }

So first we set the API key attribute. Then we call a handy function called fsockopen() which takes four arguments. The first two are the host and port to connect to. The last two are references to variables which will store the error number and message when an error is encountered by the socket. We won’t really use this error data since we aren’t covering error handling, but you may need it for some debugging. The fsockopen() function returns a resource handler which is used to send and retrieve data, so we store it in the socket attribute. If it returned false, then we echo a “Connection failed” error.

Assuming there is no problem, we are now connected to the Last.fm API. Oh joy.

Getting a Response: Writing and Reading Sockets

Our next step is to make requests to the sockets that we’ve connected to and handle the response. We’re going to do this all within one method called send(). It may seem strange to do both the sending and retrieving in one method called ‘send’, but all requests we make will have some kind of response. And processing the response is as easy as pie.

See the code first and we’ll walk through it:

    function send($message) {
        // Write (send) the message to the socket
        fwrite($this->m_socket, $message);
        
        // Get the content from the response
        $response = '';
        $contentStarted = false;
        while (!feof($this->m_socket)) {
            $line = fgets($this->m_socket, 4096);
            if (substr($line, 0, 1) === '{') $contentStarted = true;
            
            if ($contentStarted) {
                $response .= $line;
            }
        }
        
        return $response;
    }

This method takes one argument which is the message that we want to send to the Last.fm API, i.e. our request. Actually sending it only takes one line. We use the fwrite() function to write our message to the socket handler. That’s it. That one line will take your message and send it all the way to the Last.fm API servers to be handled. Of course, you can’t just send it a message as simple as “What did I last listen to?”. The message needs to be in a specific format that the server can understand, but we’ll get to that later.

Similarly, the response from the server will be in a specific format so that we can understand and process it. This format is a HTTP response. Specifically a HTTP 200 OK response. This is the kind we want anyway. You’ve probably heard of the 404 Not Found response. That’s one we don’t want. Nonetheless, our code should get a 200 OK response so we don’t need to check for it.

A HTTP response begins with a header. The header provides information about the response and may look something like this:

HTTP/1.0 200 OK
Date: Sun, 04 Apr 2010 19:12:59 GMT
Server: Apache/1.3.39 (Unix)
X-Proxy-Fix-Up: headers fixed up
X-Web-Node: www144
Connection: close
Content-Type: application/json; charset=utf-8;

As you can see, it contains information such as what kind of response it is, the date it was produced, server information and other details.

Following the header is the actual content of the response. For example, if we asked for a list of a Last.fm user’s friends, this list would be in the content. This is what we need to get so that we can process it. Luckily for us, the content of our response always begins with a left brace ({) so that’s a great way to find where it starts.

So what we do is begin a while loop that will only end once we’ve reached the end of the response (when feof() is true). For each loop iteration, we get a line of the response. If the line begins with a left brace, we set $contentStarted to true. And if $contentStarted is true, i.e. we have reached the content of the response, we start appending the lines to our $response string. Then we return this response.

Now we can make requests to the Last.fm API.

Making Requests: GET

Now the key behind HTTP-based RESTful services is the request methods. HTTP has four methods: GET, POST, PUT, DELETE. We will be making use of the first two, the most common of the methods. GET is used for retrieving a resource from the service (eg. getting information about a song) and POST is for updating a resource (eg. changing the user’s settings).

Like with a response from the server, if we want to send a HTTP GET request then it needs to be in a specific format. The GET format is very simple and consists only of a header that looks like this:

GET url HTTP/1.0
Host: host

Looks simple, right? Well we can send more information, but we don’t need to so we won’t bother. We need to make a method that will produce a message like this and then send it to the server with our send() method. The only difficult part is forming the URL. The URL is the most important part of the request because it tells the server what resource we want back. That’s no surprise, considering URL stands for “Uniform Resource Locator”.

The form of the URL is this: path?querystring. The path points to some script that the server will run and the query string is a set of parameters that tell the script what we want (in the format: parameter1=value1&parameter2=value2). The path and query string are specific to the service that you’re trying to access.

For example, if we want to get user info about the user ‘sftrabbit’ (that’s me!) our URL looks like this:

/2.0/?method=user.getinfo&api_key=4970fd1e2e67c2f4d7k6d21288c8a8dc&user=sftrabbit&format=json

The method we’re calling is user.getinfo (user is the resource). A list of Last.fm API methods and what additional parameters they require is available at the Last.fm API page.

So here’s the getRequest() method to create that GET request:

    function getRequest($method,$methodArgs) {
        // Create the URL
        $url = '/2.0/?';
        $url .= 'method='.rawurlencode(trim($method)).'&';
        $url .= 'api_key='.rawurlencode(trim($this->m_apiKey)).'&';
        foreach ($methodArgs as $name => $value) {
            $url.=rawurlencode(trim($name)).'='.rawurlencode(trim($value)).'&';
        }
        $url .= 'format=json'; // We want the response content in JSON format
        
        // Form the HTTP GET header
        $out = "GET ".$url." HTTP/1.0\r\n";
        $out .= "Host: ".$this->m_host."\r\n";
        $out .= "\r\n";
        
        // Make the request and get the JSON response
        $json = $this->send($out);
        return json_decode($json, true);
    }

As you can see, first it forms the URL by looping through an array of given parameters and appending them to the URL. Then it adds the URL and host name to the GET request. Then it sends it using our send() method. The great thing about the JSON response is that we can just call json_decode() on it and it gets turned into a format suitable for working with in PHP.

Now I’m not going to go into making a POST request but I will supply the code at the end of this tutorial. Or you can research it for yourself (it’s not so different to GET!).

We’re Done: Fetching Data

Now we actually have a working class. So let’s see if we can get a list of the songs I last listened to:

$lastfm = new lastfm('4970fd1e2e67c2f4d7k6d21288c8a8dc');
$result = $lastfm->getRequest('user.getrecenttracks',array('user'=>'sftrabbit'));
foreach ($result['recenttracks']['track'] as $track) {
    echo '' . $track['artist']['#text'] . ' - ' . $track['name'];
    echo '   ('.date("j. M y \a\\t g:ia",$track['date']['uts']);
    echo ")
\n"; }

See how simple that is? I just get the result and iterate through it to print out each track. Here’s a sample result:

Bright Eyes – Little Drummer Boy   (4. Apr 10 at 10:46pm)
Joanna Newsom – Easy   (4. Apr 10 at 10:42pm)
St. Vincent – All My Stars Aligned   (4. Apr 10 at 10:39pm)
Regina Spektor – Poor Little Rich Boy   (4. Apr 10 at 10:36pm)
Beirut – Forks and Knives   (4. Apr 10 at 10:33pm)
Sufjan Stevens – The Mistress Witch from McClure   (4. Apr 10 at 10:28pm)
New Buffalo – Cheer Me Up Thank You   (4. Apr 10 at 10:25pm)
Camera Obscura – Careless Love   (4. Apr 10 at 10:20pm)
Jens Lekman – The One Dollar Thought   (4. Apr 10 at 10:17pm)
Bright Eyes – Entry Way Song   (4. Apr 10 at 10:11pm)

Awesome. So what now? Well you can use the information you’ve gathered from this tutorial to access all sorts of RESTful services. Want to show your latest tweets on your website? Go for it. Want to write an application that posts to its users’ Facebook walls? Do it! If you ever find yourself writing a large piece of software, you might find that a RESTful approach is the best approach. You should have some idea how such an architecture is formed now. You should also know how the HTTP protocol works. Exciting stuff, huh?

So enjoy a RESTful life. 🙂

P.S. Here’s the source: PHP Last.fm API Source