json_encode serializar bytes nulos

Me encontré con esta serialize gotcha hoy. De PHP.net doc:

Nota: los miembros privados del objeto tienen el nombre de clase antepuesto al nombre del miembro; los miembros protegidos tienen un ‘*’ antepuesto al nombre del miembro. Estos valores antepuestos tienen bytes nulos en cualquier lado .

Estoy utilizando debug_backtrace para generar un seguimiento para un informe de depuración, que obtiene json_encode d. Internamente usa el serializador para generar los datos para el rastreo.

Esta es la salida (parcial) de json_encode :

 {"\u0000MyObject\u0000my_var":[]} 

El problema es que json_decode no puede manejar esto, se quejará de los bytes nulos.

Entonces json_encode escribe felizmente los bytes nulos, que json_decode no puede decodificar. Esto me parece un poco retorcido. Esperaría que json_encode se encargara del escape necesario, o al menos que json_decode pueda analizar cualquier cosa producida por json_encode , pero este no parece ser el caso.

Creo que tengo varias soluciones:

  • Elimine los bytes nulos de la traza, no estoy demasiado interesado en la desesterialización del objeto, solo quiero una representación de cadena.
  • Pele todas las variables privadas desde la traza.
  • Repara json_encode para que no produzca bytes nulos
  • Repara json_decode para que acepte bytes nulos

¿Alguien se encontró con este problema y cómo lo solucionó?


Muestra:

 <?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 

Solución

Desde PHP 5.3 call_user_func_array arrojará una advertencia cuando el segundo parámetro de call_user_func_array no sea una matriz. Hasta entonces, tendrás que comprobarlo tú mismo.

(Lo siento, esto podría ser mejor como comentario, ya que no responde exactamente a su pregunta, pero es demasiado tiempo para eso)

Intenté reproducir lo que describes, con PHP 5.3 y 5.2, y esto es lo que obtengo:

Primero, creemos una clase con una propiedad privada y sométala:

 class A { public $pub = 10; private $priv = 20; } $a = new A(); var_dump($a); 

Lo que me atrapa:

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

Ahora, si serialize() mi objeto:

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

Yo obtengo :

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

Que es más o menos lo que describes: hay esos bytes null alrededor del nombre de la propiedad private .

Y continuemos con json_encode() :

 $jsoned = json_encode($serialized); var_dump($jsoned); 

Lo que me da, como dijiste, una cadena con algunos \u0000 :

 string '"O:1:\"A\":2:{s:3:\"pub\";i:10;s:7:\"\u0000A\u0000priv\";i:20;}"' (length=64) 

Ahora, si trato de json_decode() esta cadena:

 $unjsoned = json_decode($jsoned); var_dump($unjsoned); 

Esto es lo que obtengo:

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

=> Los bytes nulos no parecen estar perdidos: se recrean correctamente a partir de la cadena JSON.

Y, llamando a unserialize() sobre eso:

 $unserialized = unserialize($unjsoned); var_dump($unserialized); 

Recupero el objeto inicial que tuve:

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

Por lo tanto, parece que no reproduzco tu problema al serializar + codificar y descodificar + deserializar …

Debo añadir que no he podido encontrar nada sobre ese error, en ambos:

  • php’s bug-tracker,
  • y el historial SVN de la extensión json.


Ahora, si bash con un objeto más complejo, con una clase que contiene un miembro privado, que a su vez es un objeto que contiene una propiedad privada:

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

Obtengo exactamente el mismo tipo de comportamiento: todo funciona bien, y aquí está la salida que obtengo, cuando uso exactamente las mismas acciones y llamadas var_dump() que antes:

 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 

Aquí también, no puedo reproducir el problema que describes.


Aún así, si pruebo esto:

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

De hecho me meto en problemas:

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

Pero, pensando en ello, si trato de decodificarlo “a mí mismo”, realmente no veo cómo hubieras obtenido una cadena JSON …

¿Estás seguro de que no hay un problema en otro lado? Al igual que en el proceso de encoding?

intente cambiar la encoding a utf8. Supongo que está tomando datos de la base de datos

 mysql_query('SET CHARACTER SET utf8'); 

deberia de funcionar. Si no es así, intenta escapar de los caracteres nulos antes de decodificar tu objeto

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

si no funciona, intente esto

 < ?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

Simplemente podría usar una expresión regular para quitar \ u0000 de la cadena JSON y, si la decodifica, debería estar bien.