В 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() данные будут декодированы с этими флагами по умолчанию, и вам не придется явно указывать их в каждом месте.