PHP Web Services (2013)

Chapter 5. JSON

JSON stands for JavaScript Object Notation, but don’t be fooled by the name. Although it sounds as if it’s a JavaScript-specific format, it is easily readable and writeable by a wide range of scripting languages today. It’s a very simple, lightweight format, which can represent nested, structured data.

For example, if there were a data set that looked like this:

§  message

§  en: “hello friend”

§  es: “hola amigo”

In JSON, that data would look like this:

{"message":{"en":"hello friend","es":"hola amigo"}}

If a piece of data is represented by a scalar value, then it is presented plainly. If it is structured (as shown in the previous example), such as an associative array or an object with properties in PHP, a curly brace is used to indicate a new level of depth in the data structure. The keys and values are separated by colons, and each record at a given level is separated with a comma.

It is also possible to show a list of items quite elegantly using JSON. Take this imaginary shopping list:

§  eggs

§  bread

§  milk

§  bananas

§  bacon

§  cheese

A JSON representation of this would simply be:

["eggs","bread","milk","bananas","bacon","cheese"]

As you can see here, many of the keys in the previous example are optional, and multiple values are enclosed with the simple square brackets. If this list was in fact the value of a property, then both kinds of brackets would be seen:

{"list":["eggs","bread","milk","bananas","bacon","cheese"]}

This example shows that our data contained a key/value pair, with the key “list.”

When to Choose JSON

JSON gives a very clear indication of the original data structure and conveys the values within, but doesn’t give us any specific information about the exact data types that were originally in use. Often, this isn’t important; HTTP is entirely string-based anyway so it is usual to deal with this type of data in web-based applications.

JSON’s strongest point is that it a simple data format. It doesn’t take much storage space in comparison to XML and isn’t too large to transfer “over the wire” or, in the case of mobile applications, over a potentially slow and patchy data connection! Since it is quite small and simple, it is inexpensive in processor terms to decode the JSON format, which makes it ideal for less powerful devices such as phones.

Use JSON when information about the exact data format isn’t critical, and the effort needed to decode it must stay light. It’s great for casual web or mobile applications—and of course it’s absolutely ideal if you are supplying data to a JavaScript consumer, since it handles this data format natively and quickly.

Content negotiation over HTTP using headers has already been covered earlier in the book (see Chapter 3); this is how it is ascertained that the client would like a JSON response format. As an example, here are the headers for a request/response pair in which the consumer is requesting JSON and the API provides exactly that:

> GET /header.php HTTP/1.1

> Accept: application/json, text/html;=0.5

< HTTP/1.1 200 OK

< Content-Type: application/json

{"message":"hello there"}

You can see that the final entry in the example is the body of the response. The format of this is the same JSON that was covered earlier in this chapter. Setting the headers correctly is absolutely key, since without the correct Content-Type header, any application receiving this request will not know how to decode it. If it requested JSON, it might hope that’s what was returned, but the Content-Type should always match. If it isn’t specified, many web servers will default to sending a Content-Type of “text/html”, which is not only inaccurate, but also dangerous because a browser will try to display the content as HTML and allow embedded JavaScript—so do take care to set those headers correctly.

Handling JSON with PHP

This is very simple, which is another reason to choose JSON as a preferred output format! In PHP, you can use json_encode() to turn either an array or an object into valid JSON.

For example, the previous example showed some JSON that looked like this:

{"message":"hello you"}

To generate that from PHP (which is exactly how I generated the previous examples), I simply used this line:

echo json_encode(array("message" => "hello you"));

This shows a very simple array wrapped in json_encode() and using echo to output it so I can see it when I request the page.

To handle incoming JSON data and turn it into a structure you can use, simply use json_decode(), passing the string containing the JSON as the first argument. Sticking with our existing simple example, the code could look something like this:

$data = json_decode('{"message":"hello you"}');

var_dump($data);

This example includes var_dump() to show exactly what actually happens when the json_decode() function is used: by default, an object is returned. Here’s the output of that script:

object(stdClass)#1 (1) {

  ["message"]=>

  string(9) "hello you"

}

Because there is no data-type information, JSON cannot tell whether this was an array with keys and values, or an object with properties, before it was turned into JSON; there is no difference between the two. We would get identical output from a script that looked like this instead:

$obj = new stdClass();

$obj->message = "hello you";

echo json_encode($obj) . "\n";

Similarly, the same output would be shown if an object of any other class were used; the object-type information just isn’t included in JSON so it can’t be retrieved at the other end. When calling the json_decode(), it is possible to convert the data to an associative array rather than an object—by passing true as the optional second argument:

$data = json_decode('{"message":"hello you"}', true);

var_dump($data);

This time around, our output is subtly different:

array(1) {

  ["message"]=>

  string(9) "hello you"

}

Whether you choose to work with objects or arrays is up to you, and really depends on the application and also the language. PHP objects are a little bit heavier than arrays, particularly in older versions of PHP (PHP 5.3 and earlier), so you will sometimes see better performance when using simple arrays for simple data.

JSON in Existing APIs

As an example of working with an API that uses JSON, let’s take a look at a little piece of the GitHub API and use JSON for our examples. The examples here work with gists, which are similar to “pastebins”—places where you can put code or other text to share with others.

Our example is very simple; it creates a gist using PHP:

// grab the access token from an external file (to avoid oversharing)

require("github-creds.php");

$data = json_encode(array(

    'description' => 'Gist created by API',

    'public' => 'true',

    'files' => array(

        'text.txt' => array(

            'content' => 'Some riveting text'

        )

    )

));

$url = "https://api.github.com/gists";

$ch = curl_init($url);

curl_setopt($ch, CURLOPT_POST, 1);

curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

curl_setopt($ch, CURLOPT_HTTPHEADER,

    array('Content-Type: application/json',

        'Authorization: token ' . $access_token)

);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($ch);

curl_close($ch);

There are a few things going on here that bear closer examination: sending JSON in requests, working with an Authorization header, and using credentials to gain access. You will notice that a variable $access_token is referenced, which isn’t set in the code. This is set in the github-creds.php file, kept separate to stop access keys being leaked in this text. In a real development project, I’d still keep this separate, but for a different reason—using a separate file means I can exclude it from source control and avoid publicizing my access keys to the world! Of course it does happen, and if it does, you can always revoke your token and generate a new one. If you ever suspect that a token has been leaked, then do destroy it and generate another (something to bear in mind if your tokens are visible when demonstrating APIs).

A POST request is used to create a new gist (GitHub has a RESTful API—these examples will come up again in Chapter 8) and send JSON-formatted data along with it. In fact, this is a PHP array (because those are easy to understand and work with), which is then converted to JSON usingjson_encode(). The resulting output is given as the value for CURLOPT_POSTFIELDS and PHP sends it as the body of the request.

This example also sets some headers using the CURLOPT_HTTPHEADER option. The first one is Content-Type, which we have already seen in many examples, and the second one is Authorization. The Authorization header here includes the “token” and the access token within it, because the GitHub API uses OAuth2 for authorization. We discussed OAuth in Chapter 3.

If all goes well with the previous request, a 200 status code will arrive with the response and the new gist will be created. The gist will also be visible on the Web. Alternatively, the gist can be requested over the API: one of the things included in the response when requesting the new gist is a link to it, so we can extend the example to also fetch the gist. Since this is a public gist, no authorization is needed and it is possible to just grab the data using file_get_contents(), then json_decode() it. Here’s the previous example again, with a few more lines added to illustrate grabbing the gist that was created:

// grab the access token from an external file

require("github-creds.php");

$data = json_encode(array(

    'description' => 'Gist created by API',

    'public' => 'true',

    'files' => array(

        'text.txt' => array(

            'content' => 'Some riveting text'

        )

    )

));

$url = "https://api.github.com/gists";

$ch = curl_init($url);

curl_setopt($ch, CURLOPT_POST, 1);

curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

curl_setopt($ch, CURLOPT_HTTPHEADER,

    array('Content-Type: application/json',

        'Authorization: token ' . $access_token)

);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($ch);

curl_close($ch);

$gist = json_decode($result, true);

if($gist) {

    echo file_get_contents($gist['url']);

}

You can easily try this yourself, or for an even simpler way to interact with the GitHub API, simply request all your own gists using https://api.github.com/user/username/gists and replacing username with your own GitHub username. Many APIs use JSON in a similar way to exchange information with consumers, and this chapter has covered how you can do that with PHP.