The fastest way is to "maybe decode" the possible JSON string
Is this really the fastest method?
If you want to decode complex objects or larger arrays, this is the fastest way, by far!
If your json string contains short values (like scalars or objects with only 1-2 attributes) then all solutions in this SO questions come to a similar performance.
Here is a performance comparison that I've done with some dummy and real-live JSON values:
PHP version: 7.4.12
1 | Duration: 1.5828 sec | 60,000 calls | json_last_error() == JSON_ERROR_NONE
2 | Duration: 1.5202 sec | 60,000 calls | is_object( json_decode() )
3 | Duration: 1.5400 sec | 60,000 calls | json_decode() && $res != $string
4 | Duration: 1.3536 sec | 60,000 calls | preg_match()
5 | Duration: 0.2261 sec | 60,000 calls | "maybe decode" approach
The last line is the code from this answer, which is the "maybe decode" approach.
Here is the performance comparison script, there you can see the test-data I used for the comparison: https://gist.github.com/stracker-phil/6a80e6faedea8dab090b4bf6668ee461
The "maybe decode" logic/code
We first perform some type checks and string comparisons before attempting to decode the JSON string. This gives us the best performance because json_decode() can be slow.
/**
* Returns true, when the given parameter is a valid JSON string.
*/
function is_json( $value ) {
// A non-string value can never be a JSON string.
if ( ! is_string( $value ) ) { return false; }
// Numeric strings are always valid JSON.
if ( is_numeric( $value ) ) { return true; }
// Any non-numeric JSON string must be longer than 2 characters.
if ( strlen( $value ) < 2 ) { return false; }
// "null" is valid JSON string.
if ( 'null' === $value ) { return true; }
// "true" and "false" are valid JSON strings.
if ( 'true' === $value ) { return true; }
if ( 'false' === $value ) { return false; }
// Any other JSON string has to be wrapped in {}, [] or "".
if ( '{' != $value[0] && '[' != $value[0] && '"' != $value[0] ) { return false; }
// Note the last param (1), this limits the depth to the first level.
$json_data = json_decode( $value, null, 1 );
// When json_decode fails, it returns NULL.
if ( is_null( $json_data ) ) { return false; }
return true;
}
Extra: Use this logic to safely double-decode JSON
This function uses the same logic but either returns the decoded JSON object or the original value.
I use this function in a parser that recursively decodes a complex object. Some attributes might be decoded already by an earlier iteration. That function recognizes this and does not attempt to double decode the value again.
/**
* Tests, if the given $value parameter is a JSON string.
* When it is a valid JSON value, the decoded value is returned.
* When the value is no JSON value (i.e. it was decoded already), then
* the original value is returned.
*/
function get_data( $value, $as_object = false ) {
if ( ! is_string( $value ) ) { return $value; }
if ( is_numeric( $value ) ) { return 0 + $value; }
if ( strlen( $value ) < 2 ) { return $value; }
if ( 'null' === $value ) { return null; }
if ( 'true' === $value ) { return true; }
if ( 'false' === $value ) { return false; }
if ( '{' != $value[0] && '[' != $value[0] && '"' != $value[0] ) { return $value; }
$json_data = json_decode( $value, $as_object );
if ( is_null( $json_data ) ) { return $value; }
return $json_data;
}
Note: When passing a non-string to any other solution in this SO question, you will get dramatically degraded performance + wrong return values (or even fatal errors). This code is bulletproof and highly performant.