Working with JSON
Travelers from different countries often switch to one shared language to talk. JSON is that shared language for programs.
A PHP program, a JavaScript app in the browser, a phone app, a weather service: they're written in different languages, but they all need to swap data. The format almost all of them use is JSON (JavaScript Object Notation, pronounced like the name "Jason").
You met JSON's shape in the JavaScript track, and in the composer.json file from Namespaces and Composer. It looks like this:
{
"name": "Maria",
"age": 16,
"clubs": ["chess", "choir"],
"isEnrolled": true
}{ }holds named values, like a PHP associative array.[ ]holds a list, like a PHP indexed array.- Text is always in double quotes. Numbers,
true,false, andnullaren't quoted.
PHP to JSON: json_encode
json_encode turns a PHP value into a JSON string:
<?php
$student = [
"name" => "Maria",
"age" => 16,
"clubs" => ["chess", "choir"],
"isEnrolled" => true,
];
echo json_encode($student), "\n";{"name":"Maria","age":16,"clubs":["chess","choir"],"isEnrolled":true}An associative array became a JSON object { }, and the indexed array became a JSON list [ ].
That's compact, which is good for sending, but hard to read. A few flags change the output:
<?php
$place = ["city" => "Parañaque", "site" => "https://example.com/map"];
echo json_encode($place), "\n";
echo json_encode($place, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode($place, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"city":"Para\u00f1aque","site":"https:\/\/example.com\/map"}
{"city":"Parañaque","site":"https://example.com/map"}
{
"city": "Parañaque",
"site": "https://example.com/map"
}- By default, letters like
ñbecome codes such as\u00f1, and/becomes\/. Both are valid JSON, but hard to read.JSON_UNESCAPED_UNICODEandJSON_UNESCAPED_SLASHESkeep them as they are. JSON_PRETTY_PRINTadds line breaks and indentation, which is great for files people might open.
JSON to PHP: json_decode
json_decode goes the other way. Pass true as the second argument to get associative arrays, which is what you'll almost always want:
<?php
$json = '{"title": "Noli Me Tangere", "year": 1887, "tags": ["classic", "novel"]}';
$book = json_decode($json, true);
echo $book["title"] . " (" . $book["year"] . ")\n";
echo "First tag: " . $book["tags"][0] . "\n";
var_dump($book["year"]);Noli Me Tangere (1887)
First tag: classic
int(1887)Unlike form data, JSON keeps its types: the year comes back as an int.
Without true, you get objects instead, read with ->: $book->title. That works too, but arrays are easier to loop over and check with ??, so this track uses true.
When the JSON is broken
JSON is strict. A single quote, a trailing comma, or a missing bracket makes it invalid. By default, json_decode then just returns null, which is easy to miss. The flag JSON_THROW_ON_ERROR makes it throw an exception instead, which you can catch, as in Errors and Exceptions:
<?php
$broken = "{'name': 'Maria'}";
var_dump(json_decode($broken, true));
try {
json_decode($broken, true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo "Bad JSON: " . $e->getMessage() . "\n";
}NULL
Bad JSON: Syntax errorThe problem here is the single quotes: JSON requires double quotes. Use JSON_THROW_ON_ERROR whenever the JSON comes from outside your program.
Saving data as JSON
Combined with Reading and Writing Files, JSON makes a simple way to save nested data and load it back exactly:
<?php
$settings = [
"theme" => "dark",
"fontSize" => 18,
"favorites" => ["php", "sql"],
];
file_put_contents("settings.json", json_encode($settings, JSON_PRETTY_PRINT));
$loaded = json_decode(file_get_contents("settings.json"), true, flags: JSON_THROW_ON_ERROR);
echo "Theme: " . $loaded["theme"] . ", favorites: " . implode(" and ", $loaded["favorites"]) . "\n";
var_dump($loaded === $settings);Theme: dark, favorites: php and sql
bool(true)What went in came back out, identical, types and all.
Sending JSON from a page
Many PHP pages don't send HTML at all. Instead, they send JSON for a JavaScript app or a phone app to use. That's called an API. The page tells the browser what's coming with a header, then echoes the JSON:
<?php
header("Content-Type: application/json");
$menu = [
["dish" => "Adobo", "price" => 120],
["dish" => "Sinigang", "price" => 150],
];
echo json_encode($menu);Opened through the built-in server, the page is just this:
[{"dish":"Adobo","price":120},{"dish":"Sinigang","price":150}]A JavaScript fetch("menu.php") in the browser could then read that and build a menu on the page.
Try it
A to-do app stores its list as JSON. Predict each line.
Running PHP here
Running PHP right in the browser is coming to Syntaxia soon. For now, you can read and edit the code here. If you have PHP installed on your computer (see Setting Up), save it as index.php and run it with php index.php.
Check your prediction
Joy has 2 task(s) left:
- Call Lola
- Water plants
{"owner":"Joy","left":["Call Lola","Water plants"]}
Could not read: Syntax errorThe JSON becomes nested arrays. A new task is added at the end, and "Buy mangoes" is marked done, which leaves two tasks. array_column makes a plain list of their titles, which json_encode turns into a JSON list. The last JSON has a trailing comma after "Joy", which JSON doesn't allow.
Try it yourself
- Print the summary with
JSON_PRETTY_PRINT. How does it look now? - Add a
"due"date to one task in the JSON, and print it with??for tasks that have one. - Save
$listtotodo.jsonwithfile_put_contents, then open the file in your editor and look at it.
Check your understanding
Up next
Files and JSON work for small amounts of data. For real applications, with many visitors reading and writing at once, you want a database. Connect PHP to one in Databases with PDO.
