Гид по технологиям

JSON в PHP: чтение, запись и надёжная обработка

• 8 min read • Разработка • Обновлено 28 Nov 2025
JSON в PHP: чтение и запись
JSON в PHP: чтение и запись

Логотип PHP

Быстрые ссылки

  • Чтение JSON
  • Обработка ошибок парсинга
  • Сериализация в JSON
  • Перенос JSON в уровень доменной модели приложения

Введение

JSON — один из самых распространённых форматов сериализации данных. Он возник внутри JavaScript (JSON расшифровывается как JavaScript Object Notation) и стал стандартом для многих веб-API и конфигурационных файлов.

PHP поставляется с встроенной поддержкой JSON. Ранее функциональность реализовывалась отдельным расширением; начиная с PHP 8.0 JSON стал постоянно активным расширением, которое нельзя отключить.

Важно понимать термин: json-парсинг — процесс преобразования текстовой JSON-строки в структуры данных PHP; сериализация — обратный процесс.

Чтение JSON

Для разбора JSON используйте функцию:

json_decode()

Полная сигнатура:

json_decode(string $json, bool|null $associative=null, int $depth=512, int $flags=0) : mixed;

Самый простой вызов — передать JSON-строку без дополнительных аргументов. Пример строки:

{"foo": "bar"}

По умолчанию она декодируется в экземпляр класса

stdClass

— объект PHP с публичным свойством

foo

со значением

bar

Если нужен ассоциативный массив вместо объекта, передайте вторым аргументом

true

Пример:

json_decode('{"foo":"bar"}', true);
// Результат: ["foo" => "bar"]

Параметр

$depth

контролирует максимальную глубину вложенности, которую будет парсить функция. Если JSON вложен глубже, результатом будет

null

и парсинг прекращается.

Параметр

$flags

принимает битовую маску флагов, меняющих поведение парсера (подробно в документации PHP). Флаги определяют, например, обработку больших чисел, UTF-8 и другие тонкости.

Хорошие практики при чтении JSON

  • Всегда проверяйте источник JSON перед парсингом (заголовки, кодировка, размер). Внешние данные могут быть вредоносными или повреждёнными.
  • Используйте строгую обработку ошибок (см. ниже) вместо слепого сравнения с null.
  • Для больших JSON-файлов избегайте одновременной загрузки всей строки в память; используйте потоковый парсинг или разбивку.

Обработка ошибок парсинга

По умолчанию

json_decode()

возвращает

null

при ошибке синтаксиса. Проблема: вернувшийся null может означать как ошибочный JSON, так и корректную JSON-строку со значением null — отличить их нельзя.

Начиная с PHP 7.3, добавлен флаг

JSON_THROW_ON_ERROR

. Передайте его в параметр

$flags

, чтобы получать исключения при ошибках парсинга:

json_decode("['malformed json", true, 512, JSON_THROW_ON_ERROR);

Это приводит к выбрасыванию объекта исключения типа

JsonException

при неверном JSON. Такой подход упрощает обработку ошибок и делает поведение предсказуемым.

PHP 8 добавил именованные аргументы, что делает вызов короче:

json_decode(json: "['malformed json", flags: JSON_THROW_ON_ERROR);

Эти два примера эквивалентны, но синтаксис с именованными аргументами доступен только в PHP 8 и выше.

Примеры обработки ошибок

Простой безопасный шаблон:

try {
    $data = json_decode($jsonString, associative: true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    // Логируем и возвращаем пользовательскую ошибку
    error_log('JSON parse error: ' . $e->getMessage());
    throw new RuntimeException('Некорректный формат данных');
}

Если вы используете фреймворк (Laravel, Symfony и т.п.), лучше преобразовывать JsonException в HTTP-ответ 400 с подробной, но безопасной для клиента информацией.

Когда JSON_THROW_ON_ERROR не подходит

  • В задачах, где нужно попытаться восстанавливать частично повреждённые документы и продолжить обработку, может быть полезен режим без исключений вместе с JSON_PARTIAL_OUTPUT_ON_ERROR при кодировании (см. ниже).
  • В средах, где исключения запрещены политикой проекта, придётся явно проверять json_last_error() после вызова json_decode без флагов.

Сериализация данных в JSON

Для преобразования PHP-значений в JSON используйте функцию:

Сигнатура:

json_encode(mixed $value, int $flags=0, int $depth=512) : string|false;

PHP принимает любые значения (кроме ресурсов) и пытается корректно их отобразить в JSON. Ассоциативные массивы и объекты PHP становятся объектами JSON; скаляры мапятся напрямую.

Обратите внимание: порядок необязательных параметров отличается от json_decode() — сначала flags, затем depth.

Важные флаги при кодировании

  • JSON_FORCE_OBJECT — принудительно кодирует числовые массивы как объекты JSON. Полезно, если пустой массив должен быть представлен как объект.
  • JSON_PRETTY_PRINT — добавляет переносы строк и отступы для удобочитаемости (конфигурационные файлы, примеры). Не используйте для сетевого трафика, если важна экономия трафика.
  • JSON_PRESERVE_ZERO_FRACTION — сохраняет дробную часть у чисел вида 0.0, вместо сокращения до 0.
  • JSON_NUMERIC_CHECK — пытается преобразовать строковые числа в числовые значения в выходном JSON (быть осторожным — это глобальная трансформация строк).
  • JSON_PARTIAL_OUTPUT_ON_ERROR — пытается продолжить сериализацию при ошибках, подставляя допустимые значения.

Пример:

$json = json_encode($data, JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION);

Особенности и подводные камни при кодировании

  • Ресурсы PHP не сериализуются; json_encode вернёт false. Всегда проверяйте результат и обрабатывайте ошибки (JSON_THROW_ON_ERROR недоступен на момент кодирования в старых версиях PHP; в современных версиях можно использовать JsonException при кодировании, если включён соответствующий флаг).
  • Порядок ключей в ассоциативном массиве будет сохранён при кодировании, но JSON вроде не гарантирует семантику порядка; не полагайтесь на порядок в клиентских кодах.
  • JSON_NUMERIC_CHECK может неожиданно преобразовать строки, которые вы хотели сохранить как строки (например, почтовые индексы с ведущими нулями).

Перенос JSON в доменный слой приложения

В бэкендах обычно приходится сериализовать объекты доменной модели (например, BlogPost, User) в JSON для HTTP-ответов. По умолчанию json_encode обходит только публичные свойства класса.

Пример простого класса:

class BlogPost {
    protected int $Id = 1;
    public string $Title = "Example";
}

// produces '{"Title": "Example"}'
$json = json_encode(new BlogPost());

Проблема: защищённые и приватные свойства не попадут в результат. Чтобы контролировать сериализацию, реализуйте интерфейс

JsonSerializable

и определите метод

jsonSerialize()

, который возвращает любые данные, пригодные для сериализации в JSON (массив, объект, скаляр).

Пример:

class BlogPost implements JsonSerializable {
    protected int $Id = 1;
    public string $Title = "Example";

    public function jsonSerialize() {
        return [
            "Id" => $this->Id,
            "Title" => $this->Title
        ];
    }
}

// produces '{"Id":1,"Title":"Example"}'
$json = json_encode(new BlogPost());

jsonSerialize может возвращать любую структуру, которую PHP сможет рекурсивно сериализовать — ещё один объект, массив и т.д.

Альтернативы и подходы

  • DTO/ресурсные объекты: вместо того чтобы заставлять доменные объекты знать о формате JSON, создавайте промежуточные объекты (Data Transfer Objects), которые ответственны за представление данных.
  • Сериализаторы/нормализаторы (Symfony Serializer, Laravel API Resources): библиотеки дают гибкий контроль над группами полей, версионированием и конфиденциальными полями.

Практические рекомендации по безопасности и валидации

  • Никогда не доверяйте JSON, полученному извне. Валидируйте структуру и типы до использования в критичных операциях (запросы к БД, выполнение команд).
  • При возвращении ошибок клиенту не раскрывайте внутренние детали: логируйте стек ошибки на сервере, а клиенту отдавайте обобщённое сообщение.
  • Ограничьте максимальный размер тела запроса и глубину вложенности для защиты от атак, направленных на исчерпание памяти.

Производительность и масштабирование

  • Для больших JSON-объектов используйте потоковый парсинг (событийные парсеры) или разбивайте данные на чанки; стандартный json_decode требует полной загрузки строки в память.
  • Для повторного использования структур данных кэшируйте результирующие объекты/JSON, если данные неизменны.
  • Профилируйте места сериализации: частые вызовы json_encode на больших объектах могут стать узким местом.

Совместимость и миграция между версиями PHP

  • PHP 7.3 ввёл JSON_THROW_ON_ERROR.
  • PHP 8.0 сделал расширение JSON всегда активным и добавил именованные аргументы, упрощающие вызовы.

Если ваше приложение поддерживает разные версии PHP, оберните использование новых флагов проверкой версии или адаптером, который использует совместимый режим в старых версиях.

Шаблон принятия (Критерии приёмки)

  • JSON от клиента парсится без исключений при корректных данных.
  • При неверном JSON сервер возвратит корректный обработанный ответ с кодом 400 (или внутренней бизнес-ошибкой по соглашению).
  • Логи содержат подробную техническую информацию, но клиент получает безопасное сообщение.
  • Сериализованные объекты не раскрывают приватные поля, которые не предназначены для внешнего употребления.

Чек-листы по ролям

Разработчик:

  • Использую json_decode с JSON_THROW_ON_ERROR в новых проектах.
  • Проверяю размер и глубину входящего JSON.
  • Логирую детальную ошибку при исключении.

Архитектор:

  • Решил, где используются DTO и где JsonSerializable уместен.
  • Определил стратегии версионирования API и формат ответов.

Операции/DevOps:

  • Ограничил размер тела запросов на уровне веб-сервера/платформы.
  • Настроил мониторинг ошибок JsonException и частоты отказов парсинга.

Примеры тестов и критерии приёмки

Тесты парсинга:

  • При корректном JSON возвращается ожидаемая структура и типы.
  • При неверном JSON выбрасывается JsonException и логируется ошибка.

Тесты сериализации:

  • JsonSerializable возвращает только ожидаемые ключи.
  • JSON_PRESERVE_ZERO_FRACTION сохраняет дробную часть у 0.0.

Маленькая методология миграции — шаги

  1. Добавьте глобальный адаптер для работы с JSON (оборачивает json_decode/json_encode).
  2. Внедрите JSON_THROW_ON_ERROR внутри адаптера с обратной совместимостью для старых PHP.
  3. Мигрируйте критичные места на использование адаптера; покрывайте тестами.
  4. Анализируйте логирование ошибок парсинга и корректируйте обработку.

Частые ошибки и когда это не сработает

  • Ожидание, что json_encode автоматически превратит приватные свойства: не сработает — нужен JsonSerializable или DTO.
  • Использование JSON_NUMERIC_CHECK без анализа входных данных — может испортить строковые идентификаторы.
  • Слепое доверие размера и глубины JSON — приводит к DoS через переполнение памяти.

Малая галерея крайних случаев

  • Пустая строка как вход — json_decode вернёт null; с JSON_THROW_ON_ERROR бросит исключение.
  • Большие числа, не помещающиеся в 64-бит — стоит хранить как строки и обрабатывать на клиенте/сервере явно.

Мини‑шпаргалка (cheat sheet)

  • Чтение: json_decode($json, true, 512, JSON_THROW_ON_ERROR)
  • Запись: json_encode($value, JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION)
  • Кастомная сериализация: implements JsonSerializable + jsonSerialize()
  • Проверка ошибок (старые версии): json_last_error(), json_last_error_msg()

Пример принятого рабочего потока (Playbook)

  1. При получении тела запроса валидируем заголовок Content-Type: application/json.
  2. Ограничиваем длину тела на уровне NGINX / web-сервера.
  3. Парсим через адаптер с JSON_THROW_ON_ERROR.
  4. Валидируем структуру через схемы (например, JSON Schema) или DTO-валидаторы.
  5. При ошибке отправляем 400 и логируем детально на сервере.

Небольшая «1‑строчная» глоссарная справка

  • json_decode: парсит JSON в PHP-структуры.
  • json_encode: сериализует PHP-значения в JSON-строку.
  • JsonSerializable: интерфейс для контроля сериализации объектов.
  • JSON_THROW_ON_ERROR: флаг, заставляющий выбрасывать исключение при ошибках парсинга.

FAQ

Q: Что делать, если json_decode возвращает null без исключения?

A: Проверьте json_last_error() и json_last_error_msg(), либо включите JSON_THROW_ON_ERROR для получения исключения и ясного поведения.

Q: Как сериализовать приватные свойства объекта в JSON?

A: Реализуйте JsonSerializable и возвращайте массив/объект с нужными полями либо используйте DTO.

Q: Можно ли использовать json_encode для потоковой записи больших наборов данных?

A: json_encode не потоковая. Для больших наборов лучше формировать JSON по частям вручную или использовать библиотеку/стриминг.

Заключение

JSON в PHP — простой и мощный инструмент, но при неправильном использовании он даёт неожиданные ошибки: потеря данных, некорректная сериализация, уязвимости по памяти. Применяйте JSON_THROW_ON_ERROR для детальной обработки ошибок, используйте JsonSerializable или DTO для контроля вывода, валидируйте входные данные и учитывайте производительность при работе с большими объёмами.

Внедрите адаптеры и стандарты в вашем проекте — это снизит технический долг и упростит миграцию между версиями PHP.

Важно: логируйте технические детали, но не раскрывайте их клиентам; на клиент отдавайте безопасные сообщения об ошибках.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Несколько аккаунтов Skype: Multi Skype Launcher
Программное обеспечение

Несколько аккаунтов Skype: Multi Skype Launcher

Журнал для работы: повысить продуктивность
Productivity

Журнал для работы: повысить продуктивность

Персональные звуки уведомлений на Android
Android.

Персональные звуки уведомлений на Android

Скачивание шоу Hulu для офлайн‑просмотра
Стриминг

Скачивание шоу Hulu для офлайн‑просмотра

Microsoft Start: персонализированная новостная лента
Новости

Microsoft Start: персонализированная новостная лента

Как изменить имя в Epic Games быстро
Гайды

Как изменить имя в Epic Games быстро