Перейти к содержимому
Теория · Файлы, пространства имён и Composer · урок 6 из 7

JSON: json_encode и json_decode

Когда библиотеки обмениваются книгами, они пишут заявки на общем бланке. Каждая библиотека расставляет книги по-своему, но бланк понимает любая. Программам тоже нужен такой бланк: сайт на PHP отдаёт данные странице на JavaScript, мобильному приложению, программе на Python.

Как вы уже знаете, функция json_encode записывает массив в JSON — текстовый формат, которым программы обмениваются данными. В этом уроке разберём, как JSON устроен, как прочитать его обратно и что делать, если пришёл испорченный текст.

Массив в JSON

PHP
<?php

$user = ['name' => 'Анна', 'age' => 30, 'admin' => false, 'tags' => ['php', 'sql']];
echo json_encode($user), "\n"; // {"name":"\u0410\u043d\u043d\u0430","age":30,"admin":false,"tags":["php","sql"]}
echo json_encode($user, JSON_UNESCAPED_UNICODE), "\n"; // {"name":"Анна","age":30,"admin":false,"tags":["php","sql"]}
echo json_encode([1 => 20]), "\n"; // {"1":20}

Давайте разберём, во что превращается каждое значение:

  1. Ассоциативный массив $user становится JSON-объектом в фигурных скобках, ключи — строками в двойных кавычках.
  2. Список ['php', 'sql'] становится JSON-массивом в квадратных скобках.
  3. Число 30 и логическое false записываются как есть: в JSON есть свои числа, true, false и null.
  4. Массив [1 => 20] — не список, ведь его ключи не идут с нуля, поэтому он тоже становится объектом.

Кириллицу json_encode по умолчанию записывает кодами вроде \u0410. Программа на другой стороне прочитает их правильно, но человеку так читать неудобно.

Флаг JSON_UNESCAPED_UNICODE оставляет буквы как есть. Ещё один флаг, JSON_PRETTY_PRINT, раскладывает JSON по строкам с отступами — так его удобно разглядывать глазами.

Обратно из JSON: json_decode

Функция json_decode читает JSON и возвращает значение PHP. Что именно она вернёт, зависит от второго аргумента:

PHP
$json = '{"name":"Anna","age":30,"tags":["php","sql"]}';
$obj = json_decode($json);
$arr = json_decode($json, true);
echo get_class($obj), "\n"; // stdClass
echo $obj->name, ' ', $obj->tags[1], "\n"; // Anna sql
echo $arr['name'], ' ', $arr['tags'][1], "\n"; // Anna sql

Два вызова различаются так:

  • без второго аргумента JSON-объект становится объектом встроенного класса stdClass, со свойством на каждый ключ JSON, и читают их через ->;
  • с аргументом true JSON-объект становится ассоциативным массивом, и значения читают через ['name'].

JSON-массив в обоих случаях становится списком PHP.

Чаще пишут json_decode($json, true): с массивами работать привычнее, и для них есть все функции из темы о массивах.

Здесь картинка с бланком перестаёт работать. На бланке есть только текст, числа и списки, а места для класса объекта нет. Поэтому json_decode не может вернуть ваш объект User — только безликий stdClass или массив.

Испорченный JSON

В JSON ключи обязательно стоят в двойных кавычках. Строка {name: "Anna"} — уже не JSON:

PHP
var_dump(json_decode('{name: "Anna"}')); // NULL
try {
    json_decode('{name: "Anna"}', true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo get_class($e), ': ', $e->getMessage(), "\n"; // JsonException: Syntax error
}

Без флагов json_decode молча возвращает null. Но null бывает и правильным ответом: строка 'null' — тоже JSON. По одному null ошибку от такого значения не отличить.

Поэтому надёжнее флаг JSON_THROW_ON_ERROR: с ним json_decode на испорченном тексте бросает исключение JsonException. Именованный аргумент flags: пропускает третий параметр, который здесь не нужен.

В PHP есть и свой формат: его записывают и читают функции serialize и unserialize. Он сохраняет объекты вместе с классом, но прочитать такую запись может только PHP.

Никогда не передавайте в unserialize данные от пользователя: такая строка может создать объект любого класса вашего проекта, и это известная дыра в безопасности. Чем JSON отличается от serialize — частый вопрос на собеседовании; для обмена данными берите JSON.

Частая ошибка

JSON прочитали без true, а значения берут как из массива:

PHP
$user = json_decode('{"name":"Anna"}');
echo $user['name'], "\n"; // ✗
TEXT
Fatal error: Uncaught Error: Cannot use object of type stdClass as array

json_decode без второго аргумента вернула объект stdClass, а к объекту нельзя обращаться через квадратные скобки. Исправление — json_decode('{"name":"Anna"}', true) или чтение через стрелку: $user->name.

Главное

  • json_encode записывает список JSON-массивом, а массив с другими ключами — объектом; флаг JSON_UNESCAPED_UNICODE оставляет кириллицу буквами.
  • json_decode($json, true) возвращает массивы, а без true — объекты stdClass, которые читают через ->.
  • На испорченном JSON json_decode без флагов возвращает null, а с флагом JSON_THROW_ON_ERROR бросает JsonException.
  • В unserialize нельзя передавать данные от пользователя; для обмена данными есть JSON.