PHP: serialize or json_encode?

PHP: ¿serialize o json_encode?

Apr 17 2009

Since PHP 5.2, there are a couple of new functions called [json_encode](http://php.net/json_encode) and [json_decode](http://php.net/json_encode) that allows to serialize variables with their content in PHP. The implementation is written in C, and thus, its performance is pretty good. JSON (JavaScript Object Notation) is a format for date interchange. And it happens that the generated JSON string is always a valid JavaScript expression. Nowadays it is a pretty common format already and it is supported officially in a lot of programming languages. Until now, the most efficient and convenient way to serialize values was: [serialize](http://php.net/serialize)/[unserialize](http://php.net/unserialize). And starting with PHP 5.2, we can ask ourselves about the new json_* functions: If they are more compatible with other languages, shorter and more flexible, it could substitute the PHP original serialization mechanism? The answer: not always. json_decode could be much slower than unserialize in a lot of cases. While `serialize` stores the lengths of the strings, `json_encode` has to iterate them to know its length. Furthermore the `json_encode` input has to be UTF-8 and the parsing is much slower. I have made a small program to test the performance. Regarding to the size of the generated string, JSON is always smaller. JSON serializes faster for arrays and numbers. But for strings, the performance is worse. ```php function & generate_random_var( $maxdepth = 5, $base = 10, $strlen = 999 ) { $r = array(); for ($n = 0, $l = mt_rand($base / 2, $base); $n < $l; $n++) { switch (mt_rand(0, 4)) { case 0: $r[] = mt_rand(); break; case 1: $r[] = str_repeat( '*', $strlen ); break; case 2: $r[] = false; break; case 3: $r[] = (object)array(1 => 2); break; case 4: if ( $maxdepth > 0 ) $r[] = generate_random_var( mt_rand( 0, $maxdepth - 1), $base, $strlen ); break; } } return $r; } mt_srand( 0 ); foreach (array( array(4, 30, 9), array(10, 30, 1), array(0, 8000, 1), array(0, 8000, 99), array(0, 8000, 999), array(4, 30, 99), array(4, 30, 999), ) as $config) { list($maxdepth, $base, $strlen) = $config; printf("config(depth=%d, elements=%d, strlen=%d):\n", $maxdepth, $base, $strlen); $v = generate_random_var($maxdepth, $base, $strlen); $t = microtime(true); for ($n = 0; $n < 60; $n++) { $s = & serialize($v); unserialize($s); } printf(" serialize : time(%.3f), length(%d)\n", $t_serialize = microtime(true) - $t, $l_serialize = strlen($s)); $t = microtime(true); for ($n = 0; $n < 60; $n++) { $s = & json_encode($v); json_decode($s); } printf(" json : time(%.3f), length(%d)\n", $t_json = microtime(true) - $t, $l_json = strlen($s)); printf(" diff: time(%d%%) | strlen(%d%%)\n", $t_json * 100 / $t_serialize, $l_json * 100 / $l_serialize); printf("\n"); } ``` Results: ``` config(depth=4, elements=30, strlen=9): serialize : time(0.236), length(42431) json : time(0.171), length(19639) diff: time(72%) | strlen(46%) config(depth=10, elements=30, strlen=1): serialize : time(0.767), length(116820) json : time(0.502), length(47610) diff: time(65%) | strlen(40%) config(depth=0, elements=8000, strlen=1): serialize : time(0.445), length(71185) json : time(0.277), length(25943) diff: time(62%) | strlen(36%) config(depth=0, elements=8000, strlen=99): serialize : time(0.784), length(289298) json : time(0.962), length(206929) diff: time(122%) | strlen(71%) config(depth=0, elements=8000, strlen=999): serialize : time(1.371), length(1665092) json : time(5.630), length(1581304) diff: time(410%) | strlen(94%) config(depth=4, elements=30, strlen=99): serialize : time(0.221), length(73598) json : time(0.265), length(54542) diff: time(119%) | strlen(74%) config(depth=4, elements=30, strlen=999): serialize : time(0.018), length(30238) json : time(0.095), length(28817) diff: time(521%) | strlen(95%) ``` Conclusion: * For data that will be accessed only by PHP, in most cases, `serialize` should be the preferred option. Specially with variables that contain long strings. * If it is required to access the data from another languages, or to modify them manually, JSON is a great option. For complex objects, JSON will be more compact. But as said, when containing long strings, there will be a performance hit.
A partir de PHP 5.2, existen dos nuevas funciones llamadas [json_encode](http://php.net/json_encode) y [json_decode](http://php.net/json_decode) que permiten serializar variables con contenidos variables en php. La implementación está hecha en C, así que su rendimiento es bastante conveniente. JSON (JavaScript Object Notation) es un formato de intercambio de datos. Y tiene la peculiaridad de que la cadena generada es siempre una expresión válida en javascript. Además la cadena resultante es muy compacta, y se puede modificar con facilidad. En la actualidad ya es un formato ampliamente usado y soportado de manera oficial en muchos lenguajes de programación. La forma más eficiente y cómoda que había hasta la fecha para serializar variables, era la opción: [serialize](http://php.net/serialize)/[unserialize](http://php.net/unserialize). A partir de PHP 5.2, con la nueva alternativa de json_*, está la duda: ¿siendo más compatible con otros lenguajes y además compacta y flexible, podría sustituir a la original? La respuesta es: no siempre. json_decode puede ser mucho más lento que unserialize en muchos casos. Mientras que serialize almacena las longitudes de las cadenas, json_encode tiene que recorrerlas para conocer su longitud. Además la entrada de json_encode tiene que ser utf-8 y el parseo es bastante más lento. He hecho un pequeño programa para probar el rendimiento. El tamaño de la cadena generada por json siempre es más pequeño. El json se codifica mas rápido para arrays y números. Pero para cadenas, el rendimiento empeora mucho. ```php function & generate_random_var( $maxdepth = 5, $base = 10, $strlen = 999 ) { $r = array(); for ($n = 0, $l = mt_rand($base / 2, $base); $n < $l; $n++) { switch (mt_rand(0, 4)) { case 0: $r[] = mt_rand(); break; case 1: $r[] = str_repeat( '*', $strlen ); break; case 2: $r[] = false; break; case 3: $r[] = (object)array(1 => 2); break; case 4: if ( $maxdepth > 0 ) $r[] = generate_random_var( mt_rand( 0, $maxdepth - 1), $base, $strlen ); break; } } return $r; } mt_srand( 0 ); foreach (array( array(4, 30, 9), array(10, 30, 1), array(0, 8000, 1), array(0, 8000, 99), array(0, 8000, 999), array(4, 30, 99), array(4, 30, 999), ) as $config) { list($maxdepth, $base, $strlen) = $config; printf("config(depth=%d, elements=%d, strlen=%d):\n", $maxdepth, $base, $strlen); $v = generate_random_var($maxdepth, $base, $strlen); $t = microtime(true); for ($n = 0; $n < 60; $n++) { $s = & serialize($v); unserialize($s); } printf(" serialize : time(%.3f), length(%d)\n", $t_serialize = microtime(true) - $t, $l_serialize = strlen($s)); $t = microtime(true); for ($n = 0; $n < 60; $n++) { $s = & json_encode($v); json_decode($s); } printf(" json : time(%.3f), length(%d)\n", $t_json = microtime(true) - $t, $l_json = strlen($s)); printf(" diff: time(%d%%) | strlen(%d%%)\n", $t_json * 100 / $t_serialize, $l_json * 100 / $l_serialize); printf("\n"); } ``` Resultados: ``` config(depth=4, elements=30, strlen=9): serialize : time(0.236), length(42431) json : time(0.171), length(19639) diff: time(72%) | strlen(46%) config(depth=10, elements=30, strlen=1): serialize : time(0.767), length(116820) json : time(0.502), length(47610) diff: time(65%) | strlen(40%) config(depth=0, elements=8000, strlen=1): serialize : time(0.445), length(71185) json : time(0.277), length(25943) diff: time(62%) | strlen(36%) config(depth=0, elements=8000, strlen=99): serialize : time(0.784), length(289298) json : time(0.962), length(206929) diff: time(122%) | strlen(71%) config(depth=0, elements=8000, strlen=999): serialize : time(1.371), length(1665092) json : time(5.630), length(1581304) diff: time(410%) | strlen(94%) config(depth=4, elements=30, strlen=99): serialize : time(0.221), length(73598) json : time(0.265), length(54542) diff: time(119%) | strlen(74%) config(depth=4, elements=30, strlen=999): serialize : time(0.018), length(30238) json : time(0.095), length(28817) diff: time(521%) | strlen(95%) ``` Conclusión: * Para datos que se deben serializar pero que únicamente van a ser accedidos por php, casi siempre será mejor usar la opción de serialize. Especialmente con variables que contendrán cadenas muy largas. * Si se requiere acceder a esos datos desde otros lenguajes o modificarlos manualmente, json es una buena opción. Si se van a serializar objetos complejos, json será siempre más compacto. Si la variable contendrá una cadena, o indirectamente alguna cadena, como elemento o clave de un array y son cadenas muy largas, json está penalizado.