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.
var_dump(json_validate('{"name":"Ada","tags":[1,2]}'));
var_dump(json_validate('{"name":"Ada",}'));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.
$ok = json_validate('{"a":1,}');
echo json_encode(
['valid' => $ok, 'message' => json_last_error_msg()],
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);{"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.
$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
);{"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"andnullare valid JSON documents on their own. Do not confuse the literalnullwith a decode failure. - Because
json_decode()returnsnullboth for the JSON textnulland for errors, comparing its result tonullis 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 afalseresult to explain the failure. - Do not validate and then decode the same string. Decode once and handle errors.
- Remember that
nullis valid JSON, and that depth is a real limit.
Functions · Arrays · Types
- Use json_validate where appropriate and explain double-parsing costs
- Implement and test a deterministic PHP 8.4 function