skip to content

JSON Encode & Decode

json_encode and json_decode with their flags, depth and error reporting, plus JsonSerializable for custom output. Interviewers probe the silent null on bad input and empty array vs object.

part ofPHPoverview, primer and where to startread it →
on this pageshow

explore

questions

6

In PHP, what does json_decode() return for a JSON object when its associative argument is omitted, and what changes when it is true?

level: juniorimportance: must knowfreq 74%

answer

  1. objects vs arrays on the PHP side
  2. stdClass by default
  3. arrow access vs bracket access
  4. JSON arrays are PHP arrays either way
  5. JSON_OBJECT_AS_ARRAY is the flag form

basics

~10 s

By default json_decode() turns every JSON object into a stdClass object, read with ->; with the associative argument true, objects become associative arrays, read with []. JSON arrays become PHP arrays in both modes.

solid answer

~30 s

`json_decode($json)` returns PHP values: strings, ints, floats, bools and `null` map directly, JSON arrays become indexed PHP arrays, and JSON **objects** become `stdClass` instances by default, so you read `$data->user->name`. Passing `true` as the second argument, `$associative`, decodes every object at every level as an **associative array** instead, so you read `$data['user']['name']`. The flag `JSON_OBJECT_AS_ARRAY` does the same, but an explicit `true` or `false` in `$associative` overrides it; only `null` (the default) lets the flag decide. Associative arrays are the common choice for payloads you feed into array functions; objects keep the JSON object-versus-array distinction that arrays lose.

code

php · 15 lines
php
<?php
declare(strict_types=1);

$json = '{"member":{"name":"Ana","tags":["board","editor"]}}';

$asObject = json_decode($json);
var_dump($asObject->member->name);   // string(3) "Ana"
var_dump($asObject->member->tags);   // array(2) - JSON arrays stay arrays

$asArray = json_decode($json, true);
var_dump($asArray['member']['name']); // string(3) "Ana"

// false beats the flag; this still returns stdClass objects
$still = json_decode($json, false, 512, JSON_OBJECT_AS_ARRAY);
var_dump($still instanceof stdClass); // bool(true)

go deeper

for a junior

Recall that the default gives stdClass objects read with -> and that passing true gives associative arrays read with brackets.

for a middle

Explain that the switch applies at every depth, how JSON_OBJECT_AS_ARRAY interacts with it, and what information array mode loses.

for a senior

Choose the decode mode per boundary: arrays for validation pipelines, objects where empty-object versus empty-list must survive a round trip.

for a principal

Set a codebase convention for decoding external payloads and for mapping them into typed objects, so every boundary does not invent its own.

## The signature In PHP 8.5, `json_decode()` is declared as: ```php json_decode(string $json, ?bool $associative = null, int $depth = 512, int $flags = 0): mixed ``` It parses a JSON text and returns the matching PHP value. The return type is `mixed` because the top-level JSON value can be anything: `json_decode('5')` returns the int `5`, `json_decode('"hi"')` the string `hi`, `json_decode('[1,2]')` an array. ## How each JSON type maps | JSON value | PHP value (default) | PHP value with `$associative = true` | |---|---|---| | object `{"a":1}` | `stdClass` with property `a` | `['a' => 1]` | | array `[1,2]` | `[1, 2]` (indexed array) | `[1, 2]` | | string | `string` | `string` | | integer / fraction | `int` / `float` | `int` / `float` | | `true` / `false` / `null` | `bool` / `null` | `bool` / `null` | Two points trip people up: - **JSON arrays are always PHP arrays.** The `$associative` switch affects objects only. - **The switch is deep.** It applies to every object at every nesting level, not just the top one. ## Reading the result With the default, you navigate with the object operator: ```php $order = json_decode('{"id":7,"lines":[{"sku":"A1"}]}'); echo $order->lines[0]->sku; // A1 ``` With `true`, you navigate with brackets: ```php $order = json_decode('{"id":7,"lines":[{"sku":"A1"}]}', true); echo $order['lines'][0]['sku']; // A1 ``` Mixing them up gives the classic errors: `Cannot use object of type stdClass as array` when you bracket an object, or a warning about reading a property on an array when you arrow into an array. ## The flag form and who wins `JSON_OBJECT_AS_ARRAY` in `$flags` requests the same array decoding. The extension's source spells out the rule: an explicit boolean `$associative` overrides the flag. So: 1. `json_decode($j)` gives objects; 2. `json_decode($j, null, 512, JSON_OBJECT_AS_ARRAY)` gives arrays; 3. `json_decode($j, false, 512, JSON_OBJECT_AS_ARRAY)` gives objects, because `false` wins. Named arguments keep this readable: `json_decode($j, flags: JSON_THROW_ON_ERROR)`. ## Choosing between them - **Arrays** fit most application code: they work with `array_map()`, `array_column()`, destructuring and `isset($a['key'])`, and they are what most validation code expects. - **Objects** keep information that arrays drop: an empty JSON object decodes to an empty `stdClass`, which encodes back to `{}`, while with `true` it decodes to `[]` and encodes back as `[]`. - **Neither** gives you a typed domain object. `json_decode()` never calls a constructor or fills a class of your choosing; mapping into your own classes is a separate step. ## Keys that look like numbers JSON object keys are strings. In array mode, PHP applies its normal array-key rules, so `{"10":"x"}` becomes `[10 => 'x']` with an int key. In object mode the property is named `10` and must be read as `$obj->{'10'}`. ## Depth and flags in the same call The third and fourth parameters are easy to mix up because `$depth` comes before `$flags`: - `json_decode($j, true, 512, JSON_THROW_ON_ERROR)` is correct; - `json_decode($j, true, JSON_THROW_ON_ERROR)` passes the flag's integer value as the **depth**, so errors are not thrown at all; - `json_decode($j, true, flags: JSON_THROW_ON_ERROR)` avoids the problem with a named argument. `$depth` (default 512) is the maximum nesting of arrays and objects; deeper input fails with `JSON_ERROR_DEPTH`. ## Quick self-test 1. What type is `json_decode('{"a":[]}')->a`? An array, because JSON arrays are always PHP arrays. 2. What does `json_decode('{"a":{}}', true)['a']` hold? An empty array. 3. How do you read key `first-name` from an object result? `$obj->{'first-name'}`, since the name is not a valid identifier.

  • What does json_decode('{}', true) re-encode to with json_encode(), and why does it matter?
    It decodes to an empty PHP array, and `json_encode([])` produces `[]`. The empty object has become an empty list, which breaks a consumer that expects an object. Decoding with the default object mode keeps an empty `stdClass`, which encodes back to `{}`.
  • Does json_decode() ever create an instance of your own class?
    No. It only produces scalars, arrays and `stdClass` objects. Turning the result into a domain object is your code's job, for example a named constructor that reads the array and validates each field.

saying these in an interview costs you the question

  • Thinking JSON arrays decode to stdClass without the associative flag
  • Believing the associative argument only affects the top-level object
  • Expecting json_decode() to hydrate a class of your choosing
  • Assuming JSON_OBJECT_AS_ARRAY beats an explicit false argument
  • Saying arrays and objects are interchangeable after decoding
open as a page

In PHP, why is comparing json_decode()'s result with null an unreliable error check, and what does JSON_THROW_ON_ERROR change?

level: middleimportance: must knowfreq 62%

basics

~10 s

json_decode() returns null both for invalid input and for the valid JSON text null, so a null check cannot tell them apart. JSON_THROW_ON_ERROR makes json_decode() and json_encode() throw JsonException instead of setting json_last_error().

open as a page

In PHP, why does json_encode() return false for some database rows, and which flags control how slashes and non-ASCII text are escaped?

level: middleimportance: should knowfreq 42%

basics

~10 s

json_encode() requires valid UTF-8; a Latin-1 string makes it fail with JSON_ERROR_UTF8 and return false. By default it escapes / as / and non-ASCII as \uXXXX; JSON_UNESCAPED_SLASHES and JSON_UNESCAPED_UNICODE turn that off.

open as a page

In PHP, which properties does json_encode() include for an object, and how does implementing JsonSerializable change the output?

level: middleimportance: should knowfreq 40%

basics

~10 s

By default json_encode() writes an object's initialized public properties and skips protected and private ones. A class implementing JsonSerializable is encoded as whatever its jsonSerialize(): mixed method returns instead.

open as a page

Why does PHP's json_encode() sometimes emit a list as a JSON object, or an empty map as [], and how do you prevent it?

level: middleimportance: should knowfreq 48%

basics

~20 s

json_encode() writes an array as a JSON list only if its keys are 0..n-1 in order, else as an object, and an empty array counts as a list. Reindex lists with array_values() and cast maps with (object).

open as a page

A PHP service decoding a partner's JSON corrupts 20-digit order IDs; why does json_decode() do that, and which flag fixes it?

level: seniorimportance: should knowfreq 30%

basics

~10 s

json_decode() turns an integer literal beyond PHP_INT_MAX into a float, which cannot hold 20 exact digits, so the ID is rounded. Pass JSON_BIGINT_AS_STRING to keep the original digits as a string.

open as a page