What JSON is
JSON (JavaScript Object Notation) is the standard text format for exchanging data between programs: browser JavaScript and your server, your site and a payment provider, a mobile app and its API. It looks like this:
{
"id": 1042,
"customer": "Asha Verma",
"paid": true,
"items": [
{ "product": "Mechanical Keyboard", "qty": 1, "price": 89.5 }
]
}It has objects (in braces, with string keys), arrays (in square brackets), strings in double quotes, numbers, true, false and null. That maps neatly onto PHP's arrays and scalars, and PHP converts in both directions with two functions.
json_encode: PHP to JSON
<?php
$order = [
'id' => 1042,
'customer' => 'Asha Verma',
'paid' => true,
'coupon' => null,
'items' => [
['product' => 'Mechanical Keyboard', 'qty' => 1, 'price' => 89.5],
['product' => 'Ceramic Mug Set', 'qty' => 2, 'price' => 18.75],
],
];
echo json_encode($order) . "\n";{"id":1042,"customer":"Asha Verma","paid":true,"coupon":null,"items":[{"product":"Mechanical Keyboard","qty":1,"price":89.5},{"product":"Ceramic Mug Set","qty":2,"price":18.75}]}The rule for arrays: a PHP list with keys 0, 1, 2... in order becomes a JSON array; any other array becomes a JSON object. That catches people out after array_filter() or unset(), which leave gaps in the keys:
<?php
$prices = [10, 20, 30];
echo json_encode($prices) . "\n";
unset($prices[0]);
echo json_encode($prices) . "\n";
echo json_encode(array_values($prices)) . "\n";
echo json_encode([]) . " " . json_encode((object) []) . "\n";[10,20,30]
{"1":20,"2":30}
[20,30]
[] {}array_values() renumbers the keys so the result is an array again. An empty PHP array becomes []; cast it to an object when the receiver expects {}.
Flags for readable output
json_encode() takes flags as its second argument. Three are used constantly:
<?php
$product = ['name' => 'Café Latte Mug', 'url' => 'https://mrhimanshu.com/tools/', 'tags' => ['mug', 'कॉफ़ी']];
echo json_encode($product) . "\n";
echo json_encode($product, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
echo json_encode($product, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";{"name":"Caf\u00e9 Latte Mug","url":"https:\/\/mrhimanshu.com\/tools\/","tags":["mug","\u0915\u0949\u092b\u093c\u0940"]}
{"name":"Café Latte Mug","url":"https://mrhimanshu.com/tools/","tags":["mug","कॉफ़ी"]}
{
"name": "Café Latte Mug",
"url": "https://mrhimanshu.com/tools/",
"tags": [
"mug",
"कॉफ़ी"
]
}All three are valid JSON and decode to the same data. By default PHP escapes non-ASCII characters and slashes, which is safe but hard to read; JSON_UNESCAPED_UNICODE and JSON_UNESCAPED_SLASHES keep them as they are, and JSON_PRETTY_PRINT adds line breaks and indentation, which is ideal for files people read. JSON_PRESERVE_ZERO_FRACTION keeps 10.0 as 10.0 instead of 10.
json_decode: JSON to PHP
json_decode() goes the other way. Pass true as the second argument to get associative arrays, which is what most code wants:
<?php
$json = '{"id":1042,"customer":"Asha Verma","paid":true,"items":[{"product":"Mechanical Keyboard","qty":1}]}';
$order = json_decode($json, true);
echo $order['customer'] . "\n";
echo $order['items'][0]['product'] . "\n";
var_dump($order['paid']);
$object = json_decode($json);
echo $object->customer . "\n";
echo get_class($object) . "\n";Asha Verma
Mechanical Keyboard
bool(true)
Asha Verma
stdClassWithout true, JSON objects become stdClass objects and you use -> instead of [...]. Both work; pick one style per project. Arrays are usually more convenient because all the array functions work on them.
Handling invalid JSON
JSON from outside, such as a request body, a file someone edited, or an API, can be broken. By default json_decode() returns null when it fails, which is indistinguishable from the valid JSON null and easy to miss. Pass JSON_THROW_ON_ERROR and invalid input throws a JsonException that you can catch:
<?php
$inputs = ['{"qty": 3}', '{"qty": 3,}', "{'qty': 3}"];
foreach ($inputs as $input) {
try {
$data = json_decode($input, true, 512, JSON_THROW_ON_ERROR);
echo "OK: qty is {$data['qty']}\n";
} catch (JsonException $e) {
echo "Invalid JSON: " . $e->getMessage() . "\n";
}
}OK: qty is 3
Invalid JSON: Syntax error
Invalid JSON: Syntax errorA trailing comma and single quotes are both invalid in JSON, even though JavaScript code allows them. The third argument, 512, is the maximum nesting depth (the default). try and catch are explained in the next lesson.
Decoding only checks that the text is valid JSON. Still validate the contents afterwards, as with any input: is qty present, is it an integer, is it at least 1?
JSON files and requests
Settings and small data sets are often kept in JSON files:
<?php
$settings = ['currency' => 'INR', 'perPage' => 20, 'features' => ['reviews' => true]];
file_put_contents('settings.json', json_encode($settings, JSON_PRETTY_PRINT));
$loaded = json_decode(file_get_contents('settings.json'), true, 512, JSON_THROW_ON_ERROR);
echo $loaded['currency'] . ", " . $loaded['perPage'] . " per page\n";
var_dump($loaded['features']['reviews']);INR, 20 per page
bool(true)When JavaScript sends JSON to your PHP page with fetch(), it arrives in the request body, not in $_POST. Read it with file_get_contents('php://input') and decode it. To answer with JSON, set the header and echo json_encode(), as shown in Superglobals, headers and redirects.
Common mistakes
- Not checking for decode errors, so broken input quietly becomes
null. - Getting objects when you expected arrays, by forgetting the
trueargument. - Sending a list with gaps in its keys, which becomes a JSON object.
- Reading $_POST for a JSON request. Use
php://input.
Exercise
Decode the JSON below, add a third item (Water Bottle, qty 3, price 12), work out the order total, add it as a total key and print the result as pretty JSON without escaped slashes or Unicode.
{"id":1042,"items":[{"product":"Mechanical Keyboard","qty":1,"price":89.5},{"product":"Ceramic Mug Set","qty":2,"price":18.75}]}Show one possible answer
<?php
$json = '{"id":1042,"items":[{"product":"Mechanical Keyboard","qty":1,"price":89.5},{"product":"Ceramic Mug Set","qty":2,"price":18.75}]}';
$order = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
$order['items'][] = ['product' => 'Water Bottle', 'qty' => 3, 'price' => 12];
$order['total'] = array_sum(array_map(fn($i) => $i['qty'] * $i['price'], $order['items']));
echo json_encode($order, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n";{
"id": 1042,
"items": [
{
"product": "Mechanical Keyboard",
"qty": 1,
"price": 89.5
},
{
"product": "Ceramic Mug Set",
"qty": 2,
"price": 18.75
},
{
"product": "Water Bottle",
"qty": 3,
"price": 12
}
],
"total": 163
}Key points
- json_encode() turns arrays and objects into a JSON string; json_decode() turns JSON back into PHP values.
- json_decode($json, true) gives associative arrays; without true you get stdClass objects.
- JSON_PRETTY_PRINT, JSON_UNESCAPED_UNICODE and JSON_UNESCAPED_SLASHES make output readable.
- Pass JSON_THROW_ON_ERROR so invalid JSON throws a JsonException instead of silently returning null.