PHP 8.3: Added json_validate function

Version8.3
TypeNew Feature

PHP 8.3 adds a new function named json_validate that returns true or false whether the given string is a valid JSON string.

Prior to PHP 8.3, the only way to determine if a given string is a valid JSON string was to attempt to decode it, and see if any errors were emitted. The new json_validate function uses the same underlying JSON parser PHP uses, but consumes less memory and processing as json_decode only analyzes the string without constructing any decoded value.

Applications that have strong measures to prevent invalid JSON strings from being processed may not find the new json_validate function useful, because a json_validate call immediately followed by a json_decode can slightly increase the execution time as the JSON string is analyzed twice, and there is a higher chance of the input JSON string being valid JSON in the place.

Applications that accept user-provided JSON, or connect remote JSON APIs may find the best use of the new json_validate, because there is a significant chance of encountering invalid JSON strings.

json_validate('[1, 2, 3]'); // true
json_validate('{1, 2, 3]'); // false

json_validate function synopsis


/**  
 * Validates a given string to be valid JSON.
 * 
 * @param string $json String to validate  
 * @param int $depth Set the maximum depth. Must be greater than zero.  
 * @param int $flags Bitmask of flags.  
 * @return bool True if $json contains a valid JSON string, false otherwise.  
 */  
function json_validate(string $json, int $depth = 512, int $flags = 0): bool {  
}

It is possible to polyfill this function with user-land PHP code, using json_decode (which consumes memory, and thus has no intrinsic value-add).

json_validate accepted flags ($flags)

json_validate function accepts a bit-mask of flags for its third parameter $flags. While it is possible that additional flags will be added later in newer PHP versions, JSON_INVALID_UTF8_IGNORE is the only accepted flag for the $flags parameter.

JSON_INVALID_UTF8_IGNORE is an existing PHP constant (since PHP 7.2) accepted by the json_decode function as well. When passed, json_decode and json_validate functions ignore UTF-8 characters in the given string.

json_validate('[1, 2, 3]', flags: JSON_INVALID_UTF8_IGNORE); // true

json_validate("[\"\xc1\xc1\",\"a\"]"); // false
json_validate("[\"\xc1\xc1\",\"a\"]", flags: JSON_INVALID_UTF8_IGNORE); // true

The snippet above uses named parameters added in PHP 8.0 and Hexadecimal character escape sequences.

Passing an unaccepted value results in a ValueError exception:

json_validate('', flags: JSON_BIGINT_AS_STRING);
json_validate(): Argument #3 ($flags) must be a valid flag (allowed flags: JSON_INVALID_UTF8_IGNORE).

json_validate validation errors

json_validate() function does not return the validation error code (such as syntax errors, depth exhaustion, unsupported type, etc). However, existing existing json_last_error and json_last_error_msg functions can be used to determine the validation error.

json_validate(""); // false

json_last_error(); // 4
json_last_error_msg(); // "Syntax error"
json_validate("null"); // true

json_last_error(); // 0
json_last_error_msg(); // "No error"

Similar to json_decode and json_encode functions, json_validate mutates the application state by storing the error code of the last operation, making json_validate not a pure function.
While this should have no significant impact on most PHP applications, there can be edge cases including race conditions on certain novelty PHP runners that serve multiple concurrent requests on the same thread.

Usage examples

The following are some usage examples of the new json_validate function. They do not check whether the json_validate function is available on the running PHP versions. Applications/packages that need to support PHP versions older than PHP 8.3 might need to either conditionally use the json_validate function, or polyfill it in user-land PHP code.


Validating a given JSON string

json_validate($_GET['json']);

Validating a given JSON string and throwing an exception

This mimics the JSON_THROW_ON_ERROR flag, introduced in PHP 7.3.

if (json_validate($_GET['json']) === false) {
    throw new \JsonException(json_last_error_msg(), json_last_error());
}

Replace existing JSON validation with new json_validate function

- $value = json_decode($_GET['json'], flags: JSON_THROW_ON_ERROR);
+ if (!json_validate($_GET['json'])) {
+   throw new \JsonException(json_last_error_msg(), json_last_error());
+ }
+ $value = json_decode($_GET['json']); 

User-land polyfill

json_validate() function's internal implementation consumes less memory and processing, and it is the main advantage of the json_validate function. It is possible to polyfill it in user-land PHP code by observing the errors accumulated in a json_decode code, but it provides no memory/processing improvement over the actual implementation.

This polyfill uses json_decode function followed by an error detection. Applications that call json_validate before calling json_decode this effectively means valid JSON strings are decoded twice. See Double-decode Cost for more information.

if (!function_exists('json_validate')) {
  function json_validate(string $json, int $depth = 512, int $flags = 0): bool {
    if ($flags !== 0 && $flags !== \JSON_INVALID_UTF8_IGNORE) {
      throw new \ValueError('json_validate(): Argument #3 ($flags) must be a valid flag (allowed flags: JSON_INVALID_UTF8_IGNORE)');  
    }

    if ($depth <= 0 ) {
      throw new \ValueError('json_validate(): Argument #2 ($depth) must be greater than 0');
    }

    \json_decode($json, null, $depth, $flags);

    return \json_last_error() === \JSON_ERROR_NONE;
  }
}

The above snippet should be functional in all PHP 8.0 and later. If the \ValueError exceptions were to be replaced with a different type of exception, the polyfill will be compatible with PHP >= 7.3.

Double-decode cost

Note that the polyfill above calls json_decode function inside, which fully decodes the provided JSON string. If the application decodes the JSON string after the json_validate call, this effectively doubles the processing resources because the polyfilled json_validate function calls json_decode.

The optimal use cases for json_validate include validating the user-provided JSON strings before passing it to another system and storing them. If the application calls json_decode right after a json_validate call, it might be more efficient to call json_decode and detect error conditions.

Backwards Compatibility Impact

json_validate() is a new function added in PHP 8.3. Existing PHP applications that declare a json_validate function in the global namespace will encounter a function redeclare error on PHP 8.3.

This function can be implemented in user-land PHP code, albeit it cannot provide the actual memory/processing benefits the actual json_validate function provides.


RFC Discussion Implementation