JSON: json_encode и json_decode
Когда библиотеки обмениваются книгами, они пишут заявки на общем бланке. Каждая библиотека расставляет книги по-своему, но бланк понимает любая. Программам тоже нужен такой бланк: сайт на PHP отдаёт данные странице на JavaScript, мобильному приложению, программе на Python.
Как вы уже знаете, функция json_encode записывает массив в JSON — текстовый формат, которым программы обмениваются данными. В этом уроке разберём, как JSON устроен, как прочитать его обратно и что делать, если пришёл испорченный текст.
Массив в JSON
<?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}Давайте разберём, во что превращается каждое значение:
- Ассоциативный массив
$userстановится JSON-объектом в фигурных скобках, ключи — строками в двойных кавычках. - Список
['php', 'sql']становится JSON-массивом в квадратных скобках. - Число
30и логическоеfalseзаписываются как есть: в JSON есть свои числа,true,falseиnull. - Массив
[1 => 20]— не список, ведь его ключи не идут с нуля, поэтому он тоже становится объектом.
Кириллицу json_encode по умолчанию записывает кодами вроде \u0410. Программа на другой стороне прочитает их правильно, но человеку так читать неудобно.
Флаг JSON_UNESCAPED_UNICODE оставляет буквы как есть. Ещё один флаг, JSON_PRETTY_PRINT, раскладывает JSON по строкам с отступами — так его удобно разглядывать глазами.
Обратно из JSON: json_decode
Функция json_decode читает JSON и возвращает значение 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, и читают их через->; - с аргументом
trueJSON-объект становится ассоциативным массивом, и значения читают через['name'].
JSON-массив в обоих случаях становится списком PHP.
Чаще пишут json_decode($json, true): с массивами работать привычнее, и для них есть все функции из темы о массивах.
Здесь картинка с бланком перестаёт работать. На бланке есть только текст, числа и списки, а места для класса объекта нет. Поэтому json_decode не может вернуть ваш объект User — только безликий stdClass или массив.
Испорченный JSON
В JSON ключи обязательно стоят в двойных кавычках. Строка {name: "Anna"} — уже не JSON:
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, а значения берут как из массива:
$user = json_decode('{"name":"Anna"}');
echo $user['name'], "\n"; // ✗Fatal error: Uncaught Error: Cannot use object of type stdClass as arrayjson_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.