Quick answer:
json_encode()converts a PHP value (array, object, or scalar) into a JSON string.json_decode()converts a JSON string back into a PHP value. Passtrueas the second argument tojson_decode()to get associative arrays instead ofstdClassobjects, and use theJSON_THROW_ON_ERRORflag (PHP 7.3+) so malformed JSON throws aJsonExceptioninstead of quietly returningnull.
If you have ever taken a PHP array, turned it into an API response, and then wondered why the receiving code sees a string instead of an array, you have met the difference between json_encode() and json_decode(). They work in opposite directions, and choosing the wrong one produces confusing bugs.
Explore the resulting JSON in the JSON Viewer, or select nested fields with the JSONPath Query Tester.
Start with a familiar PHP array
Imagine a small order in an application:
$order = [
'id' => 1042,
'customer' => 'Maya',
'paid' => true,
'items' => ['notebook', 'pen'],
];
Your controller or integration needs to send this information to another service. PHP arrays cannot travel directly over an HTTP request as PHP arrays. The receiving system needs a text representation, and JSON is a common choice.
$json = json_encode($order, JSON_THROW_ON_ERROR);
echo $json;
Output:
{"id":1042,"customer":"Maya","paid":true,"items":["notebook","pen"]}
Notice that true stays a JSON boolean, and the items become a JSON array. That is encoding.
Now decode the response
Suppose a third-party API sends this body:
{"status":"paid","total":249.50,"currency":"USD"}
To access its values in PHP, decode the text:
$body = '{"status":"paid","total":249.50,"currency":"USD"}';
$data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
echo $data['status']; // paid
Why pass true as the second argument? Because it asks PHP for an associative array rather than the default stdClass object. If your code uses $data['status'], you want the associative array. If it uses $data->status, the default object form might be more convenient. The third argument, 512, is the maximum nesting depth; the default is fine for almost every API payload.
| Task | Function | Result |
|---|---|---|
| Send a PHP value as JSON text | json_encode() | string |
| Read JSON text into PHP | json_decode() | PHP value: array, object, string, number, boolean, or null |
| Pretty-print JSON | json_encode() with JSON_PRETTY_PRINT | Indented JSON string |
| Reject malformed JSON | json_decode() with JSON_THROW_ON_ERROR | Exception on error |
The error most developers forget to handle
A successful json_decode() returns null when the JSON text is literally null. But without the throw flag, a failed decode also returns null, and json_encode() returns false. Checking only === null won't distinguish a legitimate null from broken input; the old-style fix is to call json_last_error_msg() immediately after the call.
Use exceptions on supported PHP versions:
$body = '{"status":"paid",}'; // Invalid trailing comma
try {
$data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
error_log('Invalid JSON: ' . $exception->getMessage());
// Return an appropriate API error; don't continue with invalid data.
}
JSON_THROW_ON_ERROR is available from PHP 7.3 onward. It is a good default for modern applications because failure cannot be accidentally confused with a legitimate JSON null. The trailing comma in the example above is the single most common cause of that exception; the trailing comma guide shows how to spot it quickly in larger documents.
A few cases worth knowing
A PHP list and a PHP associative array are not the same JSON shape
json_encode(['red', 'blue'], JSON_THROW_ON_ERROR);
// ["red","blue"]
json_encode(['first' => 'red', 'second' => 'blue'], JSON_THROW_ON_ERROR);
// {"first":"red","second":"blue"}
PHP produces a JSON array only when the keys are sequential integers starting at 0. Remove one element from the middle of a list with unset() and the keys are no longer sequential, so the next json_encode() call emits an object. If your API consumer expects a JSON array but receives an object, inspect the keys of the PHP array you encoded (array_values() re-indexes them).
Pretty output is for reading, not for changing data
$pretty = json_encode(
$order,
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
);
Readable JSON is ideal for documentation, logs with appropriate redaction, and test fixtures. Compact JSON may be preferable for network payloads; HTTP compression is another separate consideration.
JSON_UNESCAPED_UNICODE keeps characters such as é or ₹ as they are instead of emitting é escapes. The related failure to know about: json_encode() refuses strings that are not valid UTF-8 and reports JSON_ERROR_UTF8. Convert the input with mb_convert_encoding() first, or pass JSON_INVALID_UTF8_SUBSTITUTE (PHP 7.2+) to replace the bad bytes. The JSON encoding and BOM guide covers where those bytes usually come from.
JSON is text, not a PHP serialization format
Dates, objects, resources, and large numeric identifiers need deliberate handling. For example, json_encode() turns a DateTimeImmutable into an object with date, timezone_type, and timezone keys, not the ISO 8601 string most clients expect. Map it to an explicit string in your API DTO first, for example with ->format(DATE_ATOM); see JSON date formats for the conventions.
Large integer IDs are a similar trap. PHP encodes them exactly, but a JavaScript consumer silently rounds anything above 253. Send such IDs as strings, and when decoding big numbers from another system use the JSON_BIGINT_AS_STRING flag. The JSON number precision guide explains the limits.
How to check the final JSON
Before blaming the API consumer, copy the final encoded string into JSON Prettifier to inspect its structure. A validator checks syntax; it does not know whether your application expects an items array or an items object. That second question belongs to application or schema validation.
If you are on Laravel, response()->json() calls json_encode() for you with sensible defaults; the Laravel JSON API response guide covers status codes and validation errors. For browser-side handling, see our guide to JSON.parse() and JSON.stringify(). To troubleshoot syntax failures, use the common JSON errors guide.
Bottom line
Encode when information is leaving PHP as JSON text. Decode when JSON text arrives and your PHP code needs to work with its values. Handle errors explicitly, and most JSON integration bugs become much easier to diagnose.