Depuración de API sobre la marcha: ¿por qué tu JSON devuelve errores?
\n\n¿Alguna vez te has quedado mirando una pantalla vacía de Postman a las tres de la madrugada pensando: «¿Por qué mi script PHP devuelve HTML en lugar de JSON?»? Si es así, no estás solo. El tema Returning JSON from a PHP Script en Stack Overflow acumula decenas de miles de visitas, y no es casualidad. Uno de cada dos desarrolladores backend principiantes tropieza con los mismos errores: cabeceras olvidadas, salida basura, Content-Type incorrecto.
\n\nEn este artículo no solo copiaremos una respuesta de un foro. Analizaremos cómo devolver JSON desde PHP de forma correcta, rápida y sin sorpresas. Descubrirás por qué exit(json_encode($data)) puede fallarte, cómo ahorrar milisegundos en la serialización y qué hacer si el JSON se «rompe» de repente.
¿Listo? ¡Empecemos!
\n\nAnatomía de una respuesta JSON correcta
\n\nEmpecemos por lo básico. Así es como no se debe hacer:
\n\n<?php\n$data = ['status' => 'ok', 'message' => '¡Hola!'];\necho json_encode($data);\n?>\n\n¿Por qué está mal? Primero, falta la cabecera Content-Type. El navegador o cliente puede interpretar la respuesta como HTML o texto. Segundo, si antes hay un espacio, un echo o un warning en el código, el JSON se romperá.
La plantilla mínima correcta es esta:
\n\n<?php\nheader('Content-Type: application/json; charset=utf-8');\n$data = ['status' => 'ok', 'message' => '¡Hola!'];\necho json_encode($data, JSON_UNESCAPED_UNICODE);\nexit;\n?>\n\nFíjate en JSON_UNESCAPED_UNICODE: conserva los caracteres acentuados y especiales legibles, sin convertirlos en \\u00a1Hola!. Y exit es un seguro contra cualquier salida accidental después de la respuesta.
Cabeceras: no solo Content-Type
\n\nPara optimizar el rendimiento del JSON, añade un par de cabeceras más:
\n\nheader('Content-Type: application/json; charset=utf-8');\nheader('X-Content-Type-Options: nosniff');\nheader('Cache-Control: no-cache, must-revalidate');\n\nX-Content-Type-Options: nosniff evita que el navegador «adivine» el tipo de contenido — es seguridad. Y Cache-Control es útil para datos dinámicos, así el cliente no almacena en caché una respuesta obsoleta.
Manejo de errores: cuando json_encode falla
\n\nLa función json_encode devuelve false si falla. Y aquí empieza lo interesante. Si simplemente haces echo json_encode($data) y obtienes una cadena vacía, el cliente se quedará desconcertado.
Siempre verifica el resultado:
\n\n$json = json_encode($data, JSON_UNESCAPED_UNICODE);\nif ($json === false) {\n http_response_code(500);\n echo json_encode(['error' => 'Error de codificación JSON: ' . json_last_error_msg()]);\n exit;\n}\necho $json;\nexit;\n\nPor cierto, json_last_error_msg() devuelve un mensaje claro: «Malformed UTF-8 characters» o «Inf and NaN cannot be JSON encoded». Esto te ahorrará horas de depuración.
Datos profundamente anidados y recursión
\n\nOtro problema son las referencias circulares en objetos. Si pasas un objeto con referencias mutuas, json_encode lanzará un error. Solución: usa JSON_PARTIAL_OUTPUT_ON_ERROR o convierte el objeto a array previamente.
$json = json_encode($complexObject, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR);\n\nPero cuidado: la salida parcial puede estar incompleta. Es mejor corregir la estructura de datos.
\n\nOptimización del rendimiento JSON en PHP
\n\nAhora vamos a lo jugoso: cómo acelerar la devolución de JSON desde PHP.
\n\n1. Caché del JSON serializado
\n\nSi los datos cambian con poca frecuencia (catálogos, configuraciones, listas de ciudades), no los serialices cada vez. Guarda el JSON listo en un archivo, Memcached