Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11468 глав6256 карточек справочника
Дерево руководства

Глава 152 · После Document и BSON-значений; при обновлении существующего PHP-приложения

MongoDB: переход с удалённых BSON-функций на Document

Шесть пар старого и нового кода: корзина, импорт, редактор, JSON сервиса, типизированное задание и публичная карточка.

Что сохранить при обновлении расширения

Вы обновили расширение mongodb, и прежний вызов MongoDB\BSON\fromPHP перестал существовать. При этом корзины, файлы заданий и ответы клиентам должны сохранить понятные значения. В этой главе шесть исторических функций сопоставлены с действующими методами: сначала маленькое действие, затем две полные программы на одних исходных данных.

Одна запись на границе обновления
  1. PHP-массив корзины или текст поставщика
  2. Старая среда 1.20: функция возвращает BSON string
  3. Новая среда 2.5: фабрика возвращает Document → нужный вид результата
  4. Сравниваем конкретные поля, BSON-типы и формат потребителя

Сначала современный минимальный вариант: корзина из двух полей становится Document; если прежний обработчик принимает бинарные данные, извлекаем raw BSON явно.

$cart = ['sku' => 'PEN-7', 'qty' => 2];
$document = MongoDB\BSON\Document::fromPHP($cart);
$bytes = (string) $document;
echo get_debug_type($bytes); // string

$cart — переменная с именованными значениями; => связывает имя поля со значением. :: вызывает статическую фабрику, которая создаёт Document. (string) просит его бинарное представление; это не вызов JSON-сериализации. Методы существующего объекта вызываются через ->. Предпосылки: Document, raw BSON и PHP-копия; типы цены, даты и ID — в главе BSON-значений.

Во всех программах этой главы выполняется локальное преобразование. MongoDB-сервер не вызывается и коллекции не создаются. Так можно проверить миграцию кодека без риска изменить рабочие данные. Реальное сохранение и чтение типизированных объектов показано в соседней главе.

Шесть функций и их современные замены

Историческая функцияСовременная операцияЧто проверить на границе
fromPHP($value)Document::fromPHP($value), при необходимости (string)Возвращается объект, а не прежняя бинарная строка
fromJSON($json)Document::fromJSON($json), при необходимости (string)Extended JSON распознаёт типы; обычный json_decode не эквивалентен
toPHP($bytes, $map)Document::fromBSON($bytes)->toPHP($map)Int64 и пустой документ не должны тихо потерять форму
toJSON($bytes)Document::fromBSON(...)->toCanonicalExtendedJSON() или ->toRelaxedExtendedJSON()Другая версия формата; старого метода toJSON нет
toCanonicalExtendedJSON($bytes)Document::fromBSON(...)->toCanonicalExtendedJSON()Сохраняются нужные BSON-маркеры
toRelaxedExtendedJSON($bytes)Document::fromBSON(...)->toRelaxedExtendedJSON()Не кодировать готовый JSON повторно; не раскрывать лишние поля

Фабрики Document появились в расширении 1.16, то есть переход можно начинать ещё в среде, где старые функции существуют. Но доказательство миграции должно включать запуск нового варианта в 2.x: совместимость на одном старом окружении не обнаружит случайно оставшийся удалённый вызов.

Два окружения и видимый результат

Каждая пара состоит из двух самостоятельных ZIP. В историческом имени есть суффикс -legacy; его среда PHP 8.4.27 NTS x64 и mongodb 1.20.1. В -modern используется PHP 8.5.11 и mongodb 2.5.5. Для переносимого запуска требуется PHP 8.4+ x64; версии расширений разделяются явно. Комплект не требует Composer и MongoDB-сервера.

Распакуйте выбранный архив, укажите полный путь нужного php.exe и его php.ini, выполните example.php и откройте result.html. README содержит точные требования, официальные загрузки и команды для двух отдельных CLI-каталогов. В result.json та же таблица; metrics.json хранит фактические версии, проверки, хеши и предупреждения.

Историческая программа получает E_DEPRECATED от реального вызова. Helper ловит только точное ожидаемое предупреждение этой функции, показывает его в HTML и сохраняет в metrics. Любое другое предупреждение не прячется; временный обработчик восстанавливается. Новый вариант сначала проверяет отсутствие всех шести удалённых функций и пользуется только Document.

canonical-job работает со своим новым временным файлом и убирает его в finally. Выходные HTML/JSON остаются. Серверный доступ не нужен ни одной из двенадцати программ: runtime.server прямо сообщает not contacted (codec-only).

Сохранить корзину в прежнем бинарном формате

После обновления расширения перестал работать обработчик сохранённых корзин: прежняя функция fromPHP исчезла, а файловый адаптер по-прежнему принимает бинарную строку. Нужно заменить способ получения этих байтов, сохранив названия полей, цену Decimal128 и ObjectId покупателя.

Исходные данные

Корзина CART-42: артикул PEN-7, две штуки по 1290.00 и покупатель 507f1f77bcf86cd799439011. Это маленькая подготовленная запись, а не загрузка всех заказов из базы. Цена уже проверена и выражена строкой, затем создан Decimal128.

Как меняется именно эта запись
  1. PHP-массив: sku=PEN-7, qty=2, price=Decimal128(1290.00)
  2. Старый fromPHP → BSON-байты / новый Document::fromPHP → Document
  3. В новом коде (string) Document → те же по назначению бинарные данные
  4. Декодирование: qty=2, price остаётся Decimal128, buyer остаётся ObjectId

Маленький исторический вызов: только mongodb 1.20

$cart = ['sku' => 'PEN-7', 'qty' => 2];
$bytes = MongoDB\BSON\fromPHP($cart);
echo get_debug_type($bytes); // string

Маленькая современная замена

$cart = ['sku' => 'PEN-7', 'qty' => 2];
$document = MongoDB\BSON\Document::fromPHP($cart);
$bytes = (string) $document;
echo get_debug_type($bytes); // string

В коротких фрагментах $bytes означает подготовленную BSON-запись сценария; полный вход и все действия находятся в каждой самостоятельной программе ниже. Исторический вызов закономерно выдаёт предупреждение deprecated.

Почему результат получается таким

Сериализация превращает именованные поля в последовательность байтов. fromPHP возвращал строку непосредственно. Document::fromPHP возвращает объект; явное (string) вызывает его __toString и даёт raw BSON. Здесь слово string означает контейнер байтов PHP, а не читаемый JSON или UTF-8-текст.

Не нужно менять всё приложение разом. На границе старого бинарного обработчика оставьте строковый результат, а внутри нового кода работайте с Document. Программа читает полученное представление обратно и проверяет деловые значения, классы цены и ID. Внутренний размер BSON и завершающий нулевой байт проверяются дополнительно; они не заменяют проверку содержимого.

Набор PHP-полей передаётся без промежуточного json_encode/json_decode. Предварительное превращение ObjectId в строку или Decimal128 в float изменило бы задачу ещё до вызова кодека. По таблице видно, какие типы должны пережить обновление.

Для экрана контроля используется Relaxed Extended JSON. Цена всё ещё находится в $numberDecimal: слово Relaxed не означает, что любой BSON-тип автоматически превратится в обычную строку API. Изменять публичную схему нужно отдельно и осознанно.

Сохранить корзину в прежнем бинарном формате — исторический API 1.20

ПолеПолученное значение
orderCART-42
skuPEN-7
qty2
price1290.00
buyer507f1f77bcf86cd799439011
Тип результата для прежнего обработчикаstring
Полная самостоятельная программа, README и архив
Сохранить корзину в прежнем бинарном формате — исторический API 1.20Самостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(true);
$events=[];$details=[];
$input=['order'=>'CART-42','sku'=>'PEN-7','qty'=>2,'price'=>new MongoDB\BSON\Decimal128('1290.00'),
    'buyer'=>new MongoDB\BSON\ObjectId('507f1f77bcf86cd799439011')];
$raw=bl120Deprecated('fromPHP',fn()=>MongoDB\BSON\fromPHP($input),$events);
$copy=bl120Deprecated('toPHP',fn()=>MongoDB\BSON\toPHP($raw,['root'=>'array','document'=>'array']),$events);
$preview=bl120Deprecated('toRelaxedExtendedJSON',fn()=>MongoDB\BSON\toRelaxedExtendedJSON($raw),$events);
$view=bl120Json($preview);
$rows=[['order',$copy['order']],['sku',$copy['sku']],['qty',$copy['qty']],
    ['price',(string)$copy['price']],['buyer',(string)$copy['buyer']],['Тип результата для прежнего обработчика',get_debug_type($raw)]];
$checks=['business_fields'=>$copy['order']==='CART-42' && $copy['sku']==='PEN-7' && $copy['qty']===2,
    'typed_values'=>$copy['price'] instanceof MongoDB\BSON\Decimal128 && $copy['buyer'] instanceof MongoDB\BSON\ObjectId,
    'binary_contract'=>is_string($raw) && unpack('Vlength',substr($raw,0,4))['length']===strlen($raw) && substr($raw,-1)==="\0",
    'preview_contract'=>$view['qty']===2 && $view['price']['$numberDecimal']==='1290.00'];
$details=['raw_bytes'=>strlen($raw),'preview'=>$view];
$checks['expected_deprecation_count']=count($events)===3;
bl120Report('mongodb120-legacy-php-cart-legacy', ['Поле', 'Полученное значение'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-php-cart-legacy: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-php-cart-legacy: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Историческая среда PHP 8.4.27 / mongodb 1.20.1; ожидаемые E_DEPRECATED видны в HTML и metrics.

Открыть фактический HTML результата · Скачать JSON таблицы

Сохранить корзину в прежнем бинарном формате — современный API

ПолеПолученное значение
orderCART-42
skuPEN-7
qty2
price1290.00
buyer507f1f77bcf86cd799439011
Тип результата для прежнего обработчикаstring
Полная самостоятельная программа, README и архив
Сохранить корзину в прежнем бинарном формате — современный APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(false);
$events=[];$details=[];
$input=['order'=>'CART-42','sku'=>'PEN-7','qty'=>2,'price'=>new MongoDB\BSON\Decimal128('1290.00'),
    'buyer'=>new MongoDB\BSON\ObjectId('507f1f77bcf86cd799439011')];
$document=MongoDB\BSON\Document::fromPHP($input);$raw=(string)$document;
$copy=MongoDB\BSON\Document::fromBSON($raw)->toPHP(['root'=>'array','document'=>'array']);
$preview=MongoDB\BSON\Document::fromBSON($raw)->toRelaxedExtendedJSON();
$view=bl120Json($preview);
$rows=[['order',$copy['order']],['sku',$copy['sku']],['qty',$copy['qty']],
    ['price',(string)$copy['price']],['buyer',(string)$copy['buyer']],['Тип результата для прежнего обработчика',get_debug_type($raw)]];
$checks=['business_fields'=>$copy['order']==='CART-42' && $copy['sku']==='PEN-7' && $copy['qty']===2,
    'typed_values'=>$copy['price'] instanceof MongoDB\BSON\Decimal128 && $copy['buyer'] instanceof MongoDB\BSON\ObjectId,
    'binary_contract'=>is_string($raw) && unpack('Vlength',substr($raw,0,4))['length']===strlen($raw) && substr($raw,-1)==="\0",
    'preview_contract'=>$view['qty']===2 && $view['price']['$numberDecimal']==='1290.00'];
$details=['raw_bytes'=>strlen($raw),'preview'=>$view];
$checks['expected_deprecation_count']=count($events)===0;
bl120Report('mongodb120-legacy-php-cart-modern', ['Поле', 'Полученное значение'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-php-cart-modern: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-php-cart-modern: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Действующий API Document: PHP 8.5.11 / mongodb 2.5.5.

Открыть фактический HTML результата · Скачать JSON таблицы

Границы и следующий шаг

Обе программы только кодируют одну запись в памяти. Они не сохраняют заказ на сервер и не проверяют права покупателя. Побайтовое равенство между любыми версиями сериализатора не заявлено; здесь проверяется нужная форма и значения. Новому сервису необязательно сохранять прежний строковый интерфейс, если его потребители уже умеют принимать Document.

Упражнение с ориентиром. Добавьте разрешённое поле delivery со значением pickup в $input. После обратного чтения должно получиться pickup; цена должна остаться объектом Decimal128. Если принимающий адаптер требует string, оставьте (string) после фабрики Document.

Реальная ситуация из обсуждения: Namespace MongoDB BSON and MongoDB Drivers are missing. После установки Composer-библиотеки разработчик не понимает доступность BSON-классов и подсказок IDE. Глава различает расширение, библиотеку и фактическую версию CLI; отсутствие удалённой функции в 2.x не лечится повторной установкой Composer. Это постановка проблемы, а правила и решения здесь проверяются по первичным источникам; код оригинальный.

Принять Extended JSON от поставщика

Поставщик присылает текст с ID, датой отправки и двоичной меткой. После миграции нужно получать реальные BSON-типы, а не сохранять служебные ключи $date/$oid как обычные вложенные поля. Некорректное сообщение должно быть отклонено до передачи в базу.

Исходные данные

В одной записи специально есть plain_id как строка и typed_id с оболочкой $oid. ship_at задан как $date/$numberLong со значением 0, label содержит два байта 00 01 в Base64. Рядом проверяется испорченная строка {broken.

Как меняется именно эта запись
  1. Текст: plain_id="507f…", typed_id={"$oid":"507f…"}
  2. Парсер Extended JSON различает обычную строку и BSON-маркер
  3. BSON: string / ObjectId / UTCDateTime / Binary
  4. PHP-копия показывает классы; испорченный JSON → исключение

Маленький исторический вызов: только mongodb 1.20

$json = '{"ship_at":{"$date":{"$numberLong":"0"}}}';
$raw = MongoDB\BSON\fromJSON($json);

Маленькая современная замена

$json = '{"ship_at":{"$date":{"$numberLong":"0"}}}';
$raw = (string) MongoDB\BSON\Document::fromJSON($json);

В коротких фрагментах $bytes означает подготовленную BSON-запись сценария; полный вход и все действия находятся в каждой самостоятельной программе ниже. Исторический вызов закономерно выдаёт предупреждение deprecated.

Почему результат получается таким

Обычный JSON знает строки, числа, логические значения, null, списки и объекты. Extended JSON дополнительно договаривается, как в тексте обозначать BSON-типы. Поэтому json_decode и fromJSON решают разные задачи: первый прочитает служебную оболочку как данные PHP, второй распознает её как тип BSON.

Одинаковая последовательность 24 символов не делает обычную строку ObjectId. В таблице plain_id и typed_id имеют одинаковое видимое значение, но разные PHP-классы. Это важно при последующем фильтре MongoDB: тип участвует в сравнении.

fromJSON раньше возвращал raw BSON. В современной ветке Document::fromJSON создаёт объект; преобразование в string сохраняет прежнюю границу передачи. Чтение через toPHP выполняется уже отдельно. В мини-фрагменте одинарные кавычки PHP защищают $date и $numberLong от подстановки PHP-переменных.

Подготовленная некорректная строка действительно передаётся в парсер. Ловится именно MongoDB\Driver\Exception\UnexpectedValueException, а не любой Throwable с заявлением об успехе. Остальные сбои должны остановить программу. В историческом варианте предупреждение deprecated фиксируется даже у вызова, который затем выбросил ожидаемое исключение.

В исходном коде используется Base64-строка AAE=. Результат bin2hex равен 0001 — это представление двух исходных байтов, а не текст из четырёх цифр в Binary. Base64 служит переносимой текстовой записью бинарных данных.

Принять Extended JSON от поставщика — исторический API 1.20

ПолеPHP-типЗначение
plain_idstring507f1f77bcf86cd799439011
typed_idMongoDB\BSON\ObjectId507f1f77bcf86cd799439011
ship_atMongoDB\BSON\UTCDateTime0
labelMongoDB\BSON\Binary0001
Некорректный JSONОтклонёнда
Полная самостоятельная программа, README и архив
Принять Extended JSON от поставщика — исторический API 1.20Самостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(true);
$events=[];$details=[];
$incoming='{"supplier":"WH-4","plain_id":"507f1f77bcf86cd799439011","typed_id":{"$oid":"507f1f77bcf86cd799439011"},"ship_at":{"$date":{"$numberLong":"0"}},"label":{"$binary":{"base64":"AAE=","subType":"00"}}}';
$raw=bl120Deprecated('fromJSON',fn()=>MongoDB\BSON\fromJSON($incoming),$events);
$copy=bl120Deprecated('toPHP',fn()=>MongoDB\BSON\toPHP($raw,['root'=>'array']),$events);
$badRejected=false;
try{bl120Deprecated('fromJSON',fn()=>MongoDB\BSON\fromJSON('{broken'),$events);}catch(MongoDB\Driver\Exception\UnexpectedValueException $error){$badRejected=true;}
$preview=bl120Deprecated('toJSON',fn()=>MongoDB\BSON\toJSON($raw),$events);
$previewData=bl120Json($preview);
$rows=[['plain_id',get_debug_type($copy['plain_id']),$copy['plain_id']],
    ['typed_id',get_debug_type($copy['typed_id']),(string)$copy['typed_id']],
    ['ship_at',get_debug_type($copy['ship_at']),(string)$copy['ship_at']],
    ['label',get_debug_type($copy['label']),bin2hex($copy['label']->getData())],
    ['Некорректный JSON','Отклонён',$badRejected?'да':'нет']];
$checks=['id_types_differ'=>is_string($copy['plain_id']) && $copy['typed_id'] instanceof MongoDB\BSON\ObjectId,
    'date_is_bson'=>$copy['ship_at'] instanceof MongoDB\BSON\UTCDateTime && (string)$copy['ship_at']==='0',
    'binary_unchanged'=>$copy['label']->getData()==="\0\1" && $copy['label']->getType()===MongoDB\BSON\Binary::TYPE_GENERIC,
    'bad_json_rejected'=>$badRejected,'preview_is_json'=>$previewData['supplier']==='WH-4'];
$details=['incoming'=>$incoming,'preview'=>$previewData];
$checks['expected_deprecation_count']=count($events)===4;
bl120Report('mongodb120-legacy-json-import-legacy', ['Поле', 'PHP-тип', 'Значение'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-json-import-legacy: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-json-import-legacy: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Историческая среда PHP 8.4.27 / mongodb 1.20.1; ожидаемые E_DEPRECATED видны в HTML и metrics.

Открыть фактический HTML результата · Скачать JSON таблицы

Принять Extended JSON от поставщика — современный API

ПолеPHP-типЗначение
plain_idstring507f1f77bcf86cd799439011
typed_idMongoDB\BSON\ObjectId507f1f77bcf86cd799439011
ship_atMongoDB\BSON\UTCDateTime0
labelMongoDB\BSON\Binary0001
Некорректный JSONОтклонёнда
Полная самостоятельная программа, README и архив
Принять Extended JSON от поставщика — современный APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(false);
$events=[];$details=[];
$incoming='{"supplier":"WH-4","plain_id":"507f1f77bcf86cd799439011","typed_id":{"$oid":"507f1f77bcf86cd799439011"},"ship_at":{"$date":{"$numberLong":"0"}},"label":{"$binary":{"base64":"AAE=","subType":"00"}}}';
$raw=(string)MongoDB\BSON\Document::fromJSON($incoming);
$copy=MongoDB\BSON\Document::fromBSON($raw)->toPHP(['root'=>'array']);
$badRejected=false;
try{MongoDB\BSON\Document::fromJSON('{broken');}catch(MongoDB\Driver\Exception\UnexpectedValueException $error){$badRejected=true;}
$preview=MongoDB\BSON\Document::fromBSON($raw)->toCanonicalExtendedJSON();
$previewData=bl120Json($preview);
$rows=[['plain_id',get_debug_type($copy['plain_id']),$copy['plain_id']],
    ['typed_id',get_debug_type($copy['typed_id']),(string)$copy['typed_id']],
    ['ship_at',get_debug_type($copy['ship_at']),(string)$copy['ship_at']],
    ['label',get_debug_type($copy['label']),bin2hex($copy['label']->getData())],
    ['Некорректный JSON','Отклонён',$badRejected?'да':'нет']];
$checks=['id_types_differ'=>is_string($copy['plain_id']) && $copy['typed_id'] instanceof MongoDB\BSON\ObjectId,
    'date_is_bson'=>$copy['ship_at'] instanceof MongoDB\BSON\UTCDateTime && (string)$copy['ship_at']==='0',
    'binary_unchanged'=>$copy['label']->getData()==="\0\1" && $copy['label']->getType()===MongoDB\BSON\Binary::TYPE_GENERIC,
    'bad_json_rejected'=>$badRejected,'preview_is_json'=>$previewData['supplier']==='WH-4'];
$details=['incoming'=>$incoming,'preview'=>$previewData];
$checks['expected_deprecation_count']=count($events)===0;
bl120Report('mongodb120-legacy-json-import-modern', ['Поле', 'PHP-тип', 'Значение'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-json-import-modern: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-json-import-modern: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Действующий API Document: PHP 8.5.11 / mongodb 2.5.5.

Открыть фактический HTML результата · Скачать JSON таблицы

Границы и следующий шаг

Распознавание BSON-типов не проверяет бизнес-схему, допустимость поставщика, диапазон дат или размер входного запроса. В примере маленькое фиксированное сообщение; реальные интеграции ограничивают размер и разрешённые поля до сохранения. Старый диагностический toJSON заменён Canonical, поэтому форма текстовой диагностики меняется, хотя распознанные значения остаются теми же.

Упражнение с ориентиром. Уберите оболочку $oid у typed_id. Ожидаемый тип станет string; проверка id_types_differ должна перестать проходить. Верните оболочку и добавьте обязательную проверку допустимого supplier до отправки данных в приложение.

Реальная ситуация из обсуждения: Mongo invalid document for insert: keys cannot begin with $date. В интеграции обозначение даты в Extended JSON было принято за обычные вложенные поля. Пример различает текстовую оболочку типа и реальный UTCDateTime; исторические ограничения имён полей из обсуждения не выдаются за текущие правила. Это постановка проблемы, а правила и решения здесь проверяются по первичным источникам; код оригинальный.

Отредактировать PHP-копию без потери формы

Редактор артикула получает PHP-массив из BSON, меняет sku и сохраняет запись обратно. Простая замена имени функции здесь может скрыть две потери: BSON Int64 превращается в меньший целочисленный тип, а пустой документ meta — в список. Нужно явно удержать схему редактируемой записи.

Исходные данные

Запись содержит sku=PEN-7, qty как BSON Int64 со значением 2 и meta как пустой документ {}. typeMap намеренно переводит root, document и array в PHP-массивы, чтобы показать неоднозначность. Единственное разрешённое изменение — sku=PEN-8.

Как меняется именно эта запись
  1. BSON: qty=Int64(2), meta={}
  2. toPHP с document=>array: meta=[]; в legacy x64 qty становится int
  3. Слепая повторная сериализация: форма meta потеряна
  4. Схема восстанавливает qty=Int64 и meta=(object)[]; меняется только sku

Маленький исторический вызов: только mongodb 1.20

$map = ['root' => 'array', 'document' => 'array'];
$copy = MongoDB\BSON\toPHP($bytes, $map);
// $bytes is the prepared BSON record shown in the full program.

Маленькая современная замена

$map = ['root' => 'array', 'document' => 'array'];
$copy = MongoDB\BSON\Document::fromBSON($bytes)->toPHP($map);
// $bytes is the prepared BSON record shown in the full program.

В коротких фрагментах $bytes означает подготовленную BSON-запись сценария; полный вход и все действия находятся в каждой самостоятельной программе ниже. Исторический вызов закономерно выдаёт предупреждение deprecated.

Почему результат получается таким

Карта типов typeMap выбирает PHP-контейнеры после чтения. Массив удобен для $copy["sku"], но пустой массив больше не помнит, был ли исходник {} или []. Поэтому преобразование «всё в array, затем обратно» нельзя считать универсальным сохранением BSON-схемы.

В историческом toPHP на нашем 64-битном стенде Int64(2) становится native int. При повторной сериализации маленькое PHP-число кодируется как int32. Document::toPHP возвращает Int64-объект для BSON int64 даже на x64. Это полезное сохранение типа, но существующий код, который требует is_int($qty), тоже нужно пересмотреть.

Первая сериализация $copy намеренно демонстрирует неверный для этой схемы результат. Вторая добавляет явные правила: new Int64((string)$copy["qty"]) и (object)[] для meta. Приведение к строке здесь не выполняет денежную арифметику и не округляет float: исходник — известное целое. Строка передаётся в конструктор нужного BSON-типа.

Правило meta=(object)[] допустимо именно потому, что вход этого упражнения заранее содержит пустой документ. Нельзя применять его ко всем meta: так можно стереть реальные поля. Для произвольной записи сохраняйте различие документа и массива с самого начала и проверяйте входную схему.

Canonical делает ширину чисел видимой: $numberInt и $numberLong различаются даже при одинаковой цифре 2. Обычная распечатка числа не выявила бы эту потерю. Полная программа выводит обе формы и проверяет восстановленный документ после изменения артикула.

Отредактировать PHP-копию без потери формы — исторический API 1.20

НаблюдениеРезультат
PHP-тип qty после чтенияint
Без исправления: BSON-маркер qty$numberInt
Без исправления: форма metaarray
После исправления: BSON-маркер qty$numberLong
После исправления: PHP-форма metastdClass
Изменённый артикулPEN-8
Полная самостоятельная программа, README и архив
Отредактировать PHP-копию без потери формы — исторический API 1.20Самостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(true);
$events=[];$details=[];
$incoming='{"sku":"PEN-7","qty":{"$numberLong":"2"},"meta":{}}';
$map=['root'=>'array','document'=>'array','array'=>'array'];
$raw=bl120Deprecated('fromJSON',fn()=>MongoDB\BSON\fromJSON($incoming),$events);
$copy=bl120Deprecated('toPHP',fn()=>MongoDB\BSON\toPHP($raw,$map),$events);
$decodedType=get_debug_type($copy['qty']);
$badRaw=bl120Deprecated('fromPHP',fn()=>MongoDB\BSON\fromPHP($copy),$events);
$badJson=bl120Deprecated('toCanonicalExtendedJSON',fn()=>MongoDB\BSON\toCanonicalExtendedJSON($badRaw),$events);
$badData=bl120Json($badJson);
// The input schema says qty is Int64 and meta is an empty document.
$copy['qty']=new MongoDB\BSON\Int64((string)$copy['qty']);
$copy['meta']=(object)[];
$copy['sku']='PEN-8';
$fixedRaw=bl120Deprecated('fromPHP',fn()=>MongoDB\BSON\fromPHP($copy),$events);
$fixedJson=bl120Deprecated('toCanonicalExtendedJSON',fn()=>MongoDB\BSON\toCanonicalExtendedJSON($fixedRaw),$events);
$fixedData=json_decode($fixedJson,false,512,JSON_THROW_ON_ERROR);
$rows=[['PHP-тип qty после чтения',$decodedType],['Без исправления: BSON-маркер qty',array_key_first($badData['qty'])],
    ['Без исправления: форма meta',is_array(json_decode($badJson,false,512,JSON_THROW_ON_ERROR)->meta)?'array':'object'],
    ['После исправления: BSON-маркер qty',array_key_first((array)$fixedData->qty)],['После исправления: PHP-форма meta',get_debug_type($fixedData->meta)],
    ['Изменённый артикул',$fixedData->sku]];
$checks=['decoded_contract'=>$decodedType==='int',
    'empty_document_loss_visible'=>is_array(json_decode($badJson,false,512,JSON_THROW_ON_ERROR)->meta),
    'int64_restored'=>isset($fixedData->qty->{'$numberLong'}) && $fixedData->qty->{'$numberLong'}==='2',
    'empty_document_restored'=>$fixedData->meta instanceof stdClass && (array)$fixedData->meta===[],
    'edit_applied'=>$fixedData->sku==='PEN-8'];
$details=['unsafe_copy_json'=>$badJson,'corrected_json'=>$fixedJson];
$checks['expected_deprecation_count']=count($events)===6;
bl120Report('mongodb120-legacy-php-copy-legacy', ['Наблюдение', 'Результат'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-php-copy-legacy: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-php-copy-legacy: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Историческая среда PHP 8.4.27 / mongodb 1.20.1; ожидаемые E_DEPRECATED видны в HTML и metrics.

Открыть фактический HTML результата · Скачать JSON таблицы

Отредактировать PHP-копию без потери формы — современный API

НаблюдениеРезультат
PHP-тип qty после чтенияMongoDB\BSON\Int64
Без исправления: BSON-маркер qty$numberLong
Без исправления: форма metaarray
После исправления: BSON-маркер qty$numberLong
После исправления: PHP-форма metastdClass
Изменённый артикулPEN-8
Полная самостоятельная программа, README и архив
Отредактировать PHP-копию без потери формы — современный APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(false);
$events=[];$details=[];
$incoming='{"sku":"PEN-7","qty":{"$numberLong":"2"},"meta":{}}';
$map=['root'=>'array','document'=>'array','array'=>'array'];
$raw=(string)MongoDB\BSON\Document::fromJSON($incoming);
$copy=MongoDB\BSON\Document::fromBSON($raw)->toPHP($map);
$decodedType=get_debug_type($copy['qty']);
$badRaw=(string)MongoDB\BSON\Document::fromPHP($copy);
$badJson=MongoDB\BSON\Document::fromBSON($badRaw)->toCanonicalExtendedJSON();
$badData=bl120Json($badJson);
// The input schema says qty is Int64 and meta is an empty document.
$copy['qty']=new MongoDB\BSON\Int64((string)$copy['qty']);
$copy['meta']=(object)[];
$copy['sku']='PEN-8';
$fixedRaw=(string)MongoDB\BSON\Document::fromPHP($copy);
$fixedJson=MongoDB\BSON\Document::fromBSON($fixedRaw)->toCanonicalExtendedJSON();
$fixedData=json_decode($fixedJson,false,512,JSON_THROW_ON_ERROR);
$rows=[['PHP-тип qty после чтения',$decodedType],['Без исправления: BSON-маркер qty',array_key_first($badData['qty'])],
    ['Без исправления: форма meta',is_array(json_decode($badJson,false,512,JSON_THROW_ON_ERROR)->meta)?'array':'object'],
    ['После исправления: BSON-маркер qty',array_key_first((array)$fixedData->qty)],['После исправления: PHP-форма meta',get_debug_type($fixedData->meta)],
    ['Изменённый артикул',$fixedData->sku]];
$checks=['decoded_contract'=>$decodedType==='MongoDB\\BSON\\Int64',
    'empty_document_loss_visible'=>is_array(json_decode($badJson,false,512,JSON_THROW_ON_ERROR)->meta),
    'int64_restored'=>isset($fixedData->qty->{'$numberLong'}) && $fixedData->qty->{'$numberLong'}==='2',
    'empty_document_restored'=>$fixedData->meta instanceof stdClass && (array)$fixedData->meta===[],
    'edit_applied'=>$fixedData->sku==='PEN-8'];
$details=['unsafe_copy_json'=>$badJson,'corrected_json'=>$fixedJson];
$checks['expected_deprecation_count']=count($events)===0;
bl120Report('mongodb120-legacy-php-copy-modern', ['Наблюдение', 'Результат'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-php-copy-modern: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-php-copy-modern: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Действующий API Document: PHP 8.5.11 / mongodb 2.5.5.

Открыть фактический HTML результата · Скачать JSON таблицы

Границы и следующий шаг

Не применяйте это упражнение как миграцию всей коллекции. Оно исправляет одну известную запись без соединения с базой и не обещает универсального восстановления типов уже испорченных данных. Для объектов предметной области и ограничений bsonUnserialize см. соседнюю главу о сопоставлении объектов.

Упражнение с ориентиром. Сохраните вложенные документы как object: используйте document=>"object" и не переприсваивайте meta. Пустой meta должен остаться stdClass. Обработку qty проверьте отдельно в двух средах; отличие исторического native int от современного Int64 никуда не исчезает.

Обновить формат ответа старого сервиса

Старый сервис формирует JSON через toJSON. После обновления функция удалена, а клиент ожидает дату в старом поле $date и двоичные данные рядом с $type. Нужно договориться о новом формате и проверить потребителя, а не переименовать вызов и надеяться на идентичную строку.

Исходные данные

Задание LABEL-9 содержит дату эпохи 0 и три байта ABC. Второй отдельный образец содержит NAN — неопределённый результат датчика. Он нужен для проверки границы формата; это не допустимая цена магазина.

Как меняется именно эта запись
  1. BSON Date(0) + Binary(ABC)
  2. Исторический toJSON: $date=0, $binary="QUJD", отдельный $type
  3. Новый Canonical: вложенные $numberLong и $binary.base64/subType
  4. Проверка обычным JSON-парсером: старый nan невалиден; новый $numberDouble="NaN" валиден

Маленький исторический вызов: только mongodb 1.20

$json = MongoDB\BSON\toJSON($bytes);
// $bytes is the prepared BSON record shown below.

Маленькая современная замена

$json = MongoDB\BSON\Document::fromBSON($bytes)->toCanonicalExtendedJSON();
// $bytes is the prepared BSON record shown below.

В коротких фрагментах $bytes означает подготовленную BSON-запись сценария; полный вход и все действия находятся в каждой самостоятельной программе ниже. Исторический вызов закономерно выдаёт предупреждение deprecated.

Почему результат получается таким

Имя toJSON не означало любой современный формат JSON MongoDB. Функция выдавала Legacy Extended JSON, известный как v1 strict. У Document нет метода с таким именем. Выбор Canonical или Relaxed меняет договор с читающим клиентом.

В новой программе выбран Canonical, поскольку получатель должен восстанавливать дату и бинарный тип. Таблица сводит обе формы к одной дате в миллисекундах и одному Base64. Это проверка смысла, а не утверждение о совместимости старого пути $date с новым $date.$numberLong.

Legacy-формат не определяет корректное JSON-представление NaN/Infinity. Вторая фактическая сериализация возвращает старый литерал nan; json_decode с JSON_THROW_ON_ERROR отказывает. Современный Canonical оборачивает значение в $numberDouble, и JSON-парсер принимает текст. Деловая допустимость такого значения всё равно проверяется отдельно.

Не заменяйте ключи в JSON регулярным выражением: одинаковый текст может оказаться пользовательским содержимым, вложенным объектом или другим типом. Декодирование по формату и явная новая схема надёжнее. Если внешний потребитель менять нельзя, нужен отдельно согласованный адаптер v1 и его набор проверок; эта глава не выдаёт такой адаптер за готовый.

Обновить формат ответа старого сервиса — исторический API 1.20

Что проверилиРезультат
ФорматLegacy Extended JSON v1
Дата в миллисекундах0
Бинарные данные в Base64QUJD
json_decode принимает результат с NaNнет
Полная самостоятельная программа, README и архив
Обновить формат ответа старого сервиса — исторический API 1.20Самостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(true);
$events=[];$details=[];
$input=['job'=>'LABEL-9','sent_at'=>new MongoDB\BSON\UTCDateTime(0),
    'signature'=>new MongoDB\BSON\Binary('ABC',MongoDB\BSON\Binary::TYPE_GENERIC)];
$raw=bl120Deprecated('fromPHP',fn()=>MongoDB\BSON\fromPHP($input),$events);
$text=bl120Deprecated('toJSON',fn()=>MongoDB\BSON\toJSON($raw),$events);
$data=bl120Json($text);
$nanRaw=bl120Deprecated('fromPHP',fn()=>MongoDB\BSON\fromPHP(['sensor'=>NAN]),$events);
$nanText=bl120Deprecated('toJSON',fn()=>MongoDB\BSON\toJSON($nanRaw),$events);
$validNan=true;$decodedNan=null;
try{$decodedNan=bl120Json($nanText);}catch(JsonException $error){$validNan=false;}
$rows=[['Формат','Legacy Extended JSON v1'],
    ['Дата в миллисекундах',(string)$data['sent_at']['$date']],['Бинарные данные в Base64',$data['signature']['$binary']],
    ['json_decode принимает результат с NaN',$validNan?'да':'нет']];
$checks=['date_preserved'=>(string)$data['sent_at']['$date']==='0','binary_preserved'=>$data['signature']['$binary']==='QUJD',
    'expected_nonfinite_behavior'=>$validNan===false];
$details=['finite_payload'=>$text,'nonfinite_payload'=>$nanText];
$checks['expected_deprecation_count']=count($events)===4;
bl120Report('mongodb120-legacy-legacy-json-legacy', ['Что проверили', 'Результат'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-legacy-json-legacy: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-legacy-json-legacy: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Историческая среда PHP 8.4.27 / mongodb 1.20.1; ожидаемые E_DEPRECATED видны в HTML и metrics.

Открыть фактический HTML результата · Скачать JSON таблицы

Обновить формат ответа старого сервиса — современный API

Что проверилиРезультат
ФорматCanonical Extended JSON v2
Дата в миллисекундах0
Бинарные данные в Base64QUJD
json_decode принимает результат с NaNда
Полная самостоятельная программа, README и архив
Обновить формат ответа старого сервиса — современный APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(false);
$events=[];$details=[];
$input=['job'=>'LABEL-9','sent_at'=>new MongoDB\BSON\UTCDateTime(0),
    'signature'=>new MongoDB\BSON\Binary('ABC',MongoDB\BSON\Binary::TYPE_GENERIC)];
$raw=(string)MongoDB\BSON\Document::fromPHP($input);
$text=MongoDB\BSON\Document::fromBSON($raw)->toCanonicalExtendedJSON();
$data=bl120Json($text);
$nanRaw=(string)MongoDB\BSON\Document::fromPHP(['sensor'=>NAN]);
$nanText=MongoDB\BSON\Document::fromBSON($nanRaw)->toCanonicalExtendedJSON();
$validNan=true;$decodedNan=null;
try{$decodedNan=bl120Json($nanText);}catch(JsonException $error){$validNan=false;}
$rows=[['Формат','Canonical Extended JSON v2'],
    ['Дата в миллисекундах',$data['sent_at']['$date']['$numberLong']],['Бинарные данные в Base64',$data['signature']['$binary']['base64']],
    ['json_decode принимает результат с NaN',$validNan?'да':'нет']];
$checks=['date_preserved'=>$data['sent_at']['$date']['$numberLong']==='0','binary_preserved'=>$data['signature']['$binary']['base64']==='QUJD',
    'expected_nonfinite_behavior'=>$validNan===true];
$details=['finite_payload'=>$text,'nonfinite_payload'=>$nanText];
$checks['expected_deprecation_count']=count($events)===0;
bl120Report('mongodb120-legacy-legacy-json-modern', ['Что проверили', 'Результат'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-legacy-json-modern: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-legacy-json-modern: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Действующий API Document: PHP 8.5.11 / mongodb 2.5.5.

Открыть фактический HTML результата · Скачать JSON таблицы

Границы и следующий шаг

Canonical-оболочка с NaN означает успешно переданный специальный тип, а не корректную цену или исправный датчик. Мы не отправляем запросы реальному сервису и не проверяем все возможные старые потребители. По одному совпавшему значению даты нельзя объявлять API обратно совместимым.

Упражнение с ориентиром. Поменяйте дату на 1000 миллисекунд. В старой форме будет число 1000; в Canonical — строка "1000" внутри $numberLong. Обновите читающую сторону для нового пути и проверьте, что время не превратилось в секунды.

Передать задание с сохранением BSON-типов

Фоновый обработчик принимает задание в текстовом файле. После обновления кодека он должен различать int32, int64, double и Decimal128, хотя три первых поля визуально равны двум. Подходит Canonical Extended JSON, а не вывод чисел без обозначения типа.

Исходные данные

Задание STOCK-5: small=int32(2), wide=int64(2), ratio=double(2.0), price=Decimal128(12.30). Вход подготовлен как Extended JSON, чтобы ширина и десятичная форма не зависели от догадок PHP.

Как меняется именно эта запись
  1. Подготовленные четыре BSON-типа
  2. Canonical: $numberInt / $numberLong / $numberDouble / $numberDecimal
  3. Запись UTF-8 в собственный новый файл → чтение текста
  4. Парсер Extended JSON → сравнение BSON конкретной записи → удаление своего файла

Маленький исторический вызов: только mongodb 1.20

$text = MongoDB\BSON\toCanonicalExtendedJSON($bytes);
// Transfer $text as UTF-8; do not json_encode it again.

Маленькая современная замена

$text = MongoDB\BSON\Document::fromBSON($bytes)->toCanonicalExtendedJSON();
// Transfer $text as UTF-8; do not json_encode it again.

В коротких фрагментах $bytes означает подготовленную BSON-запись сценария; полный вход и все действия находятся в каждой самостоятельной программе ниже. Исторический вызов закономерно выдаёт предупреждение deprecated.

Почему результат получается таким

Canonical предпочитает явные типовые оболочки краткости. $numberLong содержит строку "2", а не JSON-число, поэтому читающая сторона знает, какой BSON-тип восстановить. Значение 12.30 не проходит через PHP float.

Программа действительно создаёт файл, пишет текст, читает его и декодирует. Режим x+b требует нового имени; случайный суффикс снижает вероятность коллизии, а режим не позволяет затереть существующий файл. Флаг владения ставится только после успешного fopen.

finally закрывает свой дескриптор и удаляет только созданный путь. result.html и result.json остаются для просмотра. Эта уборка относится к учебному файлу передачи, а не к пользователю и его каталогу документов.

Сравнение restored===raw доказывает совпадение байтов этой подготовленной записи после текстового круга. Canonical предназначен для сохранения типов, но не следует превращать один тест в обещание идентичной строки JSON или любой бинарной нормализации между библиотеками.

Не вызывайте json_encode над уже полученным Canonical-текстом перед записью: это упакует JSON-объект как JSON-строку с экранированными кавычками. Текст уже готов к передаче как один документ в согласованном формате.

Передать задание с сохранением BSON-типов — исторический API 1.20

ПолеМаркерЗначение
small$numberInt2
wide$numberLong2
ratio$numberDouble2.0
price$numberDecimal12.30
Обратное преобразованиеТе же BSON-байтыда
Полная самостоятельная программа, README и архив
Передать задание с сохранением BSON-типов — исторический API 1.20Самостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(true);
$events=[];$details=[];
$incoming='{"job":"STOCK-5","small":{"$numberInt":"2"},"wide":{"$numberLong":"2"},"ratio":{"$numberDouble":"2.0"},"price":{"$numberDecimal":"12.30"}}';
$raw=bl120Deprecated('fromJSON',fn()=>MongoDB\BSON\fromJSON($incoming),$events);
$text=bl120Deprecated('toCanonicalExtendedJSON',fn()=>MongoDB\BSON\toCanonicalExtendedJSON($raw),$events);
$path=__DIR__.'/transfer-'.bin2hex(random_bytes(8)).'.json';$owned=false;$handle=null;
try{
    $handle=fopen($path,'x+b');if($handle===false){throw new RuntimeException('Cannot create own transfer file');}$owned=true;
    if(fwrite($handle,$text)!==strlen($text)){throw new RuntimeException('Incomplete write');}
    rewind($handle);$read=stream_get_contents($handle);if($read===false){throw new RuntimeException('Cannot read transfer');}
    fclose($handle);$handle=null;
    $restored=bl120Deprecated('fromJSON',fn()=>MongoDB\BSON\fromJSON($read),$events);
}finally{
    if(is_resource($handle)){fclose($handle);}
    if($owned && !unlink($path)){throw new RuntimeException('Cannot remove own transfer');}
}
$data=bl120Json($text);
$rows=[['small',array_key_first($data['small']),$data['small']['$numberInt']],
    ['wide',array_key_first($data['wide']),$data['wide']['$numberLong']],
    ['ratio',array_key_first($data['ratio']),$data['ratio']['$numberDouble']],
    ['price',array_key_first($data['price']),$data['price']['$numberDecimal']],
    ['Обратное преобразование','Те же BSON-байты',$restored===$raw?'да':'нет']];
$checks=['integer_width_preserved'=>isset($data['small']['$numberInt'],$data['wide']['$numberLong']),
    'double_preserved'=>$data['ratio']['$numberDouble']==='2.0',
    'decimal_preserved'=>$data['price']['$numberDecimal']==='12.30',
    'exact_fixture_roundtrip'=>$restored===$raw,'own_file_removed'=>!file_exists($path)];
$details=['canonical'=>$data];
$checks['expected_deprecation_count']=count($events)===3;
bl120Report('mongodb120-legacy-canonical-job-legacy', ['Поле', 'Маркер', 'Значение'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-canonical-job-legacy: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-canonical-job-legacy: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Историческая среда PHP 8.4.27 / mongodb 1.20.1; ожидаемые E_DEPRECATED видны в HTML и metrics.

Открыть фактический HTML результата · Скачать JSON таблицы

Передать задание с сохранением BSON-типов — современный API

ПолеМаркерЗначение
small$numberInt2
wide$numberLong2
ratio$numberDouble2.0
price$numberDecimal12.30
Обратное преобразованиеТе же BSON-байтыда
Полная самостоятельная программа, README и архив
Передать задание с сохранением BSON-типов — современный APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(false);
$events=[];$details=[];
$incoming='{"job":"STOCK-5","small":{"$numberInt":"2"},"wide":{"$numberLong":"2"},"ratio":{"$numberDouble":"2.0"},"price":{"$numberDecimal":"12.30"}}';
$raw=(string)MongoDB\BSON\Document::fromJSON($incoming);
$text=MongoDB\BSON\Document::fromBSON($raw)->toCanonicalExtendedJSON();
$path=__DIR__.'/transfer-'.bin2hex(random_bytes(8)).'.json';$owned=false;$handle=null;
try{
    $handle=fopen($path,'x+b');if($handle===false){throw new RuntimeException('Cannot create own transfer file');}$owned=true;
    if(fwrite($handle,$text)!==strlen($text)){throw new RuntimeException('Incomplete write');}
    rewind($handle);$read=stream_get_contents($handle);if($read===false){throw new RuntimeException('Cannot read transfer');}
    fclose($handle);$handle=null;
    $restored=(string)MongoDB\BSON\Document::fromJSON($read);
}finally{
    if(is_resource($handle)){fclose($handle);}
    if($owned && !unlink($path)){throw new RuntimeException('Cannot remove own transfer');}
}
$data=bl120Json($text);
$rows=[['small',array_key_first($data['small']),$data['small']['$numberInt']],
    ['wide',array_key_first($data['wide']),$data['wide']['$numberLong']],
    ['ratio',array_key_first($data['ratio']),$data['ratio']['$numberDouble']],
    ['price',array_key_first($data['price']),$data['price']['$numberDecimal']],
    ['Обратное преобразование','Те же BSON-байты',$restored===$raw?'да':'нет']];
$checks=['integer_width_preserved'=>isset($data['small']['$numberInt'],$data['wide']['$numberLong']),
    'double_preserved'=>$data['ratio']['$numberDouble']==='2.0',
    'decimal_preserved'=>$data['price']['$numberDecimal']==='12.30',
    'exact_fixture_roundtrip'=>$restored===$raw,'own_file_removed'=>!file_exists($path)];
$details=['canonical'=>$data];
$checks['expected_deprecation_count']=count($events)===0;
bl120Report('mongodb120-legacy-canonical-job-modern', ['Поле', 'Маркер', 'Значение'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-canonical-job-modern: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-canonical-job-modern: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Действующий API Document: PHP 8.5.11 / mongodb 2.5.5.

Открыть фактический HTML результата · Скачать JSON таблицы

Границы и следующий шаг

Это одна запись и один небольшой файл, не поток произвольного размера и не резервная копия MongoDB. Для очереди нужны подтверждения доставки, лимиты, доступ к файлам и проверка схемы задания; успешный кодек этих задач не выполняет. Несколько BSON-документов нельзя склеить и передать функции, ожидающей ровно один.

Упражнение с ориентиром. Добавьте wide2 с {"$numberLong":"3"} и проверьте его отдельный маркер после чтения. Затем замените Canonical на Relaxed только в экспериментальной копии: текст станет короче, но восстановление ширины маленьких целых уже нельзя считать гарантированным.

Отдать экрану нужные поля один раз

Экран товара должен получить название, остаток, цену и дату обновления. Старый PHP-код дополнительно кодировал уже готовый JSON через json_encode, из-за чего JavaScript получал строку вместо объекта. При миграции устраняем двойное кодирование и явно выбираем публичные поля.

Исходные данные

Товар Notebook: остаток 2, цена Decimal128(12.30), дата 0. В исходной записи также есть supplier_cost=8.00. В публичное представление поле себестоимости не включено.

Как меняется именно эта запись
  1. Внутренняя запись содержит 5 полей
  2. Явный публичный набор: name, stock, price, updated
  3. Relaxed Extended JSON: stock=2, price.$numberDecimal="12.30"
  4. Одно декодирование → объект; двойное кодирование → строка

Маленький исторический вызов: только mongodb 1.20

$text = MongoDB\BSON\toRelaxedExtendedJSON($bytes);
echo $text; // already JSON; do not wrap in json_encode($text)

Маленькая современная замена

$text = MongoDB\BSON\Document::fromBSON($bytes)->toRelaxedExtendedJSON();
echo $text; // already JSON; do not wrap in json_encode($text)

В коротких фрагментах $bytes означает подготовленную BSON-запись сценария; полный вход и все действия находятся в каждой самостоятельной программе ниже. Исторический вызов закономерно выдаёт предупреждение deprecated.

Почему результат получается таким

Сначала формируется $view с точным перечнем полей, которые нужны экрану. Выбор формата JSON сам по себе не скрывает внутренние данные. В реальном запросе разумно также ограничить projection, чтобы не получать лишнее с сервера.

Relaxed сохраняет простые JSON-числа там, где это предусмотрено форматом. Остаток 2 читается как int, но цена остаётся оболочкой $numberDecimal, а дата — $date. Для обычного HTTP API часто удобнее отдельная собственная схема price:"12.30", updated:"…"; это осознанное преобразование, не автоматическое свойство BSON.

Строка $text уже содержит JSON-документ. Повторный json_encode($text) создаёт JSON-строку. После одного json_decode результатом становится string, поэтому клиент не находит .name как свойство объекта. Контрольная строка таблицы показывает это отличие без реального HTTP-запроса.

Для сравнения программа получает Canonical той же записи: stock имеет $numberInt. Эта разница объясняет, почему Relaxed удобен для просмотра, но не является выбором по умолчанию для сохранения ширины всех чисел.

Дата в демонстрации находится в допустимом для ISO-представления диапазоне и не имеет ненулевых миллисекунд. Поэтому получается 1970-01-01T00:00:00Z. Для дат до 1970 или после 9999 Relaxed использует другую форму; не стройте парсер, предполагающий всегда одну строку $date.

Отдать экрану нужные поля один раз — исторический API 1.20

НаблюдениеРезультат
nameNotebook
PHP-тип остатка после json_decodeint
Цена: десятичная строка12.30
Дата изменения UTC1970-01-01T00:00:00Z
Себестоимость видна клиентунет
Двойное кодирование даёт типstring
Полная самостоятельная программа, README и архив
Отдать экрану нужные поля один раз — исторический API 1.20Самостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(true);
$events=[];$details=[];
$record=['name'=>'Notebook','stock'=>2,'price'=>new MongoDB\BSON\Decimal128('12.30'),
    'updated'=>new MongoDB\BSON\UTCDateTime(0),'supplier_cost'=>'8.00'];
// A deliberate public field list, independent of the database object's representation.
$view=['name'=>$record['name'],'stock'=>$record['stock'],'price'=>$record['price'],'updated'=>$record['updated']];
$raw=bl120Deprecated('fromPHP',fn()=>MongoDB\BSON\fromPHP($view),$events);
$text=bl120Deprecated('toRelaxedExtendedJSON',fn()=>MongoDB\BSON\toRelaxedExtendedJSON($raw),$events);
$canonical=bl120Deprecated('toCanonicalExtendedJSON',fn()=>MongoDB\BSON\toCanonicalExtendedJSON($raw),$events);
$parsed=bl120Json($text);$canonicalData=bl120Json($canonical);
$doubleEncoded=json_encode($text,JSON_THROW_ON_ERROR);
$rows=[['name',$parsed['name']],['PHP-тип остатка после json_decode',get_debug_type($parsed['stock'])],
    ['Цена: десятичная строка',$parsed['price']['$numberDecimal']],['Дата изменения UTC',$parsed['updated']['$date']],
    ['Себестоимость видна клиенту',array_key_exists('supplier_cost',$parsed)?'да':'нет'],
    ['Двойное кодирование даёт тип',get_debug_type(json_decode($doubleEncoded,true,512,JSON_THROW_ON_ERROR))]];
$checks=['public_fields_only'=>array_keys($parsed)===['name','stock','price','updated'],
    'plain_integer'=>$parsed['stock']===2,'decimal_not_float'=>$parsed['price']['$numberDecimal']==='12.30',
    'date_iso'=>$parsed['updated']['$date']==='1970-01-01T00:00:00Z',
    'double_encoding_detected'=>is_string(json_decode($doubleEncoded,true,512,JSON_THROW_ON_ERROR)),
    'canonical_contrast'=>$canonicalData['stock']['$numberInt']==='2'];
$details=['relaxed'=>$parsed,'canonical'=>$canonicalData];
$checks['expected_deprecation_count']=count($events)===3;
bl120Report('mongodb120-legacy-relaxed-screen-legacy', ['Наблюдение', 'Результат'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-relaxed-screen-legacy: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-relaxed-screen-legacy: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Историческая среда PHP 8.4.27 / mongodb 1.20.1; ожидаемые E_DEPRECATED видны в HTML и metrics.

Открыть фактический HTML результата · Скачать JSON таблицы

Отдать экрану нужные поля один раз — современный API

НаблюдениеРезультат
nameNotebook
PHP-тип остатка после json_decodeint
Цена: десятичная строка12.30
Дата изменения UTC1970-01-01T00:00:00Z
Себестоимость видна клиентунет
Двойное кодирование даёт типstring
Полная самостоятельная программа, README и архив
Отдать экрану нужные поля один раз — современный APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/bl120-helper.php';
$runtime=bl120Require(false);
$events=[];$details=[];
$record=['name'=>'Notebook','stock'=>2,'price'=>new MongoDB\BSON\Decimal128('12.30'),
    'updated'=>new MongoDB\BSON\UTCDateTime(0),'supplier_cost'=>'8.00'];
// A deliberate public field list, independent of the database object's representation.
$view=['name'=>$record['name'],'stock'=>$record['stock'],'price'=>$record['price'],'updated'=>$record['updated']];
$raw=(string)MongoDB\BSON\Document::fromPHP($view);
$text=MongoDB\BSON\Document::fromBSON($raw)->toRelaxedExtendedJSON();
$canonical=MongoDB\BSON\Document::fromBSON($raw)->toCanonicalExtendedJSON();
$parsed=bl120Json($text);$canonicalData=bl120Json($canonical);
$doubleEncoded=json_encode($text,JSON_THROW_ON_ERROR);
$rows=[['name',$parsed['name']],['PHP-тип остатка после json_decode',get_debug_type($parsed['stock'])],
    ['Цена: десятичная строка',$parsed['price']['$numberDecimal']],['Дата изменения UTC',$parsed['updated']['$date']],
    ['Себестоимость видна клиенту',array_key_exists('supplier_cost',$parsed)?'да':'нет'],
    ['Двойное кодирование даёт тип',get_debug_type(json_decode($doubleEncoded,true,512,JSON_THROW_ON_ERROR))]];
$checks=['public_fields_only'=>array_keys($parsed)===['name','stock','price','updated'],
    'plain_integer'=>$parsed['stock']===2,'decimal_not_float'=>$parsed['price']['$numberDecimal']==='12.30',
    'date_iso'=>$parsed['updated']['$date']==='1970-01-01T00:00:00Z',
    'double_encoding_detected'=>is_string(json_decode($doubleEncoded,true,512,JSON_THROW_ON_ERROR)),
    'canonical_contrast'=>$canonicalData['stock']['$numberInt']==='2'];
$details=['relaxed'=>$parsed,'canonical'=>$canonicalData];
$checks['expected_deprecation_count']=count($events)===0;
bl120Report('mongodb120-legacy-relaxed-screen-modern', ['Наблюдение', 'Результат'], $rows, $checks, $runtime, $events, $details);
echo 'mongodb120-legacy-relaxed-screen-modern: saved result.html and result.json
';
Ожидаемый результат
mongodb120-legacy-relaxed-screen-modern: saved result.html and result.json

Локальный BSON-кодек, без соединения с базой. Действующий API Document: PHP 8.5.11 / mongodb 2.5.5.

Открыть фактический HTML результата · Скачать JSON таблицы

Границы и следующий шаг

Пример CLI показывает полезную нагрузку, а не готовый HTTP endpoint. Настройку Content-Type, авторизацию, обработку ошибок и экранирование при вставке в HTML выполняют на соответствующей границе приложения. JSON-экранирование не заменяет HTML-экранирование.

Упражнение с ориентиром. Добавьте публичное поле sku, не добавляя supplier_cost. После единственного декодирования должно быть пять публичных ключей. Удалите повторный json_encode из экспериментального HTTP-ответа; клиент должен получать объект, а не строку с JSON внутри.

Реальная ситуация из обсуждения: Communication between JavaScript and PHP scripts in a web app. В примере взаимодействия PHP и JavaScript BSON-текст повторно упаковывается json_encode, что усложняет форму ответа. Наш отдельный сценарий показывает разницу между JSON-объектом и строкой с JSON внутри, не заявляя, что это единственная причина всех ошибок исходного приложения. Это постановка проблемы, а правила и решения здесь проверяются по первичным источникам; код оригинальный.

typeMap: какой PHP-контейнер получится

typeMap управляет контейнерами после чтения BSON, а не именами полей базы. В историческом вызове карта по умолчанию []; в Document::toPHP допускается null. Отсутствующее правило оставляет стандартное поведение. Числовые BSON-типы выбираются не ключом root, а правилами соответствующего API.

Ключ картыОбластьВарианты
rootКорневой документarray; object/stdClass; имя допустимого класса; bson для Document
documentВложенные BSON-документыarray; object/stdClass; имя допустимого класса; bson для Document
arrayBSON-спискиarray; object/stdClass; имя допустимого класса; bson для PackedArray
fieldPathsОтдельные пути, приоритет перед общими правиламиКарта path=>тип; точка обозначает вложенность, $ — любой элемент массива; используются те же целевые типы

Неуказанные/пустые/null значения карты оставляют штатное сопоставление; документы обычно дают stdClass, списки — array. Для встроенных "array", "object", "stdClass", "bson" регистр несущественен. Поля со специальным __pclass и Persistable имеют отдельное правило восстановления: при подходящем доступном классе оно может менять обычный выбор документа. Явно выбранный пользовательский класс должен быть инстанцируемым и реализовывать MongoDB\BSON\Unserializable; данные передаются в bsonUnserialize, а не являются аргументами конструктора.

Правило bson доступно с 1.16 и применимо к изучаемой исторической среде 1.20. Оно сохраняет контейнеры Document/PackedArray вместо немедленной материализации всего в PHP-массивы. Чтение одного поля и редактирование копии — разные задачи; менять исходный Document нельзя.

Преобразование документа с повторяющимися ключами в PHP может потерять данные, независимо от выбора array или stdClass. Не создавайте такие BSON-документы. Полные современные правила и примеры путей: карта типов Document и восстановление пользовательских объектов.

Почему старый toJSON нельзя просто переименовать

На границе JSON важно различать формат и бизнес-схему. Следующая таблица относится к MongoDB Extended JSON, а не требует выдавать именно такие поля вашим покупателям.

BSON-значениеLegacy v1 strictCanonical v2Relaxed v2
Int64(2)2{"$numberLong":"2"}2 — ширина типа не выражена
Date(0){"$date":0}{"$date":{"$numberLong":"0"}}{"$date":"1970-01-01T00:00:00Z"}
Binary(ABC,00){"$binary":"QUJD","$type":"00"}{"$binary":{"base64":"QUJD","subType":"00"}}Та же современная оболочка Binary
Double NaNnan — не стандартный JSON{"$numberDouble":"NaN"}{"$numberDouble":"NaN"}
Decimal128(12.30){"$numberDecimal":"12.30"}{"$numberDecimal":"12.30"}{"$numberDecimal":"12.30"}

Даты Relaxed внутри 1970–9999 представлены ISO-строкой; вне этого диапазона применяется форма с $numberLong. Для полной карты обычных и специальных BSON-значений см. форматы Canonical/Relaxed. Ни один из форматов не подменяет проверку разрешённых полей или авторизацию.

Все шесть исторических функций

Карточки сохраняют сигнатуры, параметры, результат, ошибки и историю соответствующих страниц PHP.net. В каждой два различных применения из выполненных пар и действующая замена.

ФункцияНазначение
MongoDB\BSON\fromPHPИсторическое получение BSON-байтов из PHP-массива или объекта.
MongoDB\BSON\fromJSONИсторический разбор Extended JSON в бинарный BSON.
MongoDB\BSON\toPHPИсторическое чтение одного BSON-документа в PHP с картой типов.
MongoDB\BSON\toJSONИсторический Legacy Extended JSON v1; новый формат выбирается явно.
MongoDB\BSON\toCanonicalExtendedJSONИсторический экспорт с явными BSON-типами для обмена и восстановления.
MongoDB\BSON\toRelaxedExtendedJSONИсторический более краткий экспорт для просмотра и согласованного API.

Проверить одну границу и двигаться дальше

Начните с одной границы данных: бинарный обработчик, импорт, редактор, ответ клиенту или файл задания. Запишите нужные типы и вид результата, замените вызов, затем проверьте потребителя. Нельзя объявлять миграцию завершённой только потому, что исчезло сообщение об undefined function.

Дальше — сохранение и восстановление пользовательских объектов, неизменяемый Document, PackedArray и списки и типизированные значения формы/интеграции.

Здесь разобраны шесть удалённых функций кодека. Миграция всего приложения, старого расширения mongo, всех изменений серверных версий и всех протоколов сторонних систем не считается выполненной этими примерами.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

MongoDB\BSON\fromPHPИсторическое получение BSON-байтов из PHP-массива или объекта.

1.0.0; deprecated 1.20.0; removed 2.0.0 · mongodb

MongoDB\BSON\fromPHP(array|object $value): string

Сигнатура и параметры

MongoDB\BSON\fromPHP(array|object $value): string
ПараметрЧто передать
valuePHP-массив или объект, который нужно сериализовать. Корень результата — BSON-документ.

Результат и ошибки

Бинарная строка с одним BSON-документом. Это не JSON-текст.

MongoDB\Driver\Exception\UnexpectedValueException, если значение нельзя представить в BSON: в частности, встретился неподдержанный MongoDB\BSON\Type либо bsonSerialize() вернул недопустимый для используемой версии результат.

Старая страница fromPHP перечисляет array/stdClass для bsonSerialize(); с mongodb 1.17 этот метод допускает также Document/PackedArray. Это уточнение применимо к стенду 1.20.1. Вложенный последовательный PHP-список становится BSON-массивом, ассоциативная структура — документом. Публичные свойства обычного объекта и специальные интерфейсы имеют разные правила; нельзя обещать автоматическое сохранение private/protected свойств.

Версии

Версия расширенияИзменение
1.0.0Функция появилась.
1.20.0Deprecated.
2.0.0Удалена.

Современная замена

$document = MongoDB\BSON\Document::fromPHP($value);
$bytes = (string) $document; // if the consumer still expects raw BSON

Новая фабрика возвращает Document, поэтому старое ожидание string сохраняется явным (string). Не переводите BSON-скаляры в обычные строки/float до фабрики.

Корзина для существующего бинарного обработчика

Вход — артикул, количество, Decimal128 цены и ObjectId покупателя. Обратное чтение сохраняет все четыре назначения; столбец результата показывает string на прежней границе.

$cart = ['sku'=>'PEN-7','qty'=>2,'price'=>new MongoDB\BSON\Decimal128('1290.00')];
$bytes = MongoDB\BSON\fromPHP($cart);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Повторное кодирование исправленной записи редактора

После разрешённого изменения sku исходная схема требует Int64 и пустой документ. Программа сравнивает ошибочную копию с явно восстановленными типами; это другая задача, чем первоначальное создание корзины.

$copy = ['sku'=>'PEN-8','qty'=>new MongoDB\BSON\Int64('2'),'meta'=>(object)[]];
$bytes = MongoDB\BSON\fromPHP($copy);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Объект Document и его методы: справочник действующего API. Типы ID, даты и цены: ObjectId, UTCDateTime и Decimal128.

MongoDB\BSON\fromJSONИсторический разбор Extended JSON в бинарный BSON.

1.0.0; deprecated 1.20.0; removed 2.0.0 · mongodb

MongoDB\BSON\fromJSON(string $json): string

Сигнатура и параметры

MongoDB\BSON\fromJSON(string $json): string
ПараметрЧто передать
jsonТекст JSON/Extended JSON для преобразования в BSON. Маркеры Extended JSON передают дополнительные BSON-типы.

Результат и ошибки

Сериализованный BSON-документ как бинарная строка.

MongoDB\Driver\Exception\UnexpectedValueException, если текст нельзя преобразовать в BSON, например при синтаксической ошибке.

Обычная строка похожая на ObjectId остаётся строкой; $oid явно задаёт ObjectId. json_decode не выполняет тот же типовой разбор. После синтаксиса отдельно проверяются обязательные поля, размер и значения входа.

Версии

Версия расширенияИзменение
1.0.0Функция появилась.
1.20.0Deprecated.
2.0.0Удалена.

Современная замена

$document = MongoDB\BSON\Document::fromJSON($json);
$bytes = (string) $document;

Для бинарного потребителя извлеките строку из нового Document. Не выполняйте json_decode/повторный json_encode только для замены удалённой функции: это дополнительная граница потери типов.

Принять типизированную поставку

Внешний текст превращается в ObjectId, UTCDateTime и Binary; отдельная испорченная строка отклоняется. Таблица показывает тип каждого принятого поля.

$json = '{"supplier":"WH-4","ship_at":{"$date":{"$numberLong":"0"}}}';
$bytes = MongoDB\BSON\fromJSON($json);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Прочитать задание из текстового файла

Приёмник читает Canonical-файл, восстанавливает int32/int64/double/Decimal128 и сравнивает конкретную запись. Здесь источник — сохранённый текст типизированного задания, а не произвольная форма пользователя.

$json = '{"small":{"$numberInt":"2"},"wide":{"$numberLong":"2"}}';
$bytes = MongoDB\BSON\fromJSON($json);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Объект Document и его методы: справочник действующего API. Типы ID, даты и цены: ObjectId, UTCDateTime и Decimal128.

MongoDB\BSON\toPHPИсторическое чтение одного BSON-документа в PHP с картой типов.

1.0.0; deprecated 1.20.0; removed 2.0.0 · mongodb

MongoDB\BSON\toPHP(string $bson, array $typeMap = []): array|object

Сигнатура и параметры

MongoDB\BSON\toPHP(string $bson, array $typeMap = []): array|object
ПараметрЧто передать
bsonБинарная строка ровно одного BSON-документа.
typeMapКарта PHP-типов корня, документов, массивов и отдельных путей; по умолчанию пустая карта.

Результат и ошибки

Декодированный PHP-массив или объект по правилам карты типов.

InvalidArgumentException для неинстанцируемого класса карты или класса без MongoDB\BSON\Unserializable. UnexpectedValueException для повреждённого BSON, лишних байтов после документа, неожиданной ошибки libbson и другого входа, не содержащего ровно один документ. Оба класса находятся в MongoDB\Driver\Exception.

Повторяющиеся BSON-ключи могут быть потеряны в PHP: не создавайте такие документы, поведение драйвера/сервера может быть неопределённым. Корень и вложенные документы без специального сопоставления обычно читаются как stdClass, массивы как PHP array. Явная карта меняет этот выбор, но не проверяет бизнес-схему.

Версии

Версия расширенияИзменение
1.0.0Функция появилась.
1.3.0Неподдержанный устаревший BSON-тип начал вызывать UnexpectedValueException; прежде игнорировался.
1.3.2Такие типы снова игнорировались с предупреждением debug-журнала.
1.4.0Вместо предупреждения создаётся объект соответствующего типа.
1.20.0Deprecated.
2.0.0Удалена.

Современная замена

$copy = MongoDB\BSON\Document::fromBSON($bson)->toPHP($typeMap);

Document::toPHP всегда сохраняет BSON int64 объектом Int64. Старый toPHP на проверяемой x64-среде возвращает native int. Пересмотрите is_int, арифметику и последующую сериализацию. Не преобразуйте любые числовые объекты во float.

Проверить прочитанную корзину

Карта root=>array даёт доступ к полям по ключам. Реальная программа проверяет количество и классы цены/покупателя после сериализации.

$bytes = (string) MongoDB\BSON\Document::fromPHP(['sku'=>'PEN-7','qty'=>2]);
$cart = MongoDB\BSON\toPHP($bytes, ['root'=>'array']);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Дать редактору изменяемую копию

Карта вложенных документов в array делает правку удобной, но стирает различие пустых {} и []. Таблица наглядно сравнивает ошибочный и восстановленный результат.

$bytes = (string) MongoDB\BSON\Document::fromJSON('{"qty":{"$numberLong":"2"},"meta":{}}');
$copy = MongoDB\BSON\toPHP($bytes, ['root'=>'array','document'=>'array']);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Карта типов разобрана ниже: root/document/array/fieldPaths. Пользовательские классы: Serializable, Unserializable и Persistable.

MongoDB\BSON\toJSONИсторический Legacy Extended JSON v1; новый формат выбирается явно.

1.0.0; deprecated 1.20.0; removed 2.0.0 · mongodb

MongoDB\BSON\toJSON(string $bson): string

Сигнатура и параметры

MongoDB\BSON\toJSON(string $bson): string
ПараметрЧто передать
bsonБинарная строка одного BSON-документа.

Результат и ошибки

Текст Legacy Extended JSON в старом strict mode. Этот формат предшествует Canonical и Relaxed.

MongoDB\Driver\Exception\UnexpectedValueException, если вход не содержит ровно один BSON-документ: например, повреждён, имеет хвост после документа либо вызвал неожиданную ошибку libbson.

Legacy-формат не задаёт JSON-представление NaN и бесконечностей. libbson может вывести nan/inf, которые обычный JSON-парсер не принимает. Для таких данных выбирают Canonical или Relaxed, а бизнес-допустимость специальных значений проверяют отдельно.

Версии

Версия расширенияИзменение
1.0.0Функция появилась.
1.20.0Deprecated.
2.0.0Удалена.

Современная замена

$doc = MongoDB\BSON\Document::fromBSON($bson);
$json = $doc->toCanonicalExtendedJSON(); // or toRelaxedExtendedJSON(), by contract

Метода Document::toJSON нет. Canonical/Relaxed — новые форматы, поэтому клиент с ожиданием $date как числа или старой формы $binary/$type должен быть адаптирован. Простое совпадение слова JSON не означает совместимости.

Прочитать старую диагностику поставщика

Исторический код действительно строит текстовую диагностику принятого сообщения. Современная пара использует Canonical; сравниваются распознанные значения и классы, а не обещается прежняя структура диагностического JSON.

$bytes = (string) MongoDB\BSON\Document::fromPHP(['supplier'=>'WH-4','ship_at'=>new MongoDB\BSON\UTCDateTime(0)]);
$diagnostic = MongoDB\BSON\toJSON($bytes);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Изменить протокол старого сервиса

Отдельно исследуются дата, бинарная подпись и нечисловой результат датчика. Старый nan не проходит обычный JSON-парсер, современная типовая оболочка проходит.

$bytes = (string) MongoDB\BSON\Document::fromPHP(['sensor'=>NAN]);
$text = MongoDB\BSON\toJSON($bytes); // historical: contains nan

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Объект Document и его методы: справочник действующего API. Типы ID, даты и цены: ObjectId, UTCDateTime и Decimal128.

MongoDB\BSON\toCanonicalExtendedJSONИсторический экспорт с явными BSON-типами для обмена и восстановления.

1.3.0; deprecated 1.20.0; removed 2.0.0 · mongodb

MongoDB\BSON\toCanonicalExtendedJSON(string $bson): string

Сигнатура и параметры

MongoDB\BSON\toCanonicalExtendedJSON(string $bson): string
ПараметрЧто передать
bsonБинарная строка одного BSON-документа.

Результат и ошибки

Строка Canonical Extended JSON. Формат предпочитает сохранение типов краткости, в частности различает числовые типы.

MongoDB\Driver\Exception\UnexpectedValueException для входа, не содержащего ровно один документ: повреждения, дополнительных байтов или неожиданной ошибки libbson.

В Canonical int32, int64 и double имеют разные оболочки. Это помогает восстановлению типов; не обещает одинаковые пробелы или произвольную побайтовую нормализацию у всех версий кодеков.

Версии

Версия расширенияИзменение
1.3.0Функция появилась.
1.20.0Deprecated.
2.0.0Удалена.

Современная замена

$json = MongoDB\BSON\Document::fromBSON($bson)->toCanonicalExtendedJSON();

Вход метода — Document, поэтому существующие BSON-байты сначала читаются через fromBSON. Сам формат остаётся Canonical; тестируйте значения и типы, не только длину строки.

Выявить тихую потерю числового типа

Canonical показывает $numberInt вместо ожидаемого $numberLong после старого PHP-круга. Такое сравнение обнаруживает ошибку, невидимую при обычном echo 2.

$bytes = (string) MongoDB\BSON\Document::fromPHP(['qty'=>2]);
$audit = MongoDB\BSON\toCanonicalExtendedJSON($bytes);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Сохранить типизированное задание

Реальная запись файла, чтение и восстановление сохраняют четыре разных числовых типа. Результат показывает маркеры и итог конкретного обратного преобразования.

$bytes = (string) MongoDB\BSON\Document::fromPHP(['price'=>new MongoDB\BSON\Decimal128('12.30')]);
$transfer = MongoDB\BSON\toCanonicalExtendedJSON($bytes);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Объект Document и его методы: справочник действующего API. Типы ID, даты и цены: ObjectId, UTCDateTime и Decimal128.

MongoDB\BSON\toRelaxedExtendedJSONИсторический более краткий экспорт для просмотра и согласованного API.

1.3.0; deprecated 1.20.0; removed 2.0.0 · mongodb

MongoDB\BSON\toRelaxedExtendedJSON(string $bson): string

Сигнатура и параметры

MongoDB\BSON\toRelaxedExtendedJSON(string $bson): string
ПараметрЧто передать
bsonБинарная строка одного BSON-документа.

Результат и ошибки

Строка Relaxed Extended JSON. Формат предпочитает обычные JSON-примитивы точному сохранению всей информации о типах.

MongoDB\Driver\Exception\UnexpectedValueException для входа, не содержащего ровно один документ, включая повреждение, лишние байты и неожиданную ошибку libbson.

Краткость удобна человеку и веб-клиентам по согласованной схеме; ширина int32/int64 может потеряться. ObjectId, Decimal128, Binary и даты по-прежнему требуют предусмотренных форматом представлений.

Версии

Версия расширенияИзменение
1.3.0Функция появилась.
1.20.0Deprecated.
2.0.0Удалена.

Современная замена

$json = MongoDB\BSON\Document::fromBSON($bson)->toRelaxedExtendedJSON();

Удаляется функция, а не сам формат Relaxed. Полученный текст уже является JSON; повторный json_encode создаёт JSON-строку. Отбор публичных полей должен предшествовать сериализации.

Показать оператору сохранённую корзину

Декодированный просмотр содержит qty=2 и цену в $numberDecimal. Проверка удобна для проверки формы после миграции бинарного обработчика, без изменения сохранённых типов.

$bytes = (string) MongoDB\BSON\Document::fromPHP(['qty'=>2,'price'=>new MongoDB\BSON\Decimal128('1290.00')]);
$preview = MongoDB\BSON\toRelaxedExtendedJSON($bytes);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Вернуть публичную карточку товара

Явный перечень не пропускает supplier_cost. Пример сравнивает одно и двойное JSON-кодирование: клиенту нужен объект, а не строка с текстом объекта.

$bytes = (string) MongoDB\BSON\Document::fromPHP(['name'=>'Notebook','stock'=>2]);
$response = MongoDB\BSON\toRelaxedExtendedJSON($bytes);

Открыть пару полных программ, входные данные и фактические результаты. У короткого фрагмента выше историческая функция; современная ветка пары не вызывает удалённые функции.

Объект Document и его методы: справочник действующего API. Типы ID, даты и цены: ObjectId, UTCDateTime и Decimal128.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.