Lesson 90 of 100
JSON15 min5 checksPHP 8.5

Validate JSON Before Decoding

Learn when json_validate() is the right PHP 8.4 tool, why calling it right before json_decode() parses twice, and how to report bad payloads.

01 · THE IDEAREAD → TRY → LEARN

The problem

Data arriving from outside your program is just a string. Before you trust it, you need to know whether it is JSON at all. For years the only built-in way to find out was json_decode(), which builds a full PHP array or object even when you only wanted a yes or no answer. PHP 8.3 added json_validate() for that case, and it is available in PHP 8.4.

The signature

json_validate(string $json, int $depth = 512, int $flags = 0): bool

It returns true when the string is syntactically valid JSON, and false otherwise. It never builds the decoded structure, so it uses less memory than json_decode() when you discard the result. The input must be UTF-8. The only flag it accepts is JSON_INVALID_UTF8_IGNORE. A depth outside the allowed range, or an unsupported flag, throws a ValueError.

If the function returns true, then json_decode() with the same depth and flags will decode the string successfully.

A first look

A well-formed object passes. An object with a trailing comma does not.

Example
<?php
var_dump(json_validate('{"name":"Ada","tags":[1,2]}'));
var_dump(json_validate('{"name":"Ada",}'));
Output
bool(true)
bool(false)

A false result is not a thrown error. If you want to know why validation failed, ask json_last_error_msg() straight afterwards.

Example
<?php
$ok = json_validate('{"a":1,}');
echo json_encode(
    ['valid' => $ok, 'message' => json_last_error_msg()],
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
Output
{"valid":false,"message":"Syntax error"}

Depth

The second parameter limits how deeply arrays and objects may nest. This protects you from absurdly nested input. The depth limit must exceed the array/object nesting level: [1] needs depth 2 and [[1]] needs depth 3. A depth of 1 rejects both of these arrays.

Example
<?php
$tooDeep = json_validate('[[1]]', 1);
$message = json_last_error_msg();
$fits = json_validate('[[1]]', 3);
echo json_encode(
    ['depth1' => $tooDeep, 'message' => $message, 'depth3' => $fits],
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
Output
{"depth1":false,"message":"Maximum stack depth exceeded","depth3":true}

When not to use it

The manual warns about one trap. If you call json_validate() and then immediately call json_decode() on the same string, you parse it twice. json_decode() already validates as it goes, so in that situation just decode once and handle failure, for example with the JSON_THROW_ON_ERROR flag and a try block.

Reach for json_validate() when you need the verdict but not the data. Typical cases are screening a batch of stored strings, checking a value before passing it on untouched, or reporting which records are broken.

Edge cases to remember

  • An empty string is not valid JSON.
  • A string holding only whitespace is not valid JSON.
  • Scalars such as 0, "x" and null are valid JSON documents on their own. Do not confuse the literal null with a decode failure.
  • Because json_decode() returns null both for the JSON text null and for errors, comparing its result to null is an unreliable validity test. json_validate() removes that ambiguity.

Exercise contract

Write invalid_json_indexes(array $payloads, int $depth = 512): array. It receives a list of values and returns a list of the keys whose values are not valid JSON, in their original order. Anything that is not a string counts as invalid. Return an empty array when everything is valid or the input is empty. Pass $depth through to json_validate().

The tests check the returned arrays, not printed text. Pay attention to the literal null, to empty strings, and to depth.

Recap

  • json_validate() answers "is this valid JSON?" without building the data.
  • Use json_last_error_msg() right after a false result to explain the failure.
  • Do not validate and then decode the same string. Decode once and handle errors.
  • Remember that null is valid JSON, and that depth is a real limit.
Before you start

Functions · Arrays · Types

You’ll practice
  • Use json_validate where appropriate and explain double-parsing costs
  • Implement and test a deterministic PHP 8.4 function
Your task

Make invalid_json_indexes() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.