skip to content

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