Декодирование JSON из HTTP-ответов в Laravel

В Laravel 12.48.0 появилась удобная возможность точнее контролировать, как именно декодируются JSON-ответы HTTP-клиента.

Что происходит сейчас

Раньше многие разработчики сталкивались с неожиданным поведением:

$response = Http::get('https://cosmastech.com'); // URL, который возвращает HTML
$json = $response->json();

В этом примере в переменной $json оказывается null, и это вполне логично: по умолчанию PHP возвращает null, если переданный в json_decode() текст является невалидным JSON. Такая особенность известна не всем, пока не ломается код в продакшене.

Проверить, вернул ли json_decode() null из-за ошибки декодирования или потому что строка действительно содержит "null", можно через json_last_error(). Но это неудобно и громоздко.

Лучше выбрасывать исключение

В PHP 8 можно использовать флаг JSON_THROW_ON_ERROR, чтобы при попытке декодировать некорректный JSON не получить просто null, а бросить исключение \JsonException:

$str = '{"HELLO": xyz}'; // Некорректный JSON
json_decode($str, flags: JSON_THROW_ON_ERROR); // бросит исключение

Это безопаснее, потому что сразу видно, что что-то пошло не так.

Поддержка в Laravel

В Laravel теперь можно передать такие флаги прямо в методы разбора JSON у HTTP-ответа:

$response = Http::get('https://cosmastech.com');

try {
    $json = $response->json(flags: JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
    // Отлично — тут точно неверный JSON
}

Кроме метода .json() те же флаги принимают и другие методы ответа: .collect(), .object(), .fluent() что позволяет гибко управлять тем, как вы обрабатываете ответ.

Пример: большие целые числа

Если API возвращает очень большие числа, которые превышают максимальное значение целого типа в PHP (например, большие ID), без специальных флагов PHP превратит его в float. Чтобы сохранить такие числа как строки, можно использовать флаг JSON_BIGINT_AS_STRING:

Http::fake([
    '*' => '{
        "hello": "world",
        "big_int": 123343343580999843483023
    }'
]);

$response = Http::get('https://laravel.com');

$obj = $response->object(
    flags: JSON_BIGINT_AS_STRING
);

var_dump($obj->big_int); // "123343343580999843483023"

Без этого флага большое число стало бы float, и часть точности потерялась бы.

Настройки по умолчанию

Чтобы не передавать флаги в каждом вызове, можно задать дефолтные флаги декодирования в приложении. Для этого в AppServiceProvider в методе boot():

use Illuminate\Http\Client\Response;

public function boot()
{
    Response::$defaultJsonDecodingFlags =
        JSON_BIGINT_AS_STRING |
        JSON_THROW_ON_ERROR;
}

Теперь во всех вызовах .json() данные будут декодированы с этими флагами по умолчанию, и вам не придется явно указывать их в каждом месте.

Комментарии (0)

Войдите, чтобы оставить комментарий

Похожие статьи

Условные операторы if else в Bash и shell-скриптах

Использование условных операторов if, else и elif в shell-скриптах Bash. Рассматриваются базовый синтаксис, числовые и строковые сравнения, проверки файлов, вложенные условия и практические примеры для повседневных задач администрирования и автоматизации.

Наследование моделей в Laravel с помощью Parental

Разбор пакета Parental для Laravel, который реализует single table inheritance (STI) в Eloquent, т.е наследование моделей в одной таблице. Статья объясняет, зачем это нужно, как настроить Parental, управлять типами моделей и работать с дочерними связями.

Оптимизация Laravel-приложений с Octane

Полное руководство по Laravel Octane: от базовой концепции до настройки, особенностей и сравнения с классическим подходом. Узнайте, как Octane ускоряет Laravel-приложения и когда его стоит использовать.