有关PHP使用jeson_decode()转数组失败,json_last_error() 报4

摘要: php通过接口获取到json数据用json_decode($jsonStr,true)转数组失败 ,用json_last_error_msg()获取到错误信息:Syntax error 首先判断是不是json数据格式错误,可以在线json格式化http://json.cn/看看有无错误 如果没有错误,那么原因估计是,接口提供方那边输出了bom头造成或者报文body里有回车符/换行符造成的。有关bom头的处理,大家可以自行百度。回车符/换行符的处理:$post = str_replace(array("\r\n", "\r", "\n"), "", $post);//$post是待转为数组的json字符串。

最近在接第三方支付公司的一个支付通道,其他过程比较顺利。最后在接通道发送的交易通知的接口时发生了一些问题。期间为解决办法,网上也扒了很多帖子,但都没有解决问题,故而记录一下,以便为遇到类似问题的小伙伴抛砖引玉。
关于交易通知的接口文档,通道(第三方支付公司)写的比较简单,截图如下:
# 欢迎使用Markdown编辑器
OK,干就是了,代码如下:

1
2
3
4
5
6
7
            $post = file_get_contents("php://input");//接受通道参数
            $this->logger->info('post=>'.$post);//打印日志,记录接受参数
            $postResult = json_decode($post,true);//转数组
            //如果转失败就报错
            if(empty($postResult)){
                return $this->notifyResponse("FAIL",'无效报文');
            }

之所以用file_get_contents(“php://input”)接收参数,是因为我接收的是同事写一半的代码(他已奔向他的星辰大海),我用的tp5.0的框架,测试用tp的$request->param()是收不到通道的参数的,后来问了下通道,说是采用的Content-type: application/json 形式的报文,具体的各种接收方式,大家可以自行百度。OK,继续调接口,日志如下:

1
2
[2020-05-12 08:56:37] paynotify.INFO: post=>{"data":"TeqD8BXaWyzWJj3+bahWIffqG09r+RgEPiD74ck2CNsYxwMC9GtOnVChGN1i8ClRNlFBYlAq6o+w +wu4+cZe2dXHTVyad0mEEd8P5pknfy0Cf9hrPz"}  {"logKey":"5eb9f445bda1a"}
[2020-05-12 08:56:37] paynotify.INFO: 返回通道信息 {"retCode":"FAIL","retMessage":"无效报文"} {"logKey":"5eb9f445bda1a"}

能接收到通道的请求报文,但是就是转数组失败。先确认自己的代码有没有问题,

1
2
3
4
5
6
7
8
//$post = file_get_contents("php://input");//接受通道参数
//$this->logger->info('post=>'.$post);//打印日志,记录接受参数
$post = '{"data":"TeqD8BXaWyzWJj3+bahWIffqG09r+RgEPiD74ck2CNsYxwMC9GtOnVChGN1i8ClRNlFBYlAq6o+w +wu4+cZe2dXHTVyad0mEEd8P5pknfy0Cf9hrPz"}';
$postResult = json_decode($post,true);//转数组
//如果转失败就报错
if(empty($postResult)){
    return $this->notifyResponse("FAIL",'无效报文');
}

发现是OK的,可以解密。这就怪了,明明报文可以转成数组,为什么通道的请求过来时就不行呢?OK,可以用json_last_error()(返回int值)或者json_last_error_msg()(返回字符串)函数获取报错:

1
2
3
4
5
6
$postResult = json_decode($post,true);
$jsonError = json_last_error_msg();
$this->logger->info('json_error=>'.$jsonError);
if(empty($postResult)){
    return $this->notifyResponse("FAIL",'无效报文');
}

日志:

1
2
3
[2020-05-12 09:55:40] paynotify.INFO: post=>{"data":"TeqD8BXaWyzWJj3+bahWIffqG09r+RgEPiD74ck2CNsYxwMC9GtOnVChGN1i8ClRNlFBYlAq6o+w +wu4+cZe2dXHTVyad0mEEd8P5pknfy0Cf9hrPz"}  {"logKey":"5eba021ce1fde"}
[2020-05-12 09:55:40] paynotify.INFO: json_error=>Syntax error  {"logKey":"5eba021ce1fde"}
[2020-05-12 09:55:40] paynotify.INFO: 返回通道信息 {"retCode":"FAIL","retMessage":"无效报文"} {"logKey":"5eba021ce1fde"}

那就查导致报4或者Syntax error的可能原因。网上查了很多相关这个报错的处理办法,都没有解决我的问题。然后自己模拟以json报文的方式请求自己也OK,因为通道用的是java,就把通道请求过来的加密报文给公司的java同事,让他们以json方式请求我的接口,发现也没问题。那问题就在通道那里,联系通道,通道说是不是因为报文的body里有回车符和换行符导致?好那就去除回车符、换行符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$post = file_get_contents("php://input");
$this->logger->info('post=>'.$post);
//$post = '{"data":"TeqD8BXaWyzWJj3+bahWIffqG09r+RgEPiD74ck2CNsYxwMC9GtOnVChGN1i8ClRNlFBYlAq6o+w +wu4+cZe2dXHTVyad0mEEd8P5pknfy0Cf9hrPz"}';
//$postArr = explode('"',$post);//这是中间想到的一个方法:报文的结构是固定的,可以用explode()函数把报文内容截出来~!~
//$this->logger->info('postArr',$postArr);
$post = str_replace(array("\r\n", "\r", "\n"), "", $post);
$postResult = json_decode($post,true);
$jsonError = json_last_error_msg();
$this->logger->info('json_error=>'.$jsonError);
if(empty($postResult)){
    return $this->notifyResponse("FAIL",'无效报文');
}else{
    $this->logger->info('postResult=>',$postResult);
}

日志:

1
2
3
[2020-05-12 10:47:18] paynotify.INFO: post=>{"data":"TeqD8BXaWyzWJj3+bahWIffqG09r+RgEPiD74ck2CNsYxwMC9GtOnVChGN1i8ClRNlFBYlAq6o+w +wu4+cZe2dXHTVyad0mEEd8P5pknfy0Cf9hrPz"}  {"logKey":"5eba0e36322d5"}
[2020-05-12 10:47:18] paynotify.INFO: json_error=>No error  {"logKey":"5eba0e36322d5"}
[2020-05-12 10:47:18] paynotify.INFO: postResult=> {"data":"TeqD8BXaWyzWJj3+bahWIffqG09r+RgEPiD74ck2CNsYxwMC9GtOnVChGN1i8ClRNlFBYlAq6o+w +wu4+cZe2dXHTVyad0mEEd8P5pknfy0Cf9hrPz"} {"logKey":"5eba0e36322d5"}

至此,问题解决。其实想想一开始,有想过可能是报文中一些符号的问题,但是当时只是用trim()去一下报文两边的空格,发现不行,就没再考虑回车符、换行符这边,后面就走偏了(T_T),外加跟公司java同事的通信一直很正常,就有点太相信通道的接口。