json_validate() en PHP 8.3: validar JSON sin decodificarlo y errores JSON

PHP 8.3 introdujo json_validate(), una función que comprueba si una cadena es JSON válido sin decodificarla. La diferencia con json_decode() es importante: no construye ninguna estructura de datos en memoria, por lo que consume muchos menos recursos cuando solo te interesa saber si el JSON es válido, no su contenido.

Sintaxis básica

<?php
// Disponible desde PHP 8.3
var_dump(json_validate('{"nombre":"Ana","edad":28}'));      // bool(true)
var_dump(json_validate('[1,2,3]'));                         // bool(true)
var_dump(json_validate('"cadena válida"'));                 // bool(true)
var_dump(json_validate('{clave: valor}'));                  // bool(false)  ? sin comillas
var_dump(json_validate('{"a":1,}'));                       // bool(false)  ? coma final
var_dump(json_validate(''));                               // bool(false)
?>

Acepta un segundo parámetro para limitar la profundidad máxima (por defecto 512), igual que json_decode():

<?php
$jsonProfundo = str_repeat('{"a":', 10) . '1' . str_repeat('}', 10);

var_dump(json_validate($jsonProfundo, 5));   // bool(false)  ? supera el límite
var_dump(json_validate($jsonProfundo, 15));  // bool(true)
?>

Comparación con json_decode()

<?php
$json = file_get_contents('/ruta/a/fichero-grande.json');

// Opción 1: json_decode() + comprobación de error
// Construye toda la estructura en memoria aunque luego la deseches
$datos = json_decode($json, true);
$esValido = ($datos !== null || json_last_error() === JSON_ERROR_NONE);

// Opción 2: json_validate() en PHP 8.3
// Sin construcción de estructura en memoria
$esValido = json_validate($json);

// Solo si es válido, entonces decodificamos
if ($esValido) {
    $datos = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
}
?>

Combinar json_validate() con JsonException

<?php
function procesarPayload(string $body): array
{
    if (!json_validate($body)) {
        throw new InvalidArgumentException('El cuerpo de la petición no es JSON válido');
    }
    // Aquí ya podemos decodificar con confianza
    return json_decode($body, true, 512, JSON_THROW_ON_ERROR);
}

// En un controlador de API
try {
    $datos = procesarPayload(file_get_contents('php://input'));
    // procesar $datos...
} catch (InvalidArgumentException $e) {
    http_response_code(400);
    echo json_encode(['error' => $e->getMessage()]);
}
?>

Uso en validación de formularios y webhooks

<?php
// Webhook de pago: validar antes de procesar
function recibirWebhook(): void
{
    $payload = file_get_contents('php://input');

    if (!json_validate($payload)) {
        http_response_code(400);
        exit('Payload inválido');
    }

    $datos = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
    // procesar evento de pago...
}

// Validar campo JSON guardado en base de datos
function esCampoJsonValido(?string $valor): bool
{
    if ($valor === null || $valor === '') {
        return false;
    }
    return json_validate($valor);
}
?>

Polyfill para PHP 8.1 y PHP 8.2

Si tu proyecto todavía no usa PHP 8.3, puedes crear un polyfill con json_decode() y JSON_THROW_ON_ERROR:

<?php
if (!function_exists('json_validate')) {
    function json_validate(string $json, int $depth = 512, int $flags = 0): bool
    {
        if ($depth <= 0) {
            throw new ValueError('json_validate(): Argument #2 ($depth) must be greater than 0');
        }
        try {
            json_decode($json, true, $depth, JSON_THROW_ON_ERROR | $flags);
            return true;
        } catch (JsonException) {
            return false;
        }
    }
}
?>

El polyfill es funcionalmente equivalente, aunque sí construye la estructura en memoria. Para PHP 8.1/8.2 es la única opción disponible. Instalar el paquete symfony/polyfill-php83 via Composer también incluye esta función de forma transparente.

Cuándo usar json_validate() y cuándo no

  • Úsala cuando necesites validar JSON antes de procesarlo, especialmente con payloads grandes de APIs o webhooks donde la validación puede fallar frecuentemente.
  • No la uses si inmediatamente después vas a llamar a json_decode() con todos los datos: estás recorriendo la cadena dos veces. En ese caso, llama directamente a json_decode() con JSON_THROW_ON_ERROR y captura la excepción.

La documentación oficial de json_validate() incluye el benchmark de consumo de memoria frente a json_decode() y los flags disponibles.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP