JSON в PHP

Что такое JSON и зачем он вообще нужен

JSON (JavaScript Object Notation) — это текстовый формат для хранения и передачи данных. Он не привязан к какому‑то одному языку: его одинаково понимают JavaScript, PHP, Python, Java и почти всё остальное.

В вебе JSON используют, чтобы:

  • передавать данные между фронтендом и бэкендом;
  • хранить простые структуры в файлах или в базе данных;
  • обмениваться данными с API (Яндекс, соцсети, платёжные системы и т. п.);
  • делать конфиги, которые должны быть человекочитаемыми.

Почему выбирают именно JSON:

  • Читаемость. Обычный текст, можно открыть в любом редакторе.
  • Лёгкость. Нет лишних тегов, как в XML.
  • Универсальность. Почти все языки умеют его читать и писать.
  • Простота структуры. Есть только объекты, массивы, строки, числа, булевы значения и null.

Как JSON выглядит и что ему соответствует в PHP

В JSON есть два главных контейнера:

  • Объект { "key": "value" } → в PHP это ассоциативный массив или объект stdClass.
  • Массив [ "a", "b", "c" ] → в PHP это индексированный массив.

Пример JSON:

json
{
  "user": "alex",
  "active": true,
  "roles": ["admin", "editor"],
  "meta": {
    "last_login": 1710000000
  }
}

После json_decode($json, true) в PHP получится:

php
[
  'user'   => 'alex',
  'active' => true,
  'roles'  => ['admin', 'editor'],
  'meta'   => [
    'last_login' => 1710000000,
  ],
]

Если декодировать без true, будет объект: $data->user, $data->meta->last_login.


Кодирование: превращаем PHP в JSON — json_encode()

Функция: json_encode($value, $flags = 0, $depth = 512)

Простой пример

php
$data = [
    'name' => 'Иван',
    'age'  => 30,
    'active' => true,
];
 
$json = json_encode($data);
echo $json;
// {"name":"\u0418\u0432\u0430\u043d","age":30,"active":true}

Видишь \u0418...? Это UTF‑8 в экранированном виде. Чтобы кириллица была сразу читаемой, нужен флаг:

php
$json = json_encode(
    $data,
    JSON_UNESCAPED_UNICODE
);
echo $json;
// {"name":"Иван","age":30,"active":true}

Полезные флаги

  • JSON_UNESCAPED_UNICODE — русские буквы не превращаются в \uXXXX.
  • JSON_PRETTY_PRINT — делает JSON с отступами (удобно для логов и отладки).
  • JSON_THROW_ON_ERROR — при ошибке выбрасывает исключение вместо возврата false (PHP 7.3+).

Пример с двумя флагами:

php
$json = json_encode(
    $data,
    JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
);

Декодирование: превращаем JSON в PHP — json_decode()

Функция: json_decode($json, $assoc = false, $depth = 512, $flags = 0)

  • $assoc = true → результат: ассоциативный массив.
  • $assoc = false → результат: объект stdClass.

Пример с массивом (чаще удобнее)

php
$json = '{"name":"Иван","age":30}';
$data = json_decode($json, true);
 
var_dump($data['name']); // string(8) "Иван"

Пример с объектом

php
$dataObj = json_decode($json);
var_dump($dataObj->name); // string(8) "Иван"

Почему нельзя проверять ошибки по null

Это частая ошибка. json_decode может вернуть null в двух случаях:

  1. JSON был валидным и на самом деле равен null (json_decode('null') === null).
  2. Произошла ошибка декодирования.

Поэтому правильная проверка — через json_last_error():

php
$data = json_decode($json, true);
 
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
    // тут ошибка
    $msg = json_last_error_msg();
}

То же самое с json_encode: он возвращает false при ошибке, и тогда тоже нужно смотреть json_last_error().


Частые ошибки и почему они возникают

Ошибка Почему Как избежать
json_encode вернул false В данных есть ресурс (PDO‑соединение, файл), либо строка не UTF‑8, либо слишком глубокая вложенность Не кодировать ресурсы; проверять кодировку; использовать json_last_error()
Кириллица в виде \u04xx Не указан JSON_UNESCAPED_UNICODE Добавлять этот флаг всегда, если есть русский текст
json_decode вернул null, но кажется, что ошибка Забыли проверить json_last_error(); null — валидный JSON Проверять ошибку явно, а не только по null
Смешаны массивы и объекты В одном месте assoc=true, в другом нет — код становится запутанным Выбрать единый стиль проекта (чаще массивы) и придерживаться его

Безопасные обёртки: чтобы не писать проверки каждый раз

Для PHP 7 удобно сделать две простые функции.

Безопасное кодирование

php
function jsonEncodeSafe(array $data): string
{
    $json = json_encode(
        $data,
        JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
    );
    return $json;
}

С флагом JSON_THROW_ON_ERROR при ошибке сразу будет исключение JsonException, которое можно ловить через try/catch.

Безопасное декодирование

php

<

function jsonDecodeSafe(string $json, bool $assoc = true)
{
    $data = json_decode($json, $assoc);
 
    if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
        throw new \RuntimeException(
            'Ошибка JSON: ' . json_last_error_msg()
        );
    }
 
    return $data;
}

Теперь можно писать:

php
try {
    $data = jsonDecodeSafe($inputJson);
} catch (\RuntimeException $e) {
    // логирование, ответ клиенту и т. п.
}

Пример: полный цикл «запрос → валидация → ответ»

Подходит для API.

php
header('Content-Type: application/json; charset=utf-8');
 
try {
    $input = file_get_contents('php://input');
    if ($input === false) {
        throw new \RuntimeException('Не удалось прочитать тело запроса');
    }
 
    $data = jsonDecodeSafe($input, true);
 
    // простая валидация
    if (!isset($data['message']) || !is_string($data['message'])) {
        throw new \InvalidArgumentException('Поле message обязательно и должно быть строкой');
    }
 
    // какая-то логика (например, запись в БД через PDO)
    $response = [
        'success' => true,
        'message' => 'Принято: ' . $data['message'],
        'timestamp' => time(),
    ];
 
    echo jsonEncodeSafe($response);
 
} catch (\Exception $e) {
    http_response_code(400);
    echo json_encode([
        'success' => false,
        'error'   => $e->getMessage(),
    ], JSON_UNESCAPED_UNICODE);
}

Практические советы

  • Всегда используй JSON_UNESCAPED_UNICODE при кодировании, если в данных есть русский текст.
  • Выбирай один стиль (массивы или объекты) и придерживайся его в проекте. Для интеграции и API чаще удобнее массивы (assoc=true).
  • Не доверяй входящему JSON. Всегда валидируй структуру и типы полей после декодирования.
  • Логируй ошибки JSON вместе с json_last_error_msg() — это сильно ускоряет отладку.
  • Для отладки используй JSON_PRETTY_PRINT, но в продакшене лучше без него (меньше размер ответа).

Мини‑пример: сохраняем JSON в поле MySQL и читаем обратно

Допустим ты хочешь хранить простую структуру в одном JSON‑поле.

php
// запись
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 
$dataToSave = [
    'ip'       => $_SERVER['REMOTE_ADDR'] ?? '',
    'browser'  => $_SERVER['HTTP_USER_AGENT'] ?? '',
];
$jsonToDb = jsonEncodeSafe($dataToSave);
 
$stmt = $pdo->prepare('INSERT INTO logs (json_data) VALUES (:json)');
$stmt->execute([':json' => $jsonToDb]);
 
// чтение
$stmt = $pdo->prepare('SELECT json_data FROM logs WHERE id = :id');
$stmt->execute([':id' => 1]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
 
$restored = jsonDecodeSafe($row['json_data'], true);
var_dump($restored['ip']);

Когда JSON не нужен

  • Если структура очень сложная и нужна строгая схема с валидацией — иногда лучше отдельные колонки в БД.
  • Если данные бинарные (картинки, файлы) — JSON не подходит.
  • Если критична максимальная скорость и объём — бинарные форматы (MessagePack, Protobuf) могут быть эффективнее.

Но для большинства веб‑задач в PHP JSON — самый простой и надёжный вариант.

Добавить комментарий