在 PHP json_decode() 中检测错误的 json 数据?

Posted

技术标签:

【中文标题】在 PHP json_decode() 中检测错误的 json 数据?【英文标题】:Detect bad json data in PHP json_decode()? 【发布时间】:2011-01-21 19:59:43 【问题描述】:

当通过 json_decode() 解析时,我正在尝试处理错误的 json 数据。我正在使用以下脚本:

if(!json_decode($_POST)) 
  echo "bad json data!";
  exit;

如果 $_POST 等于:

' bar: "baz" '

然后 json_decode 处理错误并吐出“错误的 json 数据!”; 但是,如果我将 $_POST 设置为“无效数据”之类的东西,它会给我:

Warning: json_decode() expects parameter 1 to be string, array given in C:\server\www\myserver.dev\public_html\rivrUI\public_home\index.php  on line 6
bad json data!

我是否需要编写自定义脚本来检测有效的 json 数据,还是有其他一些不错的方法来检测?

【问题讨论】:

$_POST 始终是一个数组,其中包含通过 POST 传递的 x-www-form-urlencoded 参数。如何将数据发送到 PHP 脚本? PHP 中包含的 json 函数帮助不大。他们有很多问题。看看json.org 找到一个好的库。 【参考方案1】:

以下是关于json_decode 的几件事:

返回数据,出错时返回null 在没有错误的情况下也可以返回null:当JSON字符串包含null 它会在有警告的地方发出警告 - 您要使警告消失。

要解决警告问题,一个解决方案是使用@ operator (我不经常推荐使用它,因为它会使调试变得更加困难......但是在这里,没有太多选择) :

$_POST = array(
    'bad data'
);
$data = @json_decode($_POST);

然后您必须测试$data 是否为null——并且,为了避免json_decode 在JSON 字符串中为null 返回null 的情况,您可以检查json_last_error ,其中(引用)

返回最后一个错误(如果有) 由上次 JSON 解析发生。

这意味着您必须使用如下代码:

if ($data === null
    && json_last_error() !== JSON_ERROR_NONE) 
    echo "incorrect data";

【讨论】:

正如 Harry Bosch 在 another answer 中指出的那样,您只需检查 JSON_ERROR_NONE !== json_last_error()【参考方案2】:

您也可以使用 json_last_error :http://php.net/manual/en/function.json-last-error.php

正如文档所说:

返回上一个 JSON 期间发生的最后一个错误(如果有) 编码/解码。

这是一个例子

json_decode($string);

switch (json_last_error()) 
    case JSON_ERROR_NONE:
        echo ' - No errors';
    break;
    case JSON_ERROR_DEPTH:
        echo ' - Maximum stack depth exceeded';
    break;
    case JSON_ERROR_STATE_MISMATCH:
        echo ' - Underflow or the modes mismatch';
    break;
    case JSON_ERROR_CTRL_CHAR:
        echo ' - Unexpected control character found';
    break;
    case JSON_ERROR_SYNTAX:
        echo ' - Syntax error, malformed JSON';
    break;
    case JSON_ERROR_UTF8:
        echo ' - Malformed UTF-8 characters, possibly incorrectly encoded';
    break;
    default:
        echo ' - Unknown error';
    break;

【讨论】:

这在 PHP 5.5 中是完全没有必要的php.net/manual/en/function.json-last-error-msg.php 你可以用 json_last_error_msg 做这样的事情:if (JSON_ERROR_NONE !== json_last_error()) throw new Exception(json_last_error_msg());【参考方案3】:

自 PHP 7.3 起,json_decode 函数将接受一个新的 JSON_THROW_ON_ERROR 选项,让 json_decode 抛出异常,而不是在出错时返回 null。


示例:

try   
  json_decode("", false, 512, JSON_THROW_ON_ERROR);  
  
catch (\JsonException $exception)   
  echo $exception->getMessage(); // displays "Syntax error"  

【讨论】:

这应该是公认的答案。 JSON_THROW_ON_ERROR constant 我收到了一个异常:"Warning: Use of undefined constant JSON_THROW_ON_ERROR - assumed 'JSON_THROW_ON_ERROR' (this will throw an Error in a future version of PHP) in <file_path> @domdambrogia JSON_THROW_ON_ERROR 常量仅适用于 PHP 7.3 在 7.3.24 上不起作用,错误在那里但无法捕捉到【参考方案4】:

我刚刚发现一个 json 语法错误,似乎是完美的 json:"test1":"car", "test2":"auto" 来自一个 url 编码的字符串。

但在我的情况下,上面的一些是 html 编码的,因为添加 html_entity_decode($string) 就可以了。

$ft = json_decode(html_entity_decode(urldecode(filter_input(INPUT_GET, 'ft', FILTER_SANITIZE_STRING))));

希望这会为其他人节省一些时间。

【讨论】:

我花了 3 天时间找到我的问题,直到我在这里读到了 html_entity_decode 提示。非常感谢:) 如果我没有找到这个答案,我永远不会解决我的问题。我会给我的第一个孩子起名:“Klompenrunner”!【参考方案5】:

这就是 Guzzle 处理 json 的方式

    /**
 * Parse the JSON response body and return an array
 *
 * @return array|string|int|bool|float
 * @throws RuntimeException if the response body is not in JSON format
 */
public function json()

    $data = json_decode((string) $this->body, true);
    if (JSON_ERROR_NONE !== json_last_error()) 
        throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error());
    

    return $data === null ? array() : $data;

【讨论】:

【参考方案6】:

我只花了一个小时浏览了此页面上所有可能的解决方案。我冒昧地将所有这些可能的解决方案集合到一个函数中,以使其更快、更容易尝试和调试。

我希望它可以对其他人有用。

<?php
/**
 * Decontaminate text
 *
 * Primary sources:
 *  - https://***.com/questions/17219916/json-decode-returns-json-error-syntax-but-online-formatter-says-the-json-is-ok
 *  - https://***.com/questions/2348152/detect-bad-json-data-in-php-json-decode
 */
function decontaminate_text(
  $text,
  $remove_tags = true,
  $remove_line_breaks = true,
  $remove_BOM = true,
  $ensure_utf8_encoding = true,
  $ensure_quotes_are_properly_displayed = true,
  $decode_html_entities = true
)

  if ( '' != $text && is_string( $text ) ) 
    $text = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $text );
    $text = str_replace(']]>', ']]&gt;', $text);

    if( $remove_tags )
      // Which tags to allow (none!)
      // $text = strip_tags($text, '<p>,<strong>,<span>,<a>');
      $text = strip_tags($text, '');
    

    if( $remove_line_breaks )
      $text = preg_replace('/[\r\n\t ]+/', ' ', $text);
      $text = trim( $text );
    

    if( $remove_BOM )
      // Source: https://***.com/a/31594983/1766219
      if( 0 === strpos( bin2hex( $text ), 'efbbbf' ) )
        $text = substr( $text, 3 );
      
    

    if( $ensure_utf8_encoding )

      // Check if UTF8-encoding
      if( utf8_encode( utf8_decode( $text ) ) != $text )
        $text = mb_convert_encoding( $text, 'utf-8', 'utf-8' );
      
    

    if( $ensure_quotes_are_properly_displayed )
      $text = str_replace('&quot;', '"', $text);
    

    if( $decode_html_entities )
      $text = html_entity_decode( $text );
    

    /**
     * Other things to try
     * - the chr-function: https://***.com/a/20845642/1766219
     * - stripslashes (THIS ONE BROKE MY JSON DECODING, AFTER IT STARTED WORKING, THOUGH): https://***.com/a/28540745/1766219
     * - This (improved?) JSON-decoder didn't help me, but it sure looks fancy: https://***.com/a/43694325/1766219
     */

  
  return $text;


// Example use
$text = decontaminate_text( $text );

// $text = decontaminate_text( $text, false ); // Debug attempt 1
// $text = decontaminate_text( $text, false, false ); // Debug attempt 2
// $text = decontaminate_text( $text, false, false, false ); // Debug attempt 3

$decoded_text = json_decode( $text, true );
echo json_last_error_msg() . ' - ' . json_last_error();
?>

我会在这里维护它:https://github.com/zethodderskov/decontaminate-text-in-php/blob/master/decontaminate-text-preparing-it-for-json-decode.php

【讨论】:

【参考方案7】:
/**
 * 
 * custom json_decode 
 * handle json_decode errors
 * 
 * @param type $json_text
 * @return type
 */
public static function custom_json_decode($json_text) 

    $decoded_array = json_decode($json_text, TRUE);
    switch (json_last_error()) 
        case JSON_ERROR_NONE:
            return array(
                "status" => 0,
                "value" => $decoded_array
            );


        case JSON_ERROR_DEPTH:
            return array(
                "status" => 1,
                "value" => 'Maximum stack depth exceeded'
            );

        case JSON_ERROR_STATE_MISMATCH:
            return array(
                "status" => 1,
                "value" => 'Underflow or the modes mismatch'
            );

        case JSON_ERROR_CTRL_CHAR:
            return array(
                "status" => 1,
                "value" => 'Unexpected control character found'
            );

        case JSON_ERROR_SYNTAX:
            return array(
                "status" => 1,
                "value" => 'Syntax error, malformed JSON'
            );

        case JSON_ERROR_UTF8:
            return array(
                "status" => 1,
                "value" => 'Malformed UTF-8 characters, possibly incorrectly encoded'
            );

        default:
            return array(
                "status" => 1,
                "value" => 'Unknown error'
            );
    

【讨论】:

以上是关于在 PHP json_decode() 中检测错误的 json 数据?的主要内容,如果未能解决你的问题,请参考以下文章

json_decode() (PHP 7) 中的新行和标签

PHP 致命错误:调用 Cpanel 上未定义的函数 json_decode()

如何解决 php json_decode 中的 JSON_ERROR_UTF8 错误?

如何在 PHP 中解决“调用未定义函数 Composer\Json\json_decode()”?

未捕获的错误:调用未定义的函数 Composer\Console\json_decode()

php中json_decode()和json_encode()的使用方法