PHP JSON
What Is JSON? In simple terms: JSON (JavaScript Object Notation) is a text format for exchanging data between programs. Web APIs, JavaScript front ends, mobile apps, and configuration files all use it
What Is JSON?
In simple terms: JSON (JavaScript Object Notation) is a text format for exchanging data between programs. Web APIs, JavaScript front ends, mobile apps, and configuration files all use it. PHP can turn its arrays and objects into JSON text and read JSON text back into PHP values.
{"id": 501, "customer": "Asha", "paid": true, "items": ["Pen", "Notebook"]}
Technically: JSON supports objects ({}), arrays ([]), strings, numbers, booleans (true/false), and null. PHP's JSON extension (always available since PHP 8.0) provides json_encode() to produce JSON and json_decode() to parse it, plus json_validate() (PHP 8.3+) to check JSON without decoding it.
How PHP Values Map to JSON
| PHP | JSON |
|---|---|
int, float | number |
string | string |
true / false | true / false |
null | null |
List array ([0 => 'a', 1 => 'b']) | array ["a","b"] |
| Associative array or list with gaps | object {"key": ...} |
| Object | object (public properties, or jsonSerialize() output) |
Encoding: json_encode()
Example
<?php
$order = [
'id' => 501,
'customer' => 'Asha',
'paid' => true,
'coupon' => null,
'items' => ['Pen', 'Notebook'],
'total' => 1250.5,
];
echo json_encode($order) . "\n";
Output
{"id":501,"customer":"Asha","paid":true,"coupon":null,"items":["Pen","Notebook"],"total":1250.5}
- The associative array becomes a JSON object; the list
itemsbecomes a JSON array. - Types are preserved: numbers stay numbers,
truestaystrue, andnullstaysnull.
Common Flags
<?php
$data = ['name' => 'ஆஷா', 'url' => 'https://gocourse.in/php', 'price' => 1299.0];
echo json_encode($data) . "\n";
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION) . "\n";
echo json_encode(['a' => 1, 'b' => [1, 2]], JSON_PRETTY_PRINT) . "\n";
Output
{"name":"\u0b86\u0bb7\u0bbe","url":"https:\/\/gocourse.in\/php","price":1299}
{"name":"ஆஷா","url":"https://gocourse.in/php","price":1299.0}
{
"a": 1,
"b": [
1,
2
]
}
| Flag | Effect |
|---|---|
JSON_UNESCAPED_UNICODE | Writes non-ASCII characters (Tamil, Hindi, emoji) directly instead of \uXXXX escapes |
JSON_UNESCAPED_SLASHES | Writes / instead of \/ |
JSON_PRESERVE_ZERO_FRACTION | Keeps 1299.0 as a float (1299.0) instead of 1299 |
JSON_PRETTY_PRINT | Human-readable indentation (for files and debugging, not for API responses) |
JSON_THROW_ON_ERROR | Throws JsonException on errors (recommended) |
JSON_FORCE_OBJECT | Encodes every array as an object |
JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT | Escapes <, >, &, ', " — for embedding JSON in HTML (see Escaping Output) |
JSON_NUMERIC_CHECK | Converts numeric strings to numbers — use with care: phone numbers or PIN codes like "060042" lose their leading zero |
Both escaped and unescaped forms are valid JSON; any JSON parser decodes \u0b86 back to ஆ.
Decoding: json_decode()
Syntax
json_decode(string $json, ?bool $associative = null, int $depth = 512, int $flags = 0): mixed
Objects vs Associative Arrays
<?php
$json = '{"id": 7, "name": "Asha", "tags": ["php", "mysql"]}';
$asObject = json_decode($json);
$asArray = json_decode($json, true);
echo $asObject->name . "\n";
echo $asArray['tags'][1] . "\n";
var_dump($asObject instanceof stdClass);
Output
Asha
mysql
bool(true)
- Without the second argument, JSON objects become
stdClassobjects (accessed with->). - With
true, they become associative arrays (accessed with[]), which work with all array functions. Most applications usetrue.
Handling Invalid JSON
<?php
$broken = '{"id": 7,}';
var_dump(json_decode($broken, true));
echo json_last_error_msg() . "\n";
try {
json_decode($broken, true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo get_class($e) . ": " . $e->getMessage() . "\n";
}
Output
NULL
Syntax error
JsonException: Syntax error
Why JSON_THROW_ON_ERROR Matters
Without the flag, invalid JSON returns null — which is also the correct result for the valid JSON text null. Checking json_last_error() after every call is easy to forget. With JSON_THROW_ON_ERROR, invalid input throws an exception that cannot be silently ignored. Use it for both json_encode() and json_decode().
Validating Without Decoding (PHP 8.3+)
<?php
var_dump(json_validate('{"id": 7}'));
var_dump(json_validate('{id: 7}'));
Output
bool(true)
bool(false)
json_validate() checks syntax using less memory than decoding — useful when you only need to know whether a large payload is valid JSON (for example, before storing it).
Pitfalls
Empty Arrays vs Empty Objects
<?php
echo json_encode(['tags' => [], 'meta' => []]) . "\n";
echo json_encode(['tags' => [], 'meta' => new stdClass()]) . "\n";
Output
{"tags":[],"meta":[]}
{"tags":[],"meta":{}}
An empty PHP array is encoded as an empty JSON array. If a client expects an object for a field (such as a settings map), use new stdClass() or (object) [] for empty values.
Lists With Gaps Become Objects
After unset() or array_filter(), a list may have gaps in its keys and is then encoded as an object ({"0":"a","2":"c"}). Use array_values() before encoding lists (see Indexed Arrays).
Large Integers
<?php
$json = '{"order_id": 12345678901234567890}';
var_dump(json_decode($json, true)['order_id']);
var_dump(json_decode($json, true, flags: JSON_BIGINT_AS_STRING)['order_id']);
Output
float(1.2345678901234567E+19)
string(20) "12345678901234567890"
Integers larger than PHP_INT_MAX are converted to floats and lose precision. JSON_BIGINT_AS_STRING keeps them exact as strings. Many APIs send large IDs as strings for this reason.
Invalid UTF-8
json_encode() fails if a string contains invalid UTF-8 (for example, data from a misconfigured legacy database). JSON_INVALID_UTF8_SUBSTITUTE replaces invalid bytes with � instead of failing.
Controlling Object Output: JsonSerializable
By default, json_encode() outputs an object's public properties. Implementing JsonSerializable lets a class decide exactly what its JSON looks like:
<?php
final class Product implements JsonSerializable
{
public function __construct(
private int $id,
private string $name,
private int $pricePaise,
private DateTimeImmutable $addedAt,
private string $supplierCost = 'internal',
) {}
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->pricePaise / 100,
'added_at' => $this->addedAt->format(DATE_ATOM),
];
}
}
$product = new Product(7, 'Desk Lamp', 129900, new DateTimeImmutable('2026-10-15 09:30:00+05:30'));
echo json_encode($product, JSON_THROW_ON_ERROR) . "\n";
Output
{"id":7,"name":"Desk Lamp","price":1299,"added_at":"2026-10-15T09:30:00+05:30"}
- Private properties are included only through
jsonSerialize(). - Internal data (
supplierCost) is not exposed. - Values can be transformed for the API: the price is stored in paise but sent in rupees, and the date is formatted as ISO 8601.
Practical Example: A JSON API Endpoint
<?php
// api/products.php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
function respond(int $status, array $body): never
{
http_response_code($status);
echo json_encode($body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
if ($method === 'GET') {
respond(200, ['data' => [
['id' => 1, 'name' => 'Gel Pen', 'price' => 20],
['id' => 2, 'name' => 'Notebook', 'price' => 60],
]]);
}
if ($method === 'POST') {
try {
$input = json_decode(file_get_contents('php://input'), true, 32, JSON_THROW_ON_ERROR);
} catch (JsonException) {
respond(400, ['error' => 'Request body must be valid JSON.']);
}
$name = is_array($input) && is_string($input['name'] ?? null) ? trim($input['name']) : '';
$price = is_array($input) ? filter_var($input['price'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]) : false;
if ($name === '' || $price === false) {
respond(422, ['error' => 'Fields "name" (string) and "price" (positive integer) are required.']);
}
respond(201, ['data' => ['id' => 3, 'name' => $name, 'price' => $price]]);
}
respond(405, ['error' => 'Method not allowed.']);
Testing With curl
curl -X POST -H "Content-Type: application/json" \
-d '{"name": "Desk Lamp", "price": 1299}' \
http://localhost:8000/api/products.php
Response (status 201)
{"data":{"id":3,"name":"Desk Lamp","price":1299}}
Step-by-Step Explanation
- The
Content-Type: application/jsonheader tells clients how to interpret the response. respond()sets the status code, encodes the body, and stops the script; theneverreturn type documents that it does not return.- The request body is read from
php://input(JSON is not available in$_POST) and decoded with a depth limit of 32, which protects against deeply nested malicious input. - Invalid JSON returns 400 Bad Request; valid JSON with invalid fields returns 422 Unprocessable Content; a successful creation returns 201 Created.
catch (JsonException)without a variable (PHP 8.0+) is used because the exception details are not needed.
Building complete APIs is covered in Project: REST API with PHP.
Common Mistakes
Not Checking for Errors
Effect: Invalid JSON silently becomes null.
Fix: Use JSON_THROW_ON_ERROR.
Forgetting true in json_decode()
Effect: You get stdClass objects, and $data['key'] fails.
Fix: json_decode($json, true).
Encoding Filtered Lists Without Re-Indexing
Effect: Clients receive objects instead of arrays.
Fix: array_values() before encoding.
Using JSON_NUMERIC_CHECK Blindly
Effect: Phone numbers, PIN codes, and IDs with leading zeros are corrupted.
Fix: Cast specific fields explicitly instead.
Echoing JSON Into HTML Without Escaping Flags
Effect: </script> inside a value can break out of a script block.
Fix: Use JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT when embedding JSON in HTML.
Best Practices
- Always use
JSON_THROW_ON_ERROR. - Decode to associative arrays and validate the structure before using the data.
- Use
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHESfor readable output. - Control object output with
JsonSerializable, and never expose internal fields. - Send correct
Content-Typeheaders and HTTP status codes in JSON APIs.
Official reference: https://www.php.net/manual/en/book.json.php