PHP es un lenguaje del lado del servidor en donde se puede crear una API que se comunique a través de JSON, tanto para leer peticiones como para responder. Hoy veremos cómo codificar y decodificar JSON en PHP con funciones nativas.
Por defecto, PHP tiene algunos errores al codificar en JSON, ya que a veces algunos números no son codificados como números, sino como cadenas; por eso veremos cómo evitar eso y personalizar la salida gracias a las constantes JSON de PHP.
También veremos cómo manejar los errores; limitar la profundidad de los objetos y otras cosas muy importantes al trabajar con JSON en el lenguaje PHP.
Con PHP y JSON podemos codificar cualquier tipo de variable; no importa si es un booleano, entero, cadena o flotante; todo se codifica correctamente. Lo mismo es para los arreglos u objetos; u objetos con arreglos en su interior.
Requisitos y/o recomendaciones
Si deseas probar los ejemplos, primero instala XAMPP. Después puedes ejecutar los archivos desde la terminal, o probarlos en línea.
¿Quieres codificar y decodificar JSON en JavaScript? mira este post.
Convertir valores de PHP a su representación JSON
Para convertir una variable de PHP a su representación en JSON se usa la función json_encode
. Esta función regresa una cadena (o false en caso de error), y recibe 3 argumentos; los últimos dos opcionales.
El primer argumento es la variable que va a codificar. El segundo argumento es una mascara de bits con algunas constantes predefinidas que veremos más abajo. El tercer argumento es la profundidad; si tenemos un objeto con más objetos en su interior, podemos especificar que no se codifique todo, sino hasta un cierto nivel de profundidad.
Aquí hay algunos ejemplos de cómo se codifica JSON en PHP. Más abajo veremos al segundo y tercer argumento en acción.
<?php
$nombre = "Luis";
echo json_encode($nombre) . "\n";
// "Luis"
$edad = 10;
echo json_encode($edad) . "\n";
// 10
$arreglo = [1, 2, 3];
echo json_encode($arreglo) . "\n";
// [1,2,3]
$otroArreglo = ["Hola", "Mundo", "Soy", "Un", "Arreglo"];
echo json_encode($otroArreglo) . "\n";
// ["Hola","Mundo","Soy","Un","Arreglo"]
$objeto = new StdClass;
$objeto->unaPropiedad = 1;
$objeto->otraPropiedad = "Hola";
$objeto->unArreglo = [1, 2, 3];
echo json_encode($objeto) . "\n";
// {"unaPropiedad":1,"otraPropiedad":"Hola","unArreglo":[1,2,3]}
$otroObjeto = [
"nombre" => "Luis",
"sitio" => "parzibyte.me",
"edad" => 2,
];
echo json_encode($otroObjeto) . "\n";
// {"nombre":"Luis","sitio":"parzibyte.me","edad":2}
Ahora sí veamos más ejemplos.
El segundo argumento de json_encode: constantes para modificar el comportamiento
Múltiples constantes se pueden usar en combinación, usando el operador |
. La lista completa de las constantes que podemos usar está aquí.
Lo que mostraré será cómo forzar que la salida sea numérica y no como cadena.
Para ello se utiliza la constante JSON_NUMERIC_CHECK
al llamara a json_encode
. Aquí un ejemplo:
<?php
$numero = 123.20;
// A veces esto sale como cadena "123.20":
echo json_encode($numero);
# Por eso lo forzamos a que sea número
echo json_encode($numero, JSON_NUMERIC_CHECK);
Hay otras constantes útiles, por ejemplo, JSON_PRETTY_PRINT
agrega espacios para que el JSON se muestre con las tabulaciones y espacios que lo hacen legible; útil si estamos imprimiendo directamente el JSON generado.
Otra constante útil es JSON_PRESERVE_ZERO_FRACTION
, que mantiene el .0 en un número flotante y obliga a que se especifique que dicho número es flotante; por ejemplo, si tenemos el número 15.0 y lo codificamos, se convierte en 15; pero si se pone la constante, ahora sí se imprime bien. Veamos el ejemplo:
<?php
$flotante = 15.0;
echo json_encode($flotante) . "\n";
// 15
# Forzamos a que mantenga el .0
echo json_encode($flotante, JSON_PRESERVE_ZERO_FRACTION) . "\n";
// 15.0
De ahí las demás constantes pueden servirnos dependiendo de nuestras necesidades. Por cierto, podemos combinarlas usando |
. Si quiero que se imprima de forma legible (JSON_PRETTY_PRINT
) y que mantenga el cero en los flotantes (JSON_PRESERVE_ZERO_FRACTION
), hago esto:
<?php
// Un objeto con flotantes para ver las tabulaciones y obligar a mantener el cero
$objeto = [
"idCuenta" => "123ASD",
"cliente" => "parzibyte",
"saldo" => 250.0,
"movimientos" => [
[
"tipo" => "salida",
"monto" => 125.0,
"fecha" => "2018-12-25"
],
[
"tipo" => "salida",
"monto" => 666.55,
"fecha" => "2018-12-26"
]
]
];
echo json_encode($objeto, JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION);
/*
{
"idCuenta": "123ASD",
"cliente": "parzibyte",
"saldo": 250.0,
"movimientos": [
{
"tipo": "salida",
"monto": 125.0,
"fecha": "2018-12-25"
},
{
"tipo": "salida",
"monto": 666.55,
"fecha": "2018-12-26"
}
]
}
*/
Así podemos combinar todas las constantes predefinidas; eso fue un ejemplo pero podemos combinar cualquiera.
El tercer argumento de json_encode: la profundidad máxima
Si especificamos la profundidad máxima, al codificar un objeto JSON en PHP se regresará false si se excede la misma. Aquí un ejemplo:
<?php
$objeto = [
// Nivel 1
"idCuenta" => "123ASD",
"cliente" => "parzibyte",
"saldo" => 250.0,
"movimientos" => [
// Nivel 2
[
"tipo" => "salida",
"monto" => 125.0,
"fecha" => "2018-12-25",
"involucrados" => [
// Nivel 3
"nombre" => "Un nombre",
"idCuenta" => "456465"
]
],
[
"tipo" => "salida",
"monto" => 666.55,
"fecha" => "2018-12-26"
]
]
];
// Con profundidad 4 todo va bien
echo "Con profundidad 4:" . json_encode($objeto, 0, 4) . "\n";
# Con profundidad 4:{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}
// Pero si lo ponemos a 3, json_encode devuelve FALSE y no se imprime nada
echo "Con profundidad de 3:" . json_encode($objeto, 0, 3) . "\n";
#
Estos límites sirven para cuando no sabemos qué tan profundo es un objeto y queremos protegernos de alguna manera. Entre más niveles de profundidad, más tiempo se tarda en codificar; por eso es que existe esta opción.
Si queremos manejar el error, podemos usar una función que está más abajo.
Manejar errores al codificar JSON en PHP
Si de alguna manera json_encode
devuelve false
, debemos ver qué salió mal. Para ello, PHP proporciona igualmente algunas constantes de error. Me he dado a la tarea de crear una función que nos permite saber qué cosa está mal.
Recuerda que los errores no deben imprimirse, en cambio deberían guardarse en un log.
<?php
// La función que nos dice el error
function obtenerErrorDeJSON()
{
switch (json_last_error()) {
case JSON_ERROR_NONE:
return "No ha ocurrido ningún error";
case JSON_ERROR_DEPTH:
return "Se ha excedido la profundidad máxima de la pila.";
case JSON_ERROR_STATE_MISMATCH:
return "Error por desbordamiento de buffer o los modos no coinciden";
case JSON_ERROR_CTRL_CHAR:
return "Error del carácter de control, posiblemente se ha codificado de forma incorrecta.";
case JSON_ERROR_SYNTAX:
return "Error de sintaxis.";
case JSON_ERROR_UTF8:
return "Caracteres UTF-8 mal formados, posiblemente codificados incorrectamente.";
case JSON_ERROR_RECURSION:
return "El objeto o array pasado a json_encode() incluye referencias recursivas y no se puede codificar.";
case JSON_ERROR_INF_OR_NAN:
return "El valor pasado a json_encode() incluye NAN (Not A Number) o INF (infinito)";
case JSON_ERROR_UNSUPPORTED_TYPE:
return "Se proporcionó un valor de un tipo no admitido para json_encode(), tal como un resource.";
default:
return "Error desconocido";
}
}
# Hora de probarla. Tomamos el ejemplo anterior...
$objeto = [
// Nivel 1
"idCuenta" => "123ASD",
"cliente" => "parzibyte",
"saldo" => 250.0,
"movimientos" => [
// Nivel 2
[
"tipo" => "salida",
"monto" => 125.0,
"fecha" => "2018-12-25",
"involucrados" => [
// Nivel 3
"nombre" => "Un nombre",
"idCuenta" => "456465",
],
],
[
"tipo" => "salida",
"monto" => 666.55,
"fecha" => "2018-12-26",
],
],
];
// Con profundidad de 3 para que se genere un error
$codificado = json_encode($objeto, 0, 3);
if ($codificado === false) {
echo "Ocurrió un error al codificar: " . obtenerErrorDeJSON();
} else {
echo $codificado;
}
// Salida: Ocurrió un error al codificar: Se ha excedido la profundidad máxima de la pila.
Y así es como terminamos la parte de codificar JSON en PHP.
Decodificar JSON
Para convertir una cadena JSON a su representación como variable dentro del lenguaje, usamos json_decode
. Esta función también tiene sus argumentos:
- El primer argumento es la cadena que vamos a decodificar
- Como segundo argumento, se especifica si queremos que los objetos se codifiquen como arreglos asociativos. Por defecto, se devuelve como objeto y podemos acceder a sus propiedades con
$objeto->clave
. En caso de que sea un arreglo asociativo, se accede con$objeto["clave"]
- El tercer argumento indica la profundidad; la misma que veíamos anteriormente al codificar, pero ahora para decodificar. Por defecto el valor es de 512.
- Como último argumento tenemos a las opciones, que igualmente son constantes. Al momento de escribir el post sólo existe
JSON_BIGINT_AS_STRING
que convierte valores numéricos grandes a flotantes.
Devuelve el valor NULL en caso de error, o el valor codificado en caso de éxito.
Aquí tenemos un ejemplo:
<?php
# Esto puede venir de cualquier lugar, aquí lo ponemos en una variable directamente
$codificado = '{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}';
$decodificado = json_decode($codificado);
// Comprobamos si no devolvió NULL:
if($decodificado === NULL){
echo "Error decodificando";
}else{
// Podemos acceder a las propiedades
echo "El cliente es: " . $decodificado->cliente;
}
Es fácil decodificar, llamamos a la función y devuelve el JSON decodificado. En ese caso fue un objeto pero se puede decodificar cualquier variable, por ejemplo un arreglo, una cadena, un booleano, etcétera.
Como arreglo asociativo
Ahora veamos el segundo argumento. Si lo ponemos en true, devuelve un arreglo asociativo.
<?php
# Esto puede venir de cualquier lugar, aquí lo ponemos en una variable directamente
$codificado = '{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}';
$comoObjeto = json_decode($codificado);
echo "El saldo es: " . $comoObjeto->saldo . "\n";
$comoArreglo = json_decode($codificado, true);
echo "El saldo es: " . $comoArreglo["saldo"];
La diferencia es que los objetos se convierten a arreglos.
Especificar profundidad
El tercer argumento es la profundidad, la cual es 512 por defecto. Podemos cambiarla ya sea para que sea mayor o para que sea menor. En caso de que el objeto exceda la profundidad, se devuelve NULL
.
<?php
// La función que nos dice el error
function obtenerErrorDeJSON()
{
switch (json_last_error()) {
case JSON_ERROR_NONE:
return "No ha ocurrido ningún error";
case JSON_ERROR_DEPTH:
return "Se ha excedido la profundidad máxima de la pila.";
case JSON_ERROR_STATE_MISMATCH:
return "Error por desbordamiento de buffer o los modos no coinciden";
case JSON_ERROR_CTRL_CHAR:
return "Error del carácter de control, posiblemente se ha codificado de forma incorrecta.";
case JSON_ERROR_SYNTAX:
return "Error de sintaxis.";
case JSON_ERROR_UTF8:
return "Caracteres UTF-8 mal formados, posiblemente codificados incorrectamente.";
case JSON_ERROR_RECURSION:
return "El objeto o array pasado a json_encode() incluye referencias recursivas y no se puede codificar.";
case JSON_ERROR_INF_OR_NAN:
return "El valor pasado a json_encode() incluye NAN (Not A Number) o INF (infinito)";
case JSON_ERROR_UNSUPPORTED_TYPE:
return "Se proporcionó un valor de un tipo no admitido para json_encode(), tal como un resource.";
default:
return "Error desconocido";
}
}
# Esto puede venir de cualquier lugar, aquí lo ponemos en una variable directamente
$codificado = '{"idCuenta":"123ASD","cliente":"parzibyte","saldo":250,"movimientos":[{"tipo":"salida","monto":125,"fecha":"2018-12-25","involucrados":{"nombre":"Un nombre","idCuenta":"456465"}},{"tipo":"salida","monto":666.55,"fecha":"2018-12-26"}]}';
$decodificado = json_decode($codificado, false, 1);
if($decodificado === null){
echo "Error decodificando: " . obtenerErrorDeJSON();
}else{
echo $decodificado->cliente;
}
Se reutiliza la función para comprobar errores. El error es que se excedió la profundidad. Estos límites igualmente funcionan cuando recibimos un objeto y queremos limitar su profundidad por seguridad o porque queremos poner el límite.
Un uso sería al crear una API en donde los usuarios manden un JSON para hacer determinada cosa. Podríamos limitar la profundidad para evitar que intenten agotar los recursos del servidor.
Referencias
Me he basado en lo que hay en el sitio oficial de PHP aquí y aquí.
Hola queria saber si hay alguna forma de retorna imagens del banco de dato utilizando json_encode.
Quiero hacer un sistema de scroll inifinito en mi pagina pero necesito retorna imagens
Hola, podrías mostrar la lista de imágenes y luego agregarlas al HTML ya sea con JavaScript puro o con un framework.
Saludos 🙂
Pingback: Codificar y decodificar JSON con Go/GoLang - Parzibyte's blog
Pingback: Aprende a usar curl - Parzibyte's blog
Pingback: Enviar y recibir JSON con encabezados usando PHP y cURL - Parzibyte's blog