json_decode() convierte una cadena JSON en un valor PHP. Parece simple, pero sus parámetros y el manejo de errores tienen matices que conviene conocer antes de usarla en producción con datos de APIs externas.
Sintaxis y el segundo parámetro clave
<?php
$json = '{"nombre":"Ana García","edad":28,"activa":true,"tags":["php","backend"]}';
// Por defecto: devuelve stdClass
$obj = json_decode($json);
echo $obj->nombre; // Ana García
echo $obj->edad; // 28
// Con true como segundo argumento: devuelve array asociativo
$arr = json_decode($json, true);
echo $arr['nombre']; // Ana García
echo $arr['edad']; // 28
?>
Elige true casi siempre: los arrays son más predecibles que los objetos stdClass, se validan fácilmente con isset() y no dan errores de propiedad indefinida. Usa el modo objeto cuando la librería que consumes lo espere explícitamente.
Controlar la profundidad máxima
El tercer parámetro de json_decode() es el límite de profundidad de anidamiento. Por defecto es 512. Si el JSON supera esa profundidad, la función devuelve null y establece un error:
<?php
$jsonProfundo = str_repeat('{"a":', 10) . '1' . str_repeat('}', 10); // profundidad 10
// Con límite 5: falla
$resultado = json_decode($jsonProfundo, true, 5);
var_dump($resultado); // NULL ? error de profundidad
// Con límite suficiente: funciona
$resultado = json_decode($jsonProfundo, true, 15);
var_dump($resultado['a']['a']['a']); // array
?>
Manejo de errores con json_last_error()
<?php
$jsonRoto = '{"nombre": "Ana", "edad": 28,}'; // coma final inválida
$datos = json_decode($jsonRoto, true);
if ($datos === null && json_last_error() !== JSON_ERROR_NONE) {
echo 'Error JSON: ' . json_last_error_msg();
// Error JSON: Syntax error
}
// Constantes de error más comunes:
// JSON_ERROR_NONE = 0 (sin error)
// JSON_ERROR_DEPTH = 1 (profundidad máxima superada)
// JSON_ERROR_STATE_MISMATCH = 2 (JSON malformado)
// JSON_ERROR_CTRL_CHAR = 3 (carácter de control inesperado)
// JSON_ERROR_SYNTAX = 4 (error de sintaxis)
// JSON_ERROR_UTF8 = 5 (carácter UTF-8 malformado)
?>
JSON_THROW_ON_ERROR: la opción correcta desde PHP 7.3
El patrón con json_last_error() es verboso y fácil de olvidar. JSON_THROW_ON_ERROR como cuarto argumento convierte cualquier fallo en una JsonException:
<?php
function decodeSeguro(string $json): array
{
try {
$datos = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
return $datos;
} catch (JsonException $e) {
throw new RuntimeException(
'JSON inválido: ' . $e->getMessage(),
previous: $e
);
}
}
$resultado = decodeSeguro('{"clave":"valor"}');
// ['clave' => 'valor']
decodeSeguro('{clave:valor}');
// RuntimeException: JSON inválido: Syntax error
?>
Ejemplo real: API de GitHub
<?php
$ctx = stream_context_create(['http' => [
'header' => 'User-Agent: PHP-ejemplo/1.0',
]]);
$body = file_get_contents('https://api.github.com/repos/php/php-src', false, $ctx);
if ($body === false) {
throw new RuntimeException('No se pudo conectar a GitHub');
}
$repo = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
echo 'Repositorio: ' . $repo['full_name'] . "n";
echo 'Estrellas: ' . $repo['stargazers_count'] . "n";
echo 'Forks: ' . $repo['forks_count'] . "n";
?>
Ejemplo real: API meteorológica Open-Meteo
<?php $url = 'https://api.open-meteo.com/v1/forecast?latitude=40.42&longitude=-3.70¤t_weather=true'; $respuesta = file_get_contents($url); $datos = json_decode($respuesta, true, 512, JSON_THROW_ON_ERROR); $actual = $datos['current_weather']; echo 'Temperatura en Madrid: ' . $actual['temperature'] . ' °C' . "n"; echo 'Viento: ' . $actual['windspeed'] . ' km/h' . "n"; ?>
Ejemplo real: API de CoinGecko
<?php $url = 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur'; $body = file_get_contents($url); $datos = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo 'Bitcoin: ' . number_format($datos['bitcoin']['eur'], 2, ',', '.') . ' '; ?>
La documentación oficial de json_decode() incluye todos los flags disponibles y los detalles sobre el comportamiento con JSON muy grandes o con profundidades extremas.
