JSON with PHP

JSON is a data formatting language being used by many APIs nowadays. In this article, I will first explain JSON’s syntax. I will also talk about how you can parse JSON and how to generate it yourself. JSON is available as of PHP 5.2, but a lot of useful features have been added in PHP 5.4, so I will mention it when an example needs PHP 5.4

JSON Syntax

This is an example of JSON syntax:

{
   "coord":{
      "lon":4.88969,
      "lat":52.374031
   },
   "sys":{
      "country":"NL",
      "sunrise":1377924671,
      "sunset":1377973794
   },
   "weather":[
      {
         "id":300,
         "main":"Drizzle",
         "description":"light intensity drizzle",
         "icon":"09d"
      }
   ],
   "base":"gdps stations",
   "main":{
      "temp":17.9,
      "humidity":82,
      "pressure":1026,
      "temp_min":17.78,
      "temp_max":18
   },
   "wind":{
      "speed":6.17,
      "deg":330
   },
   "rain":{
      "3h":0
   },
   "clouds":{
      "all":24
   },
   "dt":1377937322,
   "id":2759794,
   "name":"Amsterdam",
   "cod":200
}

JSON means JavaScript Object Notation, which already indicates that JSON looks a lot like JavaScript. Even better, it is actually the same syntax as JavaScript except that double quotes are obligatory in JSON. I will explain all elements JSON can have:

value

Everything in JSON is a value: value

This means you can put true, false and null in a value next to strings, integers, floats, objects and arrays. The most complex values are these:

{ … } (object)

The braces indicate a JSON object. An object can contain properties. In the example given above, there are 12 properties in the object. You can see that some properties can also themselves be objects.

These brackets indicate an array, these can be seen in the example above at weather. In the example above, this array only has one value, in this case an object. If there was another object in it, it would have looked like this (I skipped some parts):

{
    "weather":[
      {
         "id":500,
         "main":"Rain",
         "description":"light rain",
         "icon":"10d"
      },
      {
         "id":506,
         "main":"Sun",
         "description":"sunny",
         "icon":"12d"
      }
    ]
}

In an array there can also be children as other values, in this way:

[
  {
    "title": "My Blog Post",
    ...
    "tags": ["news", "js"]
  }
]

Comments are impossible in JSON. This has a clear reason: JSON is a data structure language which means the objects should describe themselves.

Applications of JSON

JSON is being used for many APIs (Application Programming Interface), in some APIs as the only one. Others have most of the times XML as another option.

Twitter Twitter is an example of an API that only offers JSON in their newest version of their API. This is an example of a detailed tweet:

{
   "coordinates":null,
   "favorited":false,
   "truncated":false,
   "created_at":"Wed Jun 06 20:07:10 +0000 2012",
   "id_str":"210462857140252672",
   "entities":{
      "urls":[
         {
            "expanded_url":"https://dev.twitter.com/terms/display-guidelines",
            "url":"https://t.co/Ed4omjYs",
            "indices":[
               76,
               97
            ],
            "display_url":"dev.twitter.com/terms/display-\u2026"
         }
      ],
      "hashtags":[
         {
            "text":"Twitterbird",
            "indices":[
               19,
               31
            ]
         }
      ],
      "user_mentions":[

      ]
   },
   "in_reply_to_user_id_str":null,
   "contributors":[
      14927800
   ],
   "text":"Along with our new #Twitterbird, we've also updated our Display Guidelines: https://t.co/Ed4omjYs  ^JC",
   "retweet_count":66,
   "in_reply_to_status_id_str":null,
   "id":210462857140252672,
   "geo":null,
   "retweeted":true,
   "possibly_sensitive":false,
   "in_reply_to_user_id":null,
   "place":null,
   "user":{
      "profile_sidebar_fill_color":"DDEEF6",
      "profile_sidebar_border_color":"C0DEED",
      "profile_background_tile":false,
      "name":"Twitter API",
      "profile_image_url":"http://a0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png",
      "created_at":"Wed May 23 06:01:13 +0000 2007",
      "location":"San Francisco, CA",
      "follow_request_sent":false,
      "profile_link_color":"0084B4",
      "is_translator":false,
      "id_str":"6253282",
      "entities":{
         "url":{
            "urls":[
               {
                  "expanded_url":null,
                  "url":"http://dev.twitter.com",
                  "indices":[
                     0,
                     22
                  ]
               }
            ]
         },
         "description":{
            "urls":[

            ]
         }
      },
      "default_profile":true,
      "contributors_enabled":true,
      "favourites_count":24,
      "url":"http://dev.twitter.com",
      "profile_image_url_https":"https://si0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png",
      "utc_offset":-28800,
      "id":6253282,
      "profile_use_background_image":true,
      "listed_count":10774,
      "profile_text_color":"333333",
      "lang":"en",
      "followers_count":1212963,
      "protected":false,
      "notifications":null,
      "profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png",
      "profile_background_color":"C0DEED",
      "verified":true,
      "geo_enabled":true,
      "time_zone":"Pacific Time (US & Canada)",
      "description":"The Real Twitter API. I tweet about API changes, service issues and happily answer questions about Twitter and our API. Don't get an answer? It's on my website.",
      "default_profile_image":false,
      "profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png",
      "statuses_count":3333,
      "friends_count":31,
      "following":true,
      "show_all_inline_media":false,
      "screen_name":"twitterapi"
   },
   "in_reply_to_screen_name":null,
   "source":"web",
   "in_reply_to_status_id":null
}

You can see there is a lot of information in the response, and I will leave it as a challenge to you to figure out what everything means :).

Composer

Next to that it is being used in APIs, it is also being used for configuration files. Composer is the best-known PHP package manager and it uses JSON as configuration file (this is the composer.json of the Symfony framework):

{
    "name": "symfony/symfony",
    "type": "library",
    "description": "The Symfony PHP framework",
    "keywords": ["framework"],
    "homepage": "http://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "http://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=5.3.3",
        "symfony/icu": "~1.0",
        "doctrine/common": "~2.2",
        "twig/twig": "~1.11",
        "psr/log": "~1.0"
    },
    "replace": {
        "symfony/browser-kit": "self.version",
        "symfony/class-loader": "self.version",
        "symfony/config": "self.version",
        "symfony/console": "self.version",
        "symfony/css-selector": "self.version",
        "symfony/dependency-injection": "self.version",
        "symfony/debug": "self.version",
        "symfony/doctrine-bridge": "self.version",
        "symfony/dom-crawler": "self.version",
        "symfony/event-dispatcher": "self.version",
        "symfony/filesystem": "self.version",
        "symfony/finder": "self.version",
        "symfony/form": "self.version",
        "symfony/framework-bundle": "self.version",
        "symfony/http-foundation": "self.version",
        "symfony/http-kernel": "self.version",
        "symfony/intl": "self.version",
        "symfony/locale": "self.version",
        "symfony/monolog-bridge": "self.version",
        "symfony/options-resolver": "self.version",
        "symfony/process": "self.version",
        "symfony/propel1-bridge": "self.version",
        "symfony/property-access": "self.version",
        "symfony/proxy-manager-bridge": "self.version",
        "symfony/routing": "self.version",
        "symfony/security": "self.version",
        "symfony/security-bundle": "self.version",
        "symfony/serializer": "self.version",
        "symfony/stopwatch": "self.version",
        "symfony/swiftmailer-bridge": "self.version",
        "symfony/templating": "self.version",
        "symfony/translation": "self.version",
        "symfony/twig-bridge": "self.version",
        "symfony/twig-bundle": "self.version",
        "symfony/validator": "self.version",
        "symfony/web-profiler-bundle": "self.version",
        "symfony/yaml": "self.version"
    },
    "require-dev": {
        "doctrine/data-fixtures": "1.0.*",
        "doctrine/dbal": "~2.2",
        "doctrine/orm": "~2.2,>=2.2.3",
        "monolog/monolog": "~1.3",
        "propel/propel1": "1.6.*",
        "ircmaxell/password-compat": "1.0.*",
        "ocramius/proxy-manager": ">=0.3.1,<0.5-dev"
    },
    "autoload": {
        "psr-0": { "Symfony\\": "src/" },
        "classmap": [
            "src/Symfony/Component/HttpFoundation/Resources/stubs",
            "src/Symfony/Component/Intl/Resources/stubs"
        ],
        "files": [ "src/Symfony/Component/Intl/Resources/stubs/functions.php" ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "2.4-dev"
        }
    }
}

Database configuration

Some use it as database configuration, or as any other configuration. It can be compared to YAML in this respect. It can also be compared to XML, but XML is much more extensive and has more possibilities.

Parsing JSON

Now we are going to start with the real work. We are going to parse JSON using PHP. To view JSON well I recommend Postman - REST client or JSONView (both for Google Chrome).

In PHP you can use json_decode to parse JSON. First a simple example:

<?php
// we first make a variable with a JSON object in it
$json = '{"name":"Koen"}';
// then we parse (decode) it
$decoded = json_decode($json);
// to view what is in the decoded object, you can uncomment the following line
// var_dump($decoded);

// we now echo the name that has been parsed
echo 'The name is ', $decoded->name;
?>

If you understand OOP a little bit, you can see the JSON data has been decoded into an object (of the type stdClass). This means all properties can be accessed using the object operator (->). Don’t you want to use the object operator? Set the second parameter of json_decode to true.

<?php
// we first make a variable with a JSON object in it
$json = '{"name":"Koen"}';
// then we parse (decode) it
$decoded = json_decode($json, true);
// to view what is in the decoded object, you can uncomment the following line
// var_dump($decoded);

// we now echo the name that has been parsed, but now using an array
echo 'The name is ', $decoded['name'];
?>

I will show another example but then with arrays:

<?php
// we first create a variable with a JSON array in it
$json = '["Koen","Bas","Wouter","Ger"]';
// then we parse (decode) it
$decoded = json_decode($json);
// to view what is in the decoded object, you can uncomment the following line
// var_dump($decoded);

// you can now access the values using the standard array operator, for example $decoded[0]

// echo all names
foreach ($decoded as $name) {
    echo 'A name is ', $name, '<br />';
}
?>

You can see you can do everything with a decoded JSON array that you can do with a user-defined array, because it is a normal array.

What is the weather?

For this example, I will create a simple page with the weather. For this I will use the OpenWeatherMap API, because it is a relativity simple API and you don’t have to use access tokens.

Retrieving the JSON data

We’ll start with retrieving the data using file_get_contents. On some hosts file_get_contents has been blocked, which means you’ll have to work with curl. I won’t go into that right now, there are a lot of tutorials about that online. The URL I will use is http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric. With this we retrieve the current weather in Amsterdam in degrees Celcius. Let’s begin:

<?php
// retrieve the current weather in Amsterdam in degrees Celcius
$json = file_get_contents('http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric');
?>

Wasn’t that easy?

Parsing the JSON data

To make sure the data is usable, we will parse it using json_decode. This won’t be difficult either.

<?php
// retrieve the current weather in Amsterdam in degrees Celcius
$json = file_get_contents('http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric');

// parse the JSON
$data = json_decode($json);
?>

Display the data

We’ll now display the data:

<?php
// retrieve the current weather in Amsterdam in degrees Celcius
$json = file_get_contents('http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric');

// parse the JSON
$data = json_decode($json);

// show the city (and the country)
echo '<h1>', $data->name, ' (', $data->sys->country, ')</h1>';

// the general information about the weather
echo '<h2>Temperature:</h2>';
echo '<p><strong>Current:</strong> ', $data->main->temp, '° C</p>';
echo '<p><strong>Min:</strong> ', $data->main->temp_min, '° C</p>';
echo '<p><strong>Max:</strong> ', $data->main->temp_max, '° C</p>';

// something about the air
echo '<h2>Air</h2>';
echo '<p><strong>Humidity:</strong> ', $data->main->humidity, '%</p>';
echo '<p><strong>Pressure:</strong> ', $data->main->pressure, ' hPa</p>';

// and about the wind
echo '<h2>Wind</h2>';
echo '<p><strong>Speed:</strong> ', $data->wind->speed, ' m/s</p>';
echo '<p><strong>Direction:</strong> ', $data->wind->deg, '°</p>';

// and what the weather is according to the API (an array)
echo '<h2>The weather</h2>';
echo '<ul>';
foreach ($data->weather as $weather) {
    echo '<li>', $weather->description, '</li>';
}
echo '<ul>';
?>

You can extend this example yourself using the forecasts at http://api.openweathermap.org/data/2.5/forecast?q=Amsterdam,NL&units=metric&lang=en, but we’ll start looking at how we can generate it ourselves.

Generating JSON

You can also generate JSON yourself using json_encode. You can generate objects by creating an associative array, like this:

<?php
// make sure the content type has been set to JSON so we can see it pretty in the Chrome extensions :)
header('Content-Type: application/json');

// create the data
$data = array(
    'name' => 'Koen'
);

// and generate the JSON
echo json_encode($data);
?>

You can see that a JSON object is generated because we created an associative array. If you haven’t installed an extension to see JSON pretty, I would do it now, or you can supply JSON_PRETTY_PRINT as second parameter to json_encode (PHP 5.4+):

<?php
// make sure the content type has been set to JSON so we can see it pretty in the Chrome extensions :)
header('Content-Type: application/json');

// create the data
$data = array(
    'name' => 'Koen'
);

// and generate the JSON
echo json_encode($data, JSON_PRETTY_PRINT);
?>

Arrays

You can also generate JSON array by not making your array associative:

<?php
// make sure the content type has been set to JSON so we can see it pretty in the Chrome extensions :)
header('Content-Type: application/json');

// create the data
$data = array(
    'Koen',
    'Bas',
    'Wouter',
    'Ger'
);

// and generate the JSON
echo json_encode($data);
?>

With this you can do a lot, and this are just the non-OOP skills.

OOP?

If you want to make an API, you’ll probably have a big website. I recommend developing big websites fully OOP, but how are you going to create your API? There are a few possibilities:

JsonSerializable

This interface is available since PHP 5.4. If you implement it, the method jsonSerialize will automatically be called if you encode JSON using json_encode. An example:

<?php
class User implements JsonSerializable {
    private $name;
    private $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age  = $age;
    }

    public function getName() {
        return $this->name;
    }

    public function setName($name) {
        $this->name = $name;
    
        return $this;
    }

    public function getAge() {
        return $this->age;
    }

    public function setAge($age) {
        $this->age = $age;
    
        return $this;
    }

    public function jsonSerialize() {
        return array(
            'name' => $this->name,
            'age'  => $this->age
        );
    }

}

header('Content-Type: application/json');
$user = new User('Koen', 15);
echo json_encode($user);
?>

No PHP 5.4?

If you don’t have PHP 5.4, you can unfortunately not use JsonSerializable. There are a few similar solutions that are available, like the Serializer component. The Symfony Serializer is a small component.

More complex data?

Use JMSSerializer.

The future

I hope you can now understand the JSON syntax, and have already created a very functional forecast of the weather :). You can now also use the Twitter, Facebook and Foursquare API, so I wish you luck starting your new website that integrates with Twitter :).