编程

PHP 8.3 功能: json_validate 函数

1685 2023-02-05 08:06:15

PHP 8.3 添加了一个名为 json_validate 的新函数,用来返回 truefalse 以判断给定的字符串是否是有效的 JSON 字符串。

PHP 8.3 之前,判断给定字符串是不是有效的 JSON 字符串的唯一方法是,尝试解码该字符串,看看是否会产生错误。新的 json_validate 函数底层使用了同一个 JSON 解析器,不过内存使用量更少且像 json_decode 那样处理、只分析字符串,而不去构造任何解码值。

具有防止处理无效 JSON 字符串的强大措施的应用程序,新增的 json_validate 函数可能用处不大。因为 json_validatejson_decode 调用后立即执行,会导致 JSON 字符串的分析执行了两次,从而执行时间轻微增加。而且输入的 JSON 字符串是有效的 JSON 的可能性更大。

但是,接收用户提供的 JSON 或连接远程 JSON API 的应用可能会发现新 json_validate 的最佳用途,因为很有可能遇到无效的 JSON 字符串。

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

json_validate 函数摘要


/**  
 * 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 {  
}
  • json_validate 在全局命名空间中声明。
  • 验证错误可以使用现有的 json_last_errorjson_last_error_msg 函数检索。
  • 查看 json_validate 中可接收的 flag。传入无效的 flag 会抛出错误异常。  

可以使用 json_decode 函数在用户空间的 PHP 代码中模拟该函数,使之向前兼容。

json_validate 接受的 flag ($flags)

json_validate 函数位操作的 flags,作为其第三个参数 $flags 的值。虽然在后续的 PHP 版本中可能添加其他的 flags, JSON_INVALID_UTF8_IGNORE 是$flags参数目前唯一可接收的 flag 值。

JSON_INVALID_UTF8_IGNORE 是现有的 PHP 常数(从 PHP 7.2 开始),json_decode 函数也可接收该参数。如果传入,json_decodejson_validate 函数将在给定的字符串中忽略 UTF-8 字符。

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

以上代码使用了PHP 8.0 中添加的命名参数及 16 进制转义序列。

json_validate 验证错误

json_validate() 函数不返回验证错误码(比如语法错误、深度耗尽、不支持类型,等)。不过,现有的 json_last_errorjson_last_error_msg 函数可用于确定验证错误。

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"

类似于 json_decodejson_encode 函数,json_validate 通过存储上次操作的错误码来修改应用程序状态,使之不是一个纯函数。

虽然这样对大部分 PHP 应用没有显著影响,但可能会出现一些边缘情况,包括在同一线程上服务多个并发请求的某些新 PHP 运行程序上的竞争条件。

用例

以下是 json_validate 的一些用例。他们不会检查在运行的 PHP 版本上是否有 json_validate 函数。

需要支持早于 PHP 8.3 的 PHP 版本的应用程序/包可能需要有条件地使用 json_validate 函数,或者在用户端 PHP 代码中多填充它。

验证给定的 JSON 字符串

json_validate($_GET['json']);

验证给定的 JSON 字符串并抛出异常

本例模拟了 PHP 7.3 中引入的 JSON_THROW_ON_ERROR flag。

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

使用新增的 json_validate 函数替换现有的 JSON 验证

- $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']); 

用户空间填充

json_validate() 函数的内部实现消费了更少的内存和处理,这是 json_validate 函数的主要优势。可以通过观察 json_decode 生成的错误码,在 PHP 用户空间中进行编码模拟填充,不过这种模拟在实际实现中并没有内存/处理上的改进。

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;  
  }  
}

以上代码在 PHP 8.0 之后的版本可用。如果 \ValueError 异常使用其他类型的异常替代,可以兼容至 PHP ≥ 7.3 。

向后兼容影响

json_validate() 是 PHP 8.3 上添加的新函数。现有的 PHP 应用在全局命名空间中声明 json_validate 函数会在 PHP 8.3 中造成重复声明错误。

该函数可以使用用户空间的 PHP 代码实现,尽管不会带来实际 json_validate 函数提供的内存/处理上的优势。