skip to content

Data Formats

PHP ships encoders for CSV, JSON, XML and its own serialize format, each with flags and failure modes of its own. Interviewers probe error handling, streaming large inputs and untrusted data.

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

explore

questions

23

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, how do you read an element's text and an attribute with SimpleXML, and why do you cast the results to string?

level: juniorimportance: must knowfreq 50%

basics

~20 s

simplexml_load_string() returns a SimpleXMLElement: child elements are read as properties ($p->name) and attributes with array syntax ($p['sku']). Both return SimpleXMLElement objects, so cast with (string), (int) or (float) before comparing, storing or passing them on.

open as a page

In PHP, how do you read a multi-gigabyte CSV file with fgetcsv() without exhausting memory, and how do you detect the end of the file?

level: middleimportance: must knowfreq 52%

basics

~20 s

Open the file with fopen() and call fgetcsv() in a loop until it returns false; each call parses one record, so memory stays flat. A blank line returns [null], not false, so skip it explicitly.

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, when should you store a value with serialize() rather than json_encode(), and what does each round trip lose?

level: middleimportance: must knowfreq 58%

basics

~20 s

Use serialize() only for PHP-to-PHP storage you control, because it restores exact types, classes, private properties and shared references. Use json_encode() for anything another program or an untrusted party touches: portable and inert, but classes and non-public state are lost.

open as a page

In PHP 8.5, when do you choose SimpleXML, DOMDocument or the Dom\XMLDocument class added in 8.4 for reading XML?

level: middleimportance: must knowfreq 48%

basics

~20 s

SimpleXML is quickest for reading known structures; DOMDocument gives the full node API for editing, precise namespaces and XPath; Dom\XMLDocument, added in 8.4, is the spec-compliant successor with querySelector(). All three hold the whole document in memory.

open as a page

In PHP, why does calling str_getcsv() on each piece of explode("\n", $csv) corrupt records whose quoted fields contain line breaks?

level: juniorimportance: should knowfreq 33%

basics

~10 s

str_getcsv() parses one string as one record, and explode() splits on every newline, including those inside quoted fields. Feed the text to fgetcsv() through a php://temp stream instead, which continues a record across lines.

open as a page

In PHP, how do you read the string serialize() produces, such as a:2:{i:0;s:3:"foo";i:1;b:1;}?

level: juniorimportance: should knowfreq 38%

basics

~20 s

Each value is a type tag plus payload: i:42; is an int, s:3:"foo"; a string with its byte length, a:2:{...} an array with its element count, O: an object with its class name; b:, d: and N; cover bool, float, null.

open as a page

In PHP, what must a fputcsv() export of a membership list do so that Excel shows accented names and splits the columns correctly?

level: middleimportance: should knowfreq 38%

basics

~10 s

Write the UTF-8 byte-order mark "\xEF\xBB\xBF" before the first row, since fputcsv() never adds one, choose the separator the audience's Excel expects, and pass escape '' and, if wanted, eol "\r\n".

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

In PHP 8.5, how do __serialize() and __unserialize() differ from __sleep() and __wakeup(), and which should a new class implement?

level: middleimportance: should knowfreq 45%

basics

~20 s

__sleep() returns property names to keep and __wakeup() runs after they are restored; __serialize() returns any array and __unserialize() rebuilds the object from it. New classes should use __serialize()/__unserialize(): the older pair is soft-deprecated in PHP 8.5.

open as a page

In PHP, how do you make simplexml_load_string() or DOMDocument::loadXML() report malformed XML as data instead of emitting warnings?

level: middleimportance: should knowfreq 35%

basics

~10 s

Call libxml_use_internal_errors(true) before parsing; the loader then returns false silently and libxml_get_errors() returns LibXMLError objects with message, line and column. Call libxml_clear_errors() and restore the previous setting afterwards.

open as a page

In PHP, why does DOMXPath::query('//product') return an empty list for a feed that declares a default xmlns, and how do you fix it?

level: middleimportance: should knowfreq 30%

basics

~10 s

In XPath 1.0 an unprefixed name matches only elements in no namespace, but a default xmlns puts every element into that namespace. Register a prefix with DOMXPath::registerNamespace('f', $uri) and query '//f:product'.

open as a page

A PHP export written with fputcsv() defaults breaks other CSV readers when a field contains a backslash; what is the escape-parameter trap and how do you fix it?

level: seniorimportance: should knowfreq 28%

basics

~20 s

fputcsv(), fgetcsv() and str_getcsv() default escape to a backslash, a non-standard mechanism: a quote after a backslash is not doubled, and a trailing backslash can swallow the next field. Pass escape: '' on both sides.

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

In PHP, what do unserialize()'s allowed_classes and max_depth options change, and what does each leave unprotected?

level: seniorimportance: should knowfreq 40%

basics

~10 s

allowed_classes limits which classes unserialize() instantiates; any other class becomes __PHP_Incomplete_Class. max_depth caps nesting, default 4096 from unserialize_max_depth. Neither makes untrusted input safe: listed classes still run their hooks, and size is not bounded.

open as a page

After a PHP deploy renames or retypes properties of a class whose objects sit serialized in a cache, what does unserialize() do with the old payloads?

level: seniorimportance: should knowfreq 30%

basics

~10 s

unserialize() assigns stored properties by name: an undeclared key becomes a dynamic property (deprecated since 8.2, Error on readonly classes), a new typed property stays uninitialized, and a wrongly typed value throws TypeError.

open as a page

In PHP 8, why is passing LIBXML_NOENT to simplexml_load_string() or DOMDocument::loadXML() dangerous for an untrusted supplier feed?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Since PHP 8.0 requires libxml2 2.9+, external entities are not loaded by default. LIBXML_NOENT turns entity substitution on, so a crafted DOCTYPE can pull local files or URLs into the parsed document. Leave it off for untrusted XML.

open as a page

A supplier's 3 GB XML product feed exhausts memory under simplexml_load_file(); how do you process it in PHP with XMLReader instead?

level: seniorimportance: should knowfreq 32%

basics

~20 s

simplexml_load_file() builds the whole tree in memory. XMLReader pulls one node at a time: reach each <product> with read() and next('product'), expand() just that element into a small tree, process it and move on, so memory stays flat.

open as a page

In PHP, how do you iterate a CSV file with SplFileObject::READ_CSV, and which flags and setCsvControl() settings avoid blank-row and escape surprises?

level: middleimportance: nice to knowfreq 18%

basics

~10 s

Call setFlags(SplFileObject::READ_CSV | READ_AHEAD | SKIP_EMPTY | DROP_NEW_LINE) so foreach yields parsed rows and skips blank lines, and setCsvControl(',', '"', '') so the backslash escape is off.

open as a page

In PHP, how does var_export() let you cache a computed tariff table as a PHP file, and which values can it not export?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

var_export($value, true) returns PHP source for the value; write '<?php return ' . that . ';' to a file and require it later. Scalars, arrays, stdClass and enums work; cycles and resources do not, and other objects need __set_state().

open as a page