json_encode序列化空字节

时间:2011-07-18 09:45:13

标签: php json

我今天遇到了这个serialize gotcha 。来自PHP.net doc:

  

注意:   Object的私有成员具有该成员前面的类名   名称;受保护的成员在成员名称前面加上'*'。这些   前置值在任一侧都有空字节

我正在使用debug_backtrace为调试报告生成跟踪,获取json_encode d。在内部,它使用序列化器生成跟踪数据。

这是json_encode :( / p>)的(部分)输出

{"\u0000MyObject\u0000my_var":[]}

问题是json_decode无法解决这个问题,它会抱怨空字节。

所以json_encode愉快地写入空字节,json_decode无法解码。这对我来说似乎有点不稳定。我希望json_encode负责必要的转义,或者至少json_decode可以解析json_encode生成的任何内容,但似乎并非如此。

我想我有几个解决方案:

  • 从跟踪中删除空字节,我对反序列化不太感兴趣,我只想要一个字符串表示。
  • 从跟踪中一起删除私有变量。
  • 修复json_encode以使其不产生空字节
  • 修复json_decode以使其接受空字节

是否有人遇到此问题,您是如何解决此问题的?


示例:

<?php
class MyClass {
    public $mypublic = 1;
    private $myprivate = 2;
    public function myfunc() {
        return debug_backtrace();
    }
}
$c = new MyClass();
$json = json_encode(call_user_func_array(array($c, "myfunc"), new MyClass()));
echo $json;
echo json_decode($json); // <-- Fatal error: Cannot access property started with '\0' in test.php on line 12

解决方案

  

由于PHP 5.3 call_user_func_array会在第二次时发出警告   call_user_func_array的参数不是数组。在那之前,你必须自己检查一下。

3 个答案:

答案 0 :(得分:7)

(对不起,这可能会更好地作为评论,因为它没有完全回答你的问题 - 但它有点太长了)


我已经尝试用PHP 5.3和5.2来复制你描述的内容,这就是我得到的内容:

首先,让我们创建一个具有私有属性的类并实现它:

class A {
    public $pub = 10;
    private $priv = 20;
}

$a = new A();
var_dump($a);

哪个让我:

object(A)[1]
  public 'pub' => int 10
  private 'priv' => int 20


现在,如果我serialize()我的对象:

$serialized = serialize($a);
var_dump($serialized);

我明白了:

string 'O:1:"A":2:{s:3:"pub";i:10;s:7:"�A�priv";i:20;}' (length=46)

这几乎就是你描述的内容:null - private - 属性名称周围有json_encode()个字节。


让我们继续$jsoned = json_encode($serialized); var_dump($jsoned);

\u0000

这就像你说的那样,给了我一些带有string '"O:1:\"A\":2:{s:3:\"pub\";i:10;s:7:\"\u0000A\u0000priv\";i:20;}"' (length=64) 的字符串:

json_decode()


现在,如果我尝试$unjsoned = json_decode($jsoned); var_dump($unjsoned); 这个字符串:

string 'O:1:"A":2:{s:3:"pub";i:10;s:7:"�A�priv";i:20;}' (length=46)

这是我得到的:

unserialize()

<强> =&GT;空字节似乎没有丢失:它们是从JSON字符串中正确重新创建的。


并且,在那上面调用$unserialized = unserialize($unjsoned); var_dump($unserialized);

object(A)[2]
  public 'pub' => int 10
  private 'priv' => int 20

我找回了我的初始对象:

class A {
    private $priv;
    public function __construct() {
        $this->priv = new B();
    }
}

class B {
    private $b = 10;
}

所以,在序列化+编码和解码+反序列化时,我似乎没有重现你的问题......

我应该补充说,我无法在这两个方面找到任何关于此类错误的内容:

  • php的bug-tracker,
  • 和json扩展的SVN历史记录。



现在,如果我尝试使用更复杂的对象,使用包含私有成员的类,该成员本身就是一个包含私有属性的对象:

var_dump()


我得到完全相同的行为:一切正常 - 这是我得到的输出,当使用与以前完全相同的操作和object(A)[1] private 'priv' => object(B)[2] private 'b' => int 10 string 'O:1:"A":1:{s:7:"�A�priv";O:1:"B":1:{s:4:"�B�b";i:10;}}' (length=54) string '"O:1:\"A\":1:{s:7:\"\u0000A\u0000priv\";O:1:\"B\":1:{s:4:\"\u0000B\u0000b\";i:10;}}"' (length=84) string 'O:1:"A":1:{s:7:"�A�priv";O:1:"B":1:{s:4:"�B�b";i:10;}}' (length=54) object(A)[3] private 'priv' => object(B)[4] private 'b' => int 10 调用时:

var_dump( 
    unserialize( 
        json_decode('{"\u0000MyObject\u0000my_var":[]}')
    )
);

在这里,我也无法重现你描述的问题。



但是,如果我试试这个:

Fatal error: Cannot access property started with '\0'

我确实遇到了麻烦:

{{1}}

但是,考虑一下,如果我尝试将其解码为“我自己”,我真的不知道你是如何获得这样的JSON字符串......

你确定其他地方没有问题吗?就像编码过程一样?

答案 1 :(得分:-1)

你可以简单地使用正则表达式从JSON字符串中去掉\ u0000然后如果你解码它就应该没问题。

答案 2 :(得分:-1)

尝试将编码更改为utf8。我假设您从数据库中获取数据

mysql_query('SET CHARACTER SET utf8');

它应该有用。如果它赢了“T”,请在解码对象之前尝试转义空字符

preg_replace('|\\u0000|', ' ', $json);

如果它不起作用,试试这个

<?php
$json = '{"\u0000MyObject\u0000my_var":[]}';
$json = preg_replace('/\\\\u([0-9A-F]{4})/i', '', $json);
$json = json_decode($json);
print_r($json);

http://sandbox.phpcode.eu/g/684be.php