Error Handling

json_last_error_msg() returns the error string of the last json_encode() or json_decode() call.

json_last_error()
returns an integer which can be JSON_ERROR_{NONE|DEPTH|STATE_MISMATCH| CTRL_CHAR|SYNTAX|UTF8|RECURSION|INF_OR_NAN |UNSUPPORTED_TYPE}.

<?php
$o = json_decode('{"a":1,"b":2,"c":3,'); // extra comma
echo json_last_error_msg();
if (json_last_error() == JSON_ERROR_SYNTAX) echo 1;
?>

Syntax error1
PHP 7.3 added the JSON_THROW_ON_ERROR flag for both json_encode() and json_decode(). When this flag is set, a JSON error throws a JsonException (carrying the same message json_last_error_msg() would give) instead of silently returning false/null and requiring a separate json_last_error() check.

<?php
try {
  json_decode('{"a":1,"b":2,', true, 512, JSON_THROW_ON_ERROR); // extra comma
} catch (JsonException $e) {
  echo "Decode: " . $e->getMessage() . "<br />";
}

try {
  echo json_encode(NAN, JSON_THROW_ON_ERROR); // NAN cannot be represented in JSON
} catch (JsonException $e) {
  echo "Encode: " . $e->getMessage() . "<br />";
}

Decode: Syntax error
Encode: Inf and NaN cannot be JSON encoded
PHP 8.3 added json_validate($s[,$depth=512[,$flags=0]]), which returns true if $s is syntactically valid JSON. It uses the same underlying parser as json_decode(), but does not build the resulting value, so it is faster and uses less memory when you only need to check validity rather than use the decoded data.

<?php
var_dump(json_validate('{"a":1,"b":2}'));
var_dump(json_validate('{"a":1,"b":2,}')); // trailing comma
var_dump(json_validate('not json'));

bool(true) bool(false) bool(false)