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

Дерево руководства
Глава 166 · После переменных, массивов, функций и основ Query
MongoDB: точные Int64 и Decimal128 — номера, цены и результаты расчётов
От исходной строки и явного BSON-типа к точному поиску, decimal-сумме выбранного заказа, валидации и безопасному JSON-контракту.
Сохранить цифры и понять тип значения
Внешняя система прислала длинный номер заказа, а каталог — цену 19.90. Нужно найти ровно тот заказ и получить правильную сумму, не меняя цифры по дороге. Здесь последовательно пройдём от строки и простого конструктора к настоящей записи, запросу, серверному расчёту и JSON-ответу.
- ID «9007199254740993» → Int64 → BSON long → статус нужного заказа
- Цена «19.90» → Decimal128 → BSON decimal → серверная сумма 40.10
- Чтение результата → точные строки в HTML/JSON → понятные цифры человеку
Представьте подпись единицы на измерительном приборе. Одни и те же видимые цифры без контекста ещё не говорят, что и как измерено. BSON-тип также влияет на прочтение и расчёт значения. Аналогия объясняет необходимость явного типа; она не означает физическое устройство памяти или бесконечную точность.
$orderNumber = new MongoDB\BSON\Int64('9007199254740993');
$price = new MongoDB\BSON\Decimal128('19.90');
echo (string) $orderNumber; // 9007199254740993
echo (string) $price; // 19.90Знак $ начинает имя переменной; = сохраняет значение справа. new создаёт объект, а строка между скобками — аргумент конструктора. Полное имя MongoDB\BSON\... указывает класс расширения. (string) просит точное строковое представление. В этом коротком фрагменте базы данных ещё нет: это подготовка значений. Ниже каждая полная программа сама создаёт учебные документы и читает результат.
Если встречается незнакомое понятие, начните с переменных, массивов, функций и return, Manager, BulkWrite, Query и projection. Основы точных BSON-значений уже есть в главе ObjectId, UTCDateTime и Decimal128.
Целое, десятичное число, приближение и строка
| Представление | Что гарантирует | Чего не гарантирует |
|---|---|---|
| Int64 | Целое signed64, BSON long при записи | Бесконечный диапазон; ведущие нули; безопасный float на промежуточном шаге |
| PHP int на x64 | Целые значения signed64 без float | Обязательный BSON long для малого числа при повторной записи |
| Decimal128 | Десятичное BSON-число до 34 значащих цифр | Валюту, денежную политику, произвольную точность |
| PHP float / BSON double | Двоичное приближение | Точное представление всех десятичных цен или больших ID |
| Строка | Последовательность символов, если не изменять её | Автоматическую числовую арифметику или BSON-тип |
| Canonical Extended JSON | Типизированное текстовое представление BSON | Автоматическое восстановление классов обычным json_decode |
Точность и оформление — разные задачи. Decimal128("19.90") сохраняет десятичную модель, но не всякий Decimal128 обязан выводиться ровно с двумя знаками. Int64 сохраняет числовое значение, но не начальные нули. Идентификатор, цена, счётчик и измерение должны получить собственные правила, прежде чем выбирать конструктор.
Подготовка и запуск восьми программ
Восемь архивов самостоятельны. Нужны PHP CLI 8.4+ x64, ext-mongodb 2.5+ и отдельный MongoDB Community 8.0+. Среда проверки пакета: PHP 8.5.11 / mongodb 2.5.5 / MongoDB 8.0.34. Composer не нужен. В README описаны установка расширения, проверка php.ini и запуск отдельного mongod; использовать рабочую базу не требуется.
$env:PHPMAN_MONGODB_URI='mongodb://127.0.0.1:27075/?directConnection=true&retryWrites=false'
$env:PHPMAN_MONGODB_DATABASE='php_manual_lab'
php example.phpЭто команды PowerShell из распакованного каталога. Каждая программа создаёт одну собственную коллекцию mongodb127_numbers_ со случайной частью, записывает небольшой набор данных, выполняет ограниченные чтения и удаляет только созданное точное имя в finally. После удаления проверяется отсутствие коллекции. Непредвиденные отказы подключения или запроса не подменяются ожидаемой ошибкой ценового формата.
Найти заказ по большому внешнему номеру без потери цифр
Служба доставки прислала номер заказа 9007199254740993. На экране нужен статус именно этого заказа. Номер нужно донести до фильтра без изменения последней цифры и с тем BSON-типом, который хранится в поле external_id.
Исходные данные
Два документа с числовыми external_id 9007199254740993 и 9007199254740994. Первый имеет статус «готов», второй — «ожидает». Исходный номер приходит строкой. В этом примере источник определяет номер как signed64; если у идентификатора значимы ведущие нули или буквы, для него может быть нужна строковая модель.
- Вход интеграции: строка «9007199254740993» сохраняет все цифры
- Конструктор: Int64 проверяет числовую строку и задаёт BSON long
- Фильтр базы: external_id равен этому Int64; выбрать только номер и статус
- Экран: готов; строковое представление номера не потеряло цифру
Минимальный полезный вызов
$number = new MongoDB\BSON\Int64('9007199254740993');
$filter = ['external_id' => $number];
echo (string) $number; // 9007199254740993Короткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Действие | Значение / число совпадений | Результат |
|---|---|---|
| Int64 в фильтре | 9007199254740993 | готов |
| Строка в фильтре | 0 | совпадения нет |
| Байтовый BSON Document | 9007199254740993 | Int64 |
Пошаговое объяснение
new MongoDB\BSON\Int64($text) создаёт значение для BSON. Это ещё не запись: nm127Insert выполняет BulkWrite отдельно. Ключ external_id в фильтре указывает поле документа, а объект справа — искомое числовое значение. limit=1 ограничивает чтение, projection не запрашивает ненужные поля.
MongoDB отличает числовое значение от строки. Поэтому фильтр с Int64 находит первый документ, а тот же текст без конструктора не находит ни одного. При этом числовые BSON-типы в сравнениях не всегда изолированы друг от друга: для проверки именно long здесь отдельно применяется $type. Не путайте numeric equality с требованием конкретного типа хранения.
На 64-битном PHP обычный драйверный Cursor может вернуть BSON long как PHP int: обе границы signed64 помещаются в такой int. Для проверенного пути executeQuery это и происходит. Отдельный MongoDB\BSON\Document::get сохраняет raw BSON-тип и возвращает Int64. Программа не требует объекта Int64 от каждого способа чтения, а проверяет цифры и тип хранения отдельно.
Число превышает диапазон целых, которые двоичный double способен представлять подряд без пропусков. Промежуточный PHP float или JavaScript Number способен потерять единицу ещё до вызова MongoDB. После такой потери конструктор не восстановит исходный номер. При обмене с браузером передавайте исходный ID строкой или согласованным Extended JSON.
Границы и ошибки
Int64 не сохраняет вид записи: «07» нормализуется до «7». Поэтому код заказа «0007» с важными ведущими нулями нужно моделировать иначе. Номер должен укладываться в signed64; переполнение разобрано ниже. В рабочей коллекции частый поиск external_id обычно нуждается в подходящем индексе, а условие уникальности задаётся требованиями интеграции.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
$text='9007199254740993';$number=new MongoDB\BSON\Int64($text);
nm127Insert($manager,$namespace,[['_id'=>1,'external_id'=>$number,'status'=>'готов'],['_id'=>2,'external_id'=>new MongoDB\BSON\Int64('9007199254740994'),'status'=>'ожидает']]);
$found=nm127Read($manager,$namespace,['external_id'=>$number],['_id'=>1,'external_id'=>1,'status'=>1],1);
$wrong=nm127Read($manager,$namespace,['external_id'=>$text],['_id'=>1],1);
$long=nm127Read($manager,$namespace,['external_id'=>['$type'=>'long']],['_id'=>1],2);
$raw=MongoDB\BSON\Document::fromPHP(['external_id'=>$number])->get('external_id');
$rows=[['Int64 в фильтре',(string)$found[0]['external_id'],$found[0]['status']],['Строка в фильтре',(string)count($wrong),'совпадения нет'],['Байтовый BSON Document',(string)$raw,'Int64']];
$checks=['selected_exact_id'=>array_column($found,'_id')===[1],'exact_digits'=>(string)$found[0]['external_id']===$text,'string_not_number'=>$wrong===[],'stored_long_type'=>array_column($long,'_id')===[1,2],'raw_preserves_type'=>$raw instanceof MongoDB\BSON\Int64,'raw_preserves_value'=>(string)$raw===$text];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-int64-lookup', ['Действие', 'Значение / число совпадений', 'Результат'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-int64-lookup: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-int64-lookup: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. Поменяйте исходную строку на 9007199254740994. Ожидаемый выбранный статус станет «ожидает», но демонстрационный текстовый фильтр по-прежнему даст 0. Обновите ожидаемые проверки только вместе с исходными данными и объясните, почему объект значения в фильтре важен.
Проверить границы Int64 и не потерять тип небольшого счётчика
Импорт счётчиков должен принять два предельных 64-битных значения и небольшой счётчик 42, а некорректные строки вернуть в отчёт ошибок. При этом поле по договорённости обязано храниться как BSON long даже тогда, когда текущее число маленькое.
Исходные данные
Семь входных строк: нижняя и верхняя границы, 42, два выхода за диапазон, дробь 12.5 и текст abc. Это технический счётчик signed64, а не цена и не ID с ведущими нулями.
- Разобрать вход: каждая строка передаётся в new Int64
- Отделить отказы: InvalidArgumentException отмечает плохую строку, база не получает её
- Проверить запись: три принятых значения читаются через фильтр $type: long
- Проверить вычисление: 41+1 даёт Int64(42), но MAX+1 становится float
Минимальный полезный вызов
$counter = new MongoDB\BSON\Int64(42); // BSON long даже для малого значения
echo (string) $counter; // 42
// '9223372036854775808' уже выходит за верхнюю границу signed64.Короткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Вход / действие | Решение / результат | Сохранённое значение / пояснение |
|---|---|---|
| -9223372036854775808 | принят | -9223372036854775808 |
| 9223372036854775807 | принят | 9223372036854775807 |
| 42 | принят | 42 |
| 9223372036854775808 | отклонён | не записан |
| -9223372036854775809 | отклонён | не записан |
| 12.5 | отклонён | не записан |
| abc | отклонён | не записан |
| Int64(41) + 1 | 42 | Int64 |
| MAX + 1 | float | не сохраняем как точный long |
Пошаговое объяснение
Нижняя граница signed64 — −9223372036854775808, верхняя — 9223372036854775807. Конструктор принимает PHP int или строку целого. Строка удобна на входе интеграции и явно сохраняет большие значения. Десятичная дробь и abc не описывают допустимое целое, поэтому перехватывается именно InvalidArgumentException конструктора.
try/catch здесь окружает только создание отдельного значения. Ожидаемый отказ входа попадает в таблицу «не записан». Ошибки подключения или BulkWrite не превращаются в такой же мягкий отказ: они должны остановить программу, чтобы не выдавать недоступную базу за успешный импорт.
new Int64(42) задаёт BSON long. Если вместо этого сохранить обычный PHP int 42, драйвер может выбрать BSON int32 по диапазону. Прочитанный в x64 Cursor int сам по себе не говорит, какой BSON-тип был в базе: его проверяет серверный $type.
Современный Int64 поддерживает арифметические, побитовые операции и сравнения. Это не арифметика неограниченной точности: для проверенной среды 41+1 остаётся Int64, а выход MAX+1 переходит во float. Вторая операция показана как диагностическая граница, её результат намеренно не сохраняется как точный счётчик. Проверяйте бизнес-диапазон до расчёта; конкурентный счётчик обновляется отдельным серверным механизмом.
Границы и ошибки
Прямой публичный конструктор появился в mongodb 1.16.0, хотя класс существовал с 1.5.0 для декодирования. Эти версии нельзя смешивать. Разрешённость отрицательного значения, максимальный счётчик устройства и политика переполнения относятся к предметной области. Int64 не заменяет их.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
$inputs=['-9223372036854775808','9223372036854775807','42','9223372036854775808','-9223372036854775809','12.5','abc'];
$rows=[];$docs=[];$accepted=[];$rejected=[];
foreach($inputs as $i=>$text){
try{$value=new MongoDB\BSON\Int64($text);$docs[]=['_id'=>$i,'counter'=>$value];$accepted[]=$text;$rows[]=[$text,'принят',(string)$value];}
catch(MongoDB\Driver\Exception\InvalidArgumentException){$rejected[]=$text;$rows[]=[$text,'отклонён','не записан'];}
}
nm127Insert($manager,$namespace,$docs);
$stored=nm127Read($manager,$namespace,['counter'=>['$type'=>'long']],['_id'=>1,'counter'=>1]);
$sum=new MongoDB\BSON\Int64(41)+1;
$overflow=new MongoDB\BSON\Int64('9223372036854775807')+1;
$rows[]=['Int64(41) + 1',(string)$sum,'Int64'];$rows[]=['MAX + 1',get_debug_type($overflow),'не сохраняем как точный long'];
$checks=['three_valid_values'=>count($accepted)===3,'four_invalid_values'=>count($rejected)===4,'stored_as_long'=>array_column($stored,'_id')===[0,1,2],'small_long_digits'=>(string)$stored[2]['counter']==='42','minimum_roundtrip'=>(string)$stored[0]['counter']===$inputs[0],'maximum_roundtrip'=>(string)$stored[1]['counter']===$inputs[1],'bounded_addition'=>$sum instanceof MongoDB\BSON\Int64 && (string)$sum==='42','comparison_supported'=>new MongoDB\BSON\Int64(41)<new MongoDB\BSON\Int64(42),'overflow_not_int64'=>is_float($overflow)];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-int64-range', ['Вход / действие', 'Решение / результат', 'Сохранённое значение / пояснение'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-int64-range: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-int64-range: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. Добавьте строку 07. Конструктор примет её, но строковый вывод будет 7. Это дополнительный принятый документ, поэтому изменятся число строк и ожидаемые проверки; подумайте, подходит ли такая нормализация вашему полю.
Практическая проблема в обсуждении: Как принудительно сохранить небольшое число как NumberLong в PHP. Разработчик хотел BSON long для небольшого42. Старое обсуждение относится к периоду, когда прямой new Int64 был недоступен; современный конструктор с1.16.0 решает эту задачу явно. Код и объяснение здесь оригинальные; контракт подтверждают официальные источники, а обсуждение показывает реальную задачу разработчика.
Передать большой номер через JSON с сохранением числа и типа
После чтения из MongoDB большой номер нужно отправить в другую систему или в браузер. Получатель должен получить все цифры. Для BSON-обмена также важно восстановить числовой тип, а для собственного HTTP API можно явно согласовать строковое поле external_id.
Исходные данные
Один сохранённый номер 9223372036854775807. Сравниваются jsonSerialize, обычный JSON-декодер, современный Canonical Extended JSON и простой API со строковым идентификатором.
- База: выбрать один документ и поле external_id
- Восстановить BSON-контракт: явно создать Int64 из прочитанного точного значения
- Два маршрута: Canonical сохраняет $numberLong; DTO сохраняет строку ID
- Обратный поиск: Document::fromJSON восстанавливает Int64, и фильтр снова находит документ
Минимальный полезный вызов
$id = new MongoDB\BSON\Int64('9223372036854775807');
echo json_encode($id, JSON_THROW_ON_ERROR);
// {"$numberLong":"9223372036854775807"}Короткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Маршрут | Значение | Форма |
|---|---|---|
| jsonSerialize / json_encode | 9223372036854775807 | $numberLong |
| Обычный json_decode | 9223372036854775807 | массив, не BSON-класс |
| Document::fromJSON | 9223372036854775807 | Int64 |
| Согласованный API | 9223372036854775807 | строка external_id |
Пошаговое объяснение
jsonSerialize() возвращает PHP-данные, пригодные для кодирования, а не готовую JSON-строку. Для Int64 это структура с ключом $numberLong и числом в строке. json_encode($typed) вызывает сериализатор автоматически. Проверки сравнивают оба результата, чтобы не полагаться только на внешнее сходство вывода.
Обычный json_decode(..., true) создаёт PHP-массив с ключом $numberLong. Он не знает, что нужен объект MongoDB\BSON\Int64. Поэтому в таблице рядом с цифрами показан тип array, а не ложное обещание восстановленного BSON long.
Для стандартного типизированного обмена документ создаётся через Document::fromPHP, превращается в Canonical Extended JSON и читается через Document::fromJSON. get("external_id") возвращает Int64; им выполняется второй реальный запрос. Это проверка пригодности восстановленного значения к использованию, а не только сравнение строк JSON.
Для своего браузерного API проще договориться: external_id — текст. Такой DTO не обещает автоматического BSON-восстановления, зато JavaScript не округлит длинный ID при JSON.parse. Явное создание Int64 после x64 Cursor также не даёт малому номеру при повторном BSON-кодировании случайно стать int32.
Границы и ошибки
Canonical сохраняет типы, но это не разрешение выполнять произвольный присланный документ как фильтр. Выбирайте известные поля и проверяйте их. Relaxed Extended JSON использует более обычную запись целых и потому требует особого внимания к возможностям получателя. Код использует Canonical для BSON-маршрута и строку для браузерного.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
$original=new MongoDB\BSON\Int64('9223372036854775807');
nm127Insert($manager,$namespace,[['_id'=>1,'external_id'=>$original]]);
$stored=nm127Read($manager,$namespace,['_id'=>1],['_id'=>0,'external_id'=>1],1)[0]['external_id'];
$typed=new MongoDB\BSON\Int64((string)$stored);$serialized=$typed->jsonSerialize();
$encoded=json_encode($typed,JSON_THROW_ON_ERROR);$plain=json_decode($encoded,true,512,JSON_THROW_ON_ERROR);
$document=MongoDB\BSON\Document::fromPHP(['external_id'=>$typed]);$canonical=$document->toCanonicalExtendedJSON();
$roundtrip=MongoDB\BSON\Document::fromJSON($canonical)->get('external_id');
$again=nm127Read($manager,$namespace,['external_id'=>$roundtrip],['_id'=>1],1);
$dto=json_encode(['external_id'=>(string)$typed],JSON_THROW_ON_ERROR);
$rows=[['jsonSerialize / json_encode',$serialized['$numberLong'],'$numberLong'],['Обычный json_decode',$plain['$numberLong'],'массив, не BSON-класс'],['Document::fromJSON',(string)$roundtrip,'Int64'],['Согласованный API',json_decode($dto,true,512,JSON_THROW_ON_ERROR)['external_id'],'строка external_id']];
$checks=['numberlong_shape'=>$serialized===['$numberLong'=>'9223372036854775807'],'serializer_used_by_json'=>$plain===$serialized,'plain_is_array'=>is_array($plain),'canonical_type_preserved'=>$roundtrip instanceof MongoDB\BSON\Int64,'canonical_value_preserved'=>(string)$roundtrip===(string)$original,'typed_filter_after_import'=>array_column($again,'_id')===[1],'dto_keeps_string'=>is_string(json_decode($dto,true,512,JSON_THROW_ON_ERROR)['external_id'])];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-int64-json', ['Маршрут', 'Значение', 'Форма'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-int64-json: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-int64-json: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. Замените номер на 42. Его Canonical-представление всё ещё должно содержать $numberLong со строкой 42, потому что тип задан Int64 явно. В DTO значение тоже останется строкой. Это полезная проверка контракта независимо от текущего размера числа.
Сохранить цену строкой на входе и числом Decimal128 в базе
Каталог получает цену 19.90. Нам нужно сохранить десятичное значение, которое затем можно точно умножать и складывать в базе. Одинаково выглядящая цена на экране может скрывать разные типы: decimal, double или string.
Исходные данные
Три учебных документа получают Decimal128("19.90"), PHP float 19.90 и PHP string "19.90". Название source фиксирует выбранный путь. Сравнительные варианты нужны для понимания причины ошибки, а не как рекомендуемые способы хранения цены.
- Форма / интеграция: исходная строка «19.90»
- Точный путь: new Decimal128("19.90")
- Проверка MongoDB: aggregation $type показывает decimal, double или string
- Выборка цены: тип decimal и цена ≥10.00 выбирают только первый документ
Минимальный полезный вызов
$price = new MongoDB\BSON\Decimal128('19.90');
$product = ['price' => $price, 'currency' => 'RUB'];
echo (string) $price; // 19.90; не floatКороткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Создано в PHP | Прочитанное значение | BSON-тип поля |
|---|---|---|
| Decimal128 | 19.90 | decimal |
| float | 19.9 | double |
| string | 19.90 | string |
Пошаговое объяснение
Decimal128 хранит десятичное число. Двоичный PHP float хранит приближение в другой модели, а строка хранит символы. Тип поля влияет на сравнение и арифметику. В таблице серверный $type подтверждает результат каждого пути, а не выводит предположение по PHP-исходнику.
19.90 в PHP без кавычек становится float. Текстовый вывод может выглядеть аккуратно как 19.9, но это не доказательство точности двоичного хранения. В примере не придумана длинная ошибочная дробь для каждого float: показывается реальная строка и подтверждённый сервером double.
Конструктор вызывается от исходной строки до преобразования во float. new Decimal128((string)$alreadyRoundedFloat) не возвращает ранее потерянные цифры. Для цены валюта и допустимый диапазон задаются отдельно; правила формы разбираются в следующем примере проверки входа.
Агрегация здесь не выполняет расчёт суммы. Она выбирает три известных документа, сортирует их и показывает source, price и $type. Следующий фильтр совмещает требование BSON decimal с десятичной нижней границей 10.00. Это конкретный отбор нужных данных, а не выгрузка всего каталога с последующей фильтрацией в PHP.
Границы и ошибки
Decimal128 даёт до 34 значащих цифр и конечный диапазон экспонент. Он не гарантирует любой произвольный десятичный текст и сам не назначает валюту. Целые минимальные денежные единицы тоже возможны при фиксированной шкале; выбор зависит от диапазона и необходимых расчётов. Отображение с двумя знаками и округление налогов — отдельные правила.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
nm127Insert($manager,$namespace,[['_id'=>1,'source'=>'Decimal128','price'=>new MongoDB\BSON\Decimal128('19.90')],['_id'=>2,'source'=>'float','price'=>19.90],['_id'=>3,'source'=>'string','price'=>'19.90']]);
$docs=nm127Aggregate($manager,$database,$collection,[['$sort'=>['_id'=>1]],['$limit'=>3],['$project'=>['_id'=>1,'source'=>1,'price'=>1,'bson_type'=>['$type'=>'$price']]]]);
$rows=[];foreach($docs as $doc){$rows[]=[$doc['source'],(string)$doc['price'],$doc['bson_type']];}
$selected=nm127Read($manager,$namespace,['price'=>['$type'=>'decimal','$gte'=>new MongoDB\BSON\Decimal128('10.00')]],['_id'=>1,'price'=>1],3);
$checks=['three_storage_types'=>array_column($docs,'bson_type')===['decimal','double','string'],'decimal_remains_object'=>$docs[0]['price'] instanceof MongoDB\BSON\Decimal128,'exact_fraction'=>(string)$docs[0]['price']==='19.90','explicit_decimal_filter'=>array_column($selected,'_id')===[1],'double_is_not_decimal'=>is_float($docs[1]['price']),'text_is_not_decimal'=>is_string($docs[2]['price'])];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-decimal-price', ['Создано в PHP', 'Прочитанное значение', 'BSON-тип поля'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-decimal-price: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-decimal-price: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. Поменяйте цену во всех трёх документах на 0.10. BSON-типы останутся теми же, но фильтр ≥10.00 ничего не вернёт. Затем измените нижнюю границу на Decimal128("0.00"): подходящий decimal-документ снова попадёт в результат.
Практическая проблема в обсуждении: Почему round в PHP не гарантирует точную десятичную цену в базе. После округления и записи пользователь видел хвост двоичного приближения. Это повод различить красивый вывод float и десятичную модель хранения; наш пример показывает реальные BSON-типы трёх путей. Код и объяснение здесь оригинальные; контракт подтверждают официальные источники, а обсуждение показывает реальную задачу разработчика.
Получить итог одного заказа в базе без выгрузки всех позиций
На странице выбранного заказа нужен итог 40.10 RUB. База уже содержит строки других заказов и валют. Запрос должен посчитать только выбранный заказ на сервере и вернуть одну итоговую строку, а не передавать весь массив покупок в PHP.
Исходные данные
Заказ A17: две единицы по 19.90 RUB и три единицы по 0.10 RUB. Есть посторонний заказ B18 на 800 EUR. Цены учебного набора заранее выбраны в рамках правила ввода и сохранены как Decimal128; количества — положительные int32. После вставки этот набор не изменяется. Отдельная программа ниже показывает саму проверку внешнего ввода.
- Отбор: order=A17 и currency=RUB
- Проверка структуры: отвергнуть неправильный BSON-тип цены/количества или qty вне 1..1000
- Расчёт базы: 19.90×2 + 0.10×3 =40.10 через $multiply и $sum
- Ответ: одна строка: A17, RUB, 2 позиции, 40.10, decimal
Минимальный полезный вызов
$pipeline = [
['$match' => ['order' => 'A17', 'currency' => 'RUB']],
['$group' => ['_id' => '$order',
'total' => ['$sum' => ['$multiply' => ['$price', '$qty']]]]],
]; // 19.90 × 2 + 0.10 × 3 = Decimal128('40.10')Короткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Заказ | Валюта | Строк заказа | Итого | Тип результата |
|---|---|---|---|---|
| A17 | RUB | 2 | 40.10 | decimal |
Пошаговое объяснение
$match ставится до $group: сначала остаются документы конкретного заказа и одной валюты. $multiply вычисляет цену каждой строки, $sum собирает эти произведения. Другой $sum со значением 1 считает число товарных строк, а не общее количество единиц. Поэтому в результате lines=2, хотя товаров в штуках пять.
В выражении "$price" знак $ означает ссылку на поле документа MongoDB, а не PHP-переменную. Поэтому в PHP для такого текста используются одинарные кавычки. Decimal128 цены вместе с целочисленным qty даёт decimal-расчёт; сервер возвращает Decimal128, который для отчёта преобразуется в строку без float.
$sum игнорирует некоторые нечисловые входы. Поэтому нельзя безоговорочно рассчитывать, что он сам сообщит о цене, случайно сохранённой текстом. Здесь отдельная проверка типов и диапазона qty прерывает расчёт при подозрительной строке выбранного заказа. Она не выбрасывает плохие строки молча ради красивой неполной суммы.
До агрегации выполнена проверка фиксированного набора, но это два отдельных запроса. Она не обеспечивает согласованный счёт при конкурентном изменении документов. Кроме того, фильтр структуры не повторяет все правила цены: NaN, Infinity и отрицательная цена должны быть запрещены входным контрактом. Для действующего сервиса закрепите правила схемы и согласованность окончательного расчёта отдельно.
После группировки сервер отдаёт одну строку. В PHP остаётся только форматирование результата. Для частого отбора заказ/валюта нужен подходящий индекс исходных документов; limit после $group ограничивает ответ, но не уменьшает работу подсчёта всех подходящих позиций.
Границы и ошибки
Это точный учебный итог неизменяемых проверенных строк одной валюты. Здесь не реализованы скидки, налоги, обмен валют, деление и правила округления. Для пустого выбранного заказа $group не создаст строку: приложение должно отдельно показать отсутствие заказа или согласованное значение 0, а не обращаться вслепую к [0]. В программе ожидается созданный fixture A17.
Для изменяемого сервиса: транзакции и согласованный расчёт; проверки при атомарной записи.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
nm127Insert($manager,$namespace,[['_id'=>1,'order'=>'A17','currency'=>'RUB','price'=>new MongoDB\BSON\Decimal128('19.90'),'qty'=>2],['_id'=>2,'order'=>'A17','currency'=>'RUB','price'=>new MongoDB\BSON\Decimal128('0.10'),'qty'=>3],['_id'=>3,'order'=>'B18','currency'=>'EUR','price'=>new MongoDB\BSON\Decimal128('800.00'),'qty'=>1]]);
$selected=['order'=>'A17','currency'=>'RUB'];
$invalid=nm127Read($manager,$namespace,$selected+['$or'=>[['price'=>['$not'=>['$type'=>'decimal']]],['qty'=>['$not'=>['$type'=>'int']]],['qty'=>['$lt'=>1]],['qty'=>['$gt'=>1000]]]],['_id'=>1],1);
if($invalid!==[]){throw new RuntimeException('Reject order with an invalid line instead of ignoring it');}
$pipeline=[['$match'=>$selected],['$group'=>['_id'=>'$order','lines'=>['$sum'=>1],'total'=>['$sum'=>['$multiply'=>['$price','$qty']]]]],['$project'=>['_id'=>0,'order'=>'$_id','lines'=>1,'total'=>1,'total_type'=>['$type'=>'$total']]],['$limit'=>1]];
$result=nm127Aggregate($manager,$database,$collection,$pipeline);
if(count($result)!==1){throw new RuntimeException('Selected order does not exist');}
$total=$result[0]['total'];$rows=[[$result[0]['order'],'RUB',$result[0]['lines'],(string)$total,$result[0]['total_type']]];
$checks=['one_summary_only'=>count($result)===1,'two_selected_lines'=>$result[0]['lines']===2,'exact_decimal_total'=>(string)$total==='40.10','total_has_decimal_type'=>$total instanceof MongoDB\BSON\Decimal128 && $result[0]['total_type']==='decimal','other_order_excluded'=>$result[0]['order']==='A17','source_contract_checked'=>$invalid===[]];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-decimal-total', ['Заказ', 'Валюта', 'Строк заказа', 'Итого', 'Тип результата'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-decimal-total: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-decimal-total: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. У второй позиции A17 измените qty с 3 на 4. Итог будет 40.20 RUB, а lines останется 2. Посторонний B18 по-прежнему не должен изменить результат. Затем отдельно попробуйте испортить тип qty: пример обязан остановиться на проверке структуры, а не выдать неполный счёт.
Практическая проблема в обсуждении: Как посчитать сумму decimal-поля и почему важны типы и сервер. В обсуждении с Azure Cosmos DB возникла проблема суммы decimal. Совместимый протокол не гарантирует одинаковые операторы и поддержку типов. Здесь результат проверяется на MongoDB Community, а не объявляется доказательством совместимости Cosmos DB. Код и объяснение здесь оригинальные; контракт подтверждают официальные источники, а обсуждение показывает реальную задачу разработчика.
Разделить допустимый BSON и допустимую цену формы
Цена из формы должна быть понятной покупателю и совместимой с расчётом. Не всякий текст, который умеет представить Decimal128, является допустимой ценой: NaN, Infinity и 1e2 не соответствуют правилу этой формы.
Исходные данные
Разрешены только строки от 0.00 до 999999.99 с точкой и ровно двумя дробными знаками. Восемь входов включают две допустимые цены, запятую, специальные значения, научную запись, уже полученный float и отрицательное число.
- Проверка PHP-типа: только исходная строка, float отвергается
- Правило цены: без знака, до 6 цифр целой части, точка, две дробные
- Конструктор: допустимый текст превращается в Decimal128
- Результат: два decimal-документа в базе, шесть понятных отказов в отчёте
Минимальный полезный вызов
$input = '19.90'; // текст формы, пока ещё без преобразования во float
if (!preg_match('/\A(?:0|[1-9][0-9]{0,5})\.[0-9]{2}\z/D', $input)) {
throw new InvalidArgumentException('Нужна цена с двумя цифрами после точки');
}
$price = new MongoDB\BSON\Decimal128($input);Короткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Вход | Политика цены | Что записано |
|---|---|---|
| 19.90 | принят | 19.90 |
| 0.00 | принят | 0.00 |
| 19,90 | отклонён | до записи |
| NaN | отклонён | до записи |
| Infinity | отклонён | до записи |
| 1e2 | отклонён | до записи |
| float 19.9 | отклонён | до записи |
| -1.00 | отклонён | до записи |
Пошаговое объяснение
nm127Price принимает mixed, чтобы явно показать отказ неподходящего входа, например float19.9. is_string проверяет исходный тип. Регулярное выражение задаёт именно контракт этой формы: 0 либо число без ведущих нулей, ещё до пяти цифр, литеральная точка и два знака после неё. Якоря \A и \z не позволяют оставить посторонние символы в начале или конце.
Для неподходящего прикладного формата функция выбрасывает обычный InvalidArgumentException приложения. Конструктор Decimal128 вызывается только после этой проверки. В этом разрешённом небольшом диапазоне точность Decimal128 достаточна. Отдельно программа показывает технический отказ строки с 35 существенными цифрами, которую нельзя представить точно.
Число символов не равно количеству значащих цифр. Текст с 35 цифрами и подходящим конечным нулём иногда представим точно после изменения экспоненты. Поэтому здесь не предлагается универсальная проверка Decimal128 через strlen($text)>34. Для произвольных десятичных значений применяйте контракт конструктора, а для цены — отдельный понятный диапазон.
Decimal128("NaN") технически создаётся, но nm127Price("NaN") его не допускает. Разделение показывает два уровня: возможности BSON и правила продукта. 19,90 не превращается в 19.90 скрыто: если интерфейс разрешит запятую, добавьте явную нормализацию до этой функции и покажите пользователю принятое значение.
Границы и ошибки
Правило с двумя дробными знаками относится к этой учебной цене, а не ко всем валютам и не ко всем финансовым величинам. Округлять произвольный float до двух знаков и объявлять его точным вводом нельзя. Ошибки MongoDB не перехватываются как «неверная цена»; они должны оставаться заметными.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
function nm127Price(mixed $raw): MongoDB\BSON\Decimal128 {
if(!is_string($raw) || !preg_match('/\A(?:0|[1-9][0-9]{0,5})\.[0-9]{2}\z/D',$raw)){
throw new InvalidArgumentException('Price must be text 0.00..999999.99 with two fractional digits');
}
return new MongoDB\BSON\Decimal128($raw);
}
$inputs=['19.90','0.00','19,90','NaN','Infinity','1e2',19.9,'-1.00'];$docs=[];$rows=[];$rejected=0;
foreach($inputs as $i=>$input){
$label=is_string($input)?$input:'float 19.9';
try{$price=nm127Price($input);$docs[]=['_id'=>$i,'price'=>$price];$rows[]=[$label,'принят',(string)$price];}
catch(InvalidArgumentException){$rejected++;$rows[]=[$label,'отклонён','до записи'];}
}
nm127Insert($manager,$namespace,$docs);$stored=nm127Read($manager,$namespace,[],['_id'=>1,'price'=>1],3);
$inexact=false;try{new MongoDB\BSON\Decimal128('1.2345678901234567890123456789012345');}catch(MongoDB\Driver\Exception\InvalidArgumentException){$inexact=true;}
$nan=new MongoDB\BSON\Decimal128('NaN');
$checks=['two_valid_prices'=>array_column($stored,'_id')===[0,1],'six_business_rejections'=>$rejected===6,'stored_price_digits'=>array_map(fn($d)=>(string)$d['price'],$stored)===['19.90','0.00'],'inexact_35_digits_rejected'=>$inexact,'bson_accepts_nan'=>(string)$nan==='NaN','business_rejects_nan'=>$rows[3][1]==='отклонён'];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-decimal-validation', ['Вход', 'Политика цены', 'Что записано'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-decimal-validation: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-decimal-validation: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. Добавьте 999999.99 и 1000000.00 как строки. Первая должна пройти, вторая — нет. Добавьте 01.00: она также отклоняется из-за ведущего нуля, хотя Decimal128 способен представить числовое значение 1.00. Объясните, какая проверка приняла каждое решение.
Сделать типизированный экспорт цены и отдельный ответ для интерфейса
Сервис каталога отдаёт цену другому MongoDB-приложению и браузеру. Первому нужен BSON decimal после чтения, второму — согласованные amount и currency без случайного перехода к JavaScript Number.
Исходные данные
Документ с Decimal128("19.90") и валютой RUB. Сравниваются jsonSerialize, полный Canonical Extended JSON, обратное восстановление BSON и простой JSON для собственного API.
- Чтение: одна цена и валюта из MongoDB
- Типизированный маршрут: Canonical с $numberDecimal → Document::fromJSON
- Обычный маршрут: amount="19.90", currency="RUB"
- Проверка: восстановленный Decimal128 снова находит исходный документ
Минимальный полезный вызов
$price = new MongoDB\BSON\Decimal128('19.90');
echo json_encode($price, JSON_THROW_ON_ERROR); // {"$numberDecimal":"19.90"}
echo json_encode(['amount' => (string) $price, 'currency' => 'RUB'], JSON_THROW_ON_ERROR);Короткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Маршрут | Цена | Тип / валюта |
|---|---|---|
| jsonSerialize | 19.90 | $numberDecimal |
| Canonical JSON | 19.90 | $numberDecimal |
| fromJSON | 19.90 | Decimal128 |
| API для экрана | 19.90 | RUB |
Пошаговое объяснение
Для Decimal128 jsonSerialize возвращает структуру с ключом $numberDecimal и десятичным текстом. json_encode автоматически использует её. В отличие от самого метода, результат json_encode — строка JSON. Программа сравнивает данные, полученные обоими путями.
Конкретная форма Decimal128 с $numberDecimal совпадает в legacy и современном Canonical Extended JSON. Это не значит, что json_encode любого BSON-объекта всегда выдаёт Canonical: например Binary имеет различия. Для полного документа выбирайте Document::toCanonicalExtendedJSON явно, чтобы формат не зависел от догадки по одному полю.
Document::fromJSON понимает BSON-маркеры и возвращает типизированное поле через get. Обычный json_decode даёт массив, а не Decimal128. Повторный запрос по восстановленной цене подтверждает, что пример действительно вернулся к BSON-значению.
В собственном API amount намеренно строка. Браузер может её показать без перевода в Number. Строковое сложение или преобразование amount во float на следующем шаге снова изменит модель, поэтому вычисления должны идти по согласованному десятичному контракту. Валюта передаётся отдельным полем и не угадывается по знаку или количеству дробных цифр.
Границы и ошибки
Extended JSON — формат представления типов, а не средство проверки прав или правил цены. Импортируемые поля всё равно проверяются. Простое строковое API не обещает автоматическое BSON-декодирование: получатель сам валидирует amount и создаёт Decimal128, если это его внутренняя модель.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
nm127Insert($manager,$namespace,[['_id'=>1,'price'=>new MongoDB\BSON\Decimal128('19.90'),'currency'=>'RUB']]);
$stored=nm127Read($manager,$namespace,['_id'=>1],['_id'=>0,'price'=>1,'currency'=>1],1)[0];$price=$stored['price'];
$serialized=$price->jsonSerialize();$encoded=json_encode($price,JSON_THROW_ON_ERROR);$plain=json_decode($encoded,true,512,JSON_THROW_ON_ERROR);
$canonical=MongoDB\BSON\Document::fromPHP($stored)->toCanonicalExtendedJSON();$imported=MongoDB\BSON\Document::fromJSON($canonical);$restored=$imported->get('price');
$canonicalShape=json_decode($canonical,true,512,JSON_THROW_ON_ERROR)['price'];
$dto=['amount'=>(string)$price,'currency'=>$stored['currency']];$dtoJSON=json_encode($dto,JSON_THROW_ON_ERROR);$dtoBack=json_decode($dtoJSON,true,512,JSON_THROW_ON_ERROR);
$found=nm127Read($manager,$namespace,['price'=>$restored],['_id'=>1],1);
$rows=[['jsonSerialize',$serialized['$numberDecimal'],'$numberDecimal'],['Canonical JSON',$canonicalShape['$numberDecimal'],'$numberDecimal'],['fromJSON',(string)$restored,'Decimal128'],['API для экрана',$dtoBack['amount'],$dtoBack['currency']]];
$checks=['decimal_serializer_shape'=>$serialized===['$numberDecimal'=>'19.90'],'json_encode_uses_serializer'=>$plain===$serialized,'canonical_shape_matches_for_decimal'=>$canonicalShape===$serialized,'typed_import'=>$restored instanceof MongoDB\BSON\Decimal128,'typed_query_roundtrip'=>array_column($found,'_id')===[1],'dto_string_and_currency'=>$dtoBack===['amount'=>'19.90','currency'=>'RUB'],'json_decode_is_not_bson'=>is_array($plain)];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-decimal-json', ['Маршрут', 'Цена', 'Тип / валюта'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-decimal-json: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-decimal-json: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. Замените цену на 0.10. В обоих JSON-маршрутах должна остаться строка 0.10, а типизированный импорт — дать Decimal128. Затем прочитайте простой DTO через json_decode: amount останется string, и это предусмотренный контракт, а не ошибка.
Передать точную цену в функцию и объект представления
Цена из базы нужна в двух местах: в видимой подписи каталога и в JSON экспорте. Хочется явно требовать объект точного десятичного значения и не принимать случайный float или строку там, где валидация уже должна быть завершена.
Исходные данные
Два товара: CUP по 19.90 RUB и BOX по 2.50 EUR. Функция nm127Label строит подпись, а небольшой объект PriceCell127 хранит цену и валюту для экспорта. Их задача — представление уже проверенных данных, а не ввод цены.
- База: прочитать sku, Decimal128 price и currency двух товаров
- Тип параметра: Decimal128Interface требует согласованный объект
- Два потребителя: подпись «19.90 RUB» и DTO с отдельными amount/currency
- Ошибочный вызов: обычная строка 19.90 отвергается PHP TypeError
Минимальный полезный вызов
function label(MongoDB\BSON\Decimal128Interface $amount): string {
return (string) $amount;
}
echo label(new MongoDB\BSON\Decimal128('19.90')); // 19.90Короткий фрагмент выделяет текущую операцию. Подключение, исходные документы, чтение и очистка находятся в полной программе ниже.
Что получится при выполнении
| Товар | Подпись на экране | amount для API | currency для API |
|---|---|---|---|
| CUP | 19.90 RUB | 19.90 | RUB |
| BOX | 2.50 EUR | 2.50 | EUR |
Пошаговое объяснение
Интерфейс описывает доступные операции объекта. Decimal128Interface содержит __toString():string; встроенный Decimal128 его реализует. Его имя в параметре означает «ожидается совместимый объект». Оно не создаёт цену и не выполняет конвертацию строки автоматически.
nm127Label($amount,$currency) получает значение, проверяет разрешённую валюту и возвращает текст. return отдаёт строку вызывающему коду. (string)$amount обращается к __toString; арифметика и float здесь не появляются. В тесте намеренно передана обычная строка, и ожидаемый TypeError подтверждает границу функции.
В PriceCell127 запись public readonly в конструкторе объявляет и инициализирует свойства. value():Decimal128Interface возвращает объект цены, export():array возвращает два понятных поля для JSON. Эти типы показывают три места применения интерфейса: параметр, свойство и результат метода. Если синтаксис ООП пока незнаком, сначала разберите короткую функцию над примером, затем переходите к классу.
Интерфейс не гарантирует две дробные цифры, положительную цену или допустимую валюту. Текущий пример читает известные проверенные Decimal128. Пользовательский класс с тем же интерфейсом обязан соблюдать сигнатуру, но одним фактом implements нельзя гарантировать автоматическое BSON-кодирование как встроенного Decimal128. DTO создан явно и не выдаётся за новый BSON-тип.
Границы и ошибки
Создать new Decimal128Interface нельзя: это контракт, а не реализация. Он не содержит jsonSerialize и не обещает методы MongoDB\BSON\Document. Для финансового приложения валидация и валюта должны быть закреплены до создания представления; демонстрационный PriceCell127 сам не проверяет произвольные входные данные как полноценный денежный объект.
Связанные понятия: интерфейсы; параметры и возвращаемое значение.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/nm127-helper.php';
$settings=nm127Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=nm127Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB 8.0+ required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database'];
$namespace=nm127Create($manager,$database,$owned);$collection=$owned[0];
function nm127Label(MongoDB\BSON\Decimal128Interface $amount,string $currency): string {
if(!in_array($currency,['RUB','EUR'],true)){throw new InvalidArgumentException('Unsupported currency');}
return (string)$amount.' '.$currency;
}
final class PriceCell127 {
public function __construct(public readonly MongoDB\BSON\Decimal128Interface $amount,public readonly string $currency){}
public function value(): MongoDB\BSON\Decimal128Interface{return $this->amount;}
public function export(): array{return ['amount'=>(string)$this->value(),'currency'=>$this->currency];}
}
nm127Insert($manager,$namespace,[['_id'=>1,'sku'=>'CUP','price'=>new MongoDB\BSON\Decimal128('19.90'),'currency'=>'RUB'],['_id'=>2,'sku'=>'BOX','price'=>new MongoDB\BSON\Decimal128('2.50'),'currency'=>'EUR']]);
$docs=nm127Read($manager,$namespace,[],['_id'=>1,'sku'=>1,'price'=>1,'currency'=>1],2);$rows=[];$interfaces=true;$exports=[];
foreach($docs as $doc){
$cell=new PriceCell127($doc['price'],$doc['currency']);$export=$cell->export();$exports[]=$export;
$interfaces=$interfaces && $cell->value() instanceof MongoDB\BSON\Decimal128Interface;
$rows[]=[$doc['sku'],nm127Label($cell->value(),$doc['currency']),$export['amount'],$export['currency']];
}
$wrongType=false;try{nm127Label('19.90','RUB');}catch(TypeError){$wrongType=true;}
$checks=['both_values_support_interface'=>$interfaces,'distinct_exports'=>$exports===[['amount'=>'19.90','currency'=>'RUB'],['amount'=>'2.50','currency'=>'EUR']],'literal_string_rejected_by_type'=>$wrongType,'no_float_conversion'=>is_string($exports[0]['amount']) && $exports[0]['amount']==='19.90','two_database_documents'=>array_column($docs,'_id')===[1,2]];
}finally{
foreach(array_reverse($owned) as $name){nm127Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(nm127Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
nm127Report('mongodb127-numbers-decimal-interface', ['Товар', 'Подпись на экране', 'amount для API', 'currency для API'], $rows, $checks, $runtime);
echo 'mongodb127-numbers-decimal-interface: saved result.html and result.json
';
Ожидаемый результат
mongodb127-numbers-decimal-interface: saved result.html and result.json
Реальные запись и чтение одной собственной учебной коллекции. Результат — понятные HTML/JSON таблицы. Helper и README входят в архив; ожидаемые отказы ввода отделены от ошибок базы.
Разберите результат: большие целые и десятичные значения в HTML и JSON показаны строками, чтобы их отображение не изменило точность. Реальный BSON-тип, выбранный документ или результат серверной операции проверяются отдельно в полной программе.
Открыть фактическую таблицу · JSON результата
Попробуйте изменить. Добавьте товар PEN с Decimal128("3.00") и валютой RUB. Его подпись будет 3.00 RUB, а export вернёт amount="3.00". Не складывайте CUP и BOX в один итог: у них разные валюты, и интерфейс сам не выполняет конвертацию.
Выбрать маршрут по задаче
| Задача | Выбор | Обязательная граница |
|---|---|---|
| Найти заказ по большому числовому ID | Строка → Int64 → Query | Диапазон signed64, числовая модель поля, точный вход |
| Сохранить малый счётчик как long | Явный Int64(42) | Не полагаться на BSON-тип обычного PHP int |
| Ввести цену | Строка→бизнес-проверка→Decimal128 | Формат, диапазон, валюта; не float |
| Посчитать выбранный заказ | Match→multiply→sum в MongoDB | Проверенные поля одной валюты, согласованность данных |
| Перенести BSON-типы в другую систему | Canonical Extended JSON | Поддержка формата и проверка полей на входе |
| Отдать значения браузеру | Явные строковые ID/amount и currency | Не переводить незаметно в JavaScript Number |
| Типизировать слой представления | Decimal128Interface | Интерфейс не заменяет денежную валидацию |
Справочник Int64, Decimal128 и Decimal128Interface
Новые карточки ниже раскрывают полные контракты. Конструктор и строковое представление Decimal128 сохраняют прежние канонические адреса: __construct и __toString.
| API | Для чего применять |
|---|---|
| MongoDB\BSON\Int64 | Сохранить и перенести целое signed64 с явным BSON-типом long, включая большие номера интеграции. |
| MongoDB\BSON\Int64::__construct | Создать точное signed64-значение из целого PHP или исходной десятичной строки. |
| MongoDB\BSON\Int64::jsonSerialize | Получить данные Extended JSON, в которых большой long представлен без потери цифр. |
| MongoDB\BSON\Int64::__toString | Получить точные цифры long для подписи, журнала или строкового API без промежуточного float. |
| MongoDB\BSON\Decimal128 | Представить десятичные значения для точного хранения и серверных расчётов цены, количества или измерений. |
| MongoDB\BSON\Decimal128Interface | Обозначить контракт точного десятичного объекта в параметрах, свойствах и результатах собственных PHP-функций. |
| MongoDB\BSON\Decimal128::jsonSerialize | Подготовить десятичное BSON-значение к JSON, сохранив его строковые цифры и метку $numberDecimal. |
| MongoDB\BSON\Decimal128Interface::__toString | Объявить доступ к строковому представлению десятичного объекта без промежуточного float. |
Закрепить числовой контракт поля
Выберите одно числовое поле своей интеграции и опишите его: целое или десятичное, допустимый диапазон, важны ли ведущие нули, где выполняются расчёты и какой тип получает браузер. Затем пройдите один из разобранных маршрутов от входа до видимого результата. Такой контракт поможет сохранить цифры при каждом преобразовании.
Продолжение: BSON Document и типизированный JSON; ObjectId и время UTC; ограниченное чтение результатов.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
MongoDB\BSON\Int64Сохранить и перенести целое signed64 с явным BSON-типом long, включая большие номера интеграции.
mongodb 1.5.0; new с 1.16.0 · mongodb
final class MongoDB\BSON\Int64 implements MongoDB\BSON\Type, JsonSerializable, Stringable {
final public function __construct(int|string $value);
final public function jsonSerialize(): mixed;
final public function __toString(): string;
}Сохранить и перенести целое signed64 с явным BSON-типом long, включая большие номера интеграции.
Сигнатура и параметры
final class MongoDB\BSON\Int64 implements MongoDB\BSON\Type, JsonSerializable, Stringable {
final public function __construct(int|string $value);
final public function jsonSerialize(): mixed;
final public function __toString(): string;
}| Вход / часть контракта | Значение и ограничение |
|---|---|
| value конструктора | PHP int или строка целого в диапазоне −9223372036854775808..9223372036854775807. Для внешнего большого номера исходная строка должна сохраниться до конструирования. |
| Константы и свойства | У класса не объявлены собственные публичные константы или поля значения. Чтение значения выполняется через строковое представление и сериализацию. |
Что получит вызывающий код
Объект задаёт BSON long при кодировании даже для 42. Класс поддерживает сравнения, арифметические и побитовые операции. Raw BSON Document/PackedArray/Iterator сохраняют long как Int64. Обычный драйверный Cursor на x64 может вернуть PHP int; на 32-битной платформе для long нужен объект. Само значение не открывает соединение и не делает запрос.
Ошибки
Недопустимая целочисленная строка или выход за signed64 при конструировании дают MongoDB\Driver\Exception\InvalidArgumentException. Другие операции имеют свои правила: арифметическое переполнение не следует считать гарантированным исключением — в проверенной среде MAX+1 даёт float. Ошибки подключения/запроса относятся к Driver, а не к созданию Int64.
Версии и совместимость
Класс существует с mongodb 1.5.0 для декодирования. Публичный конструктор и перегрузка операторов появились с 1.16.0. С 1.12.0 на PHP 8 класс явно реализует Stringable. С 2.0.0 интерфейс Serializable удалён; JsonSerializable и строковое представление остаются.
Два практических применения
Найти заказ большого внешнего ID
Номер 9007199254740993 остаётся точным и выбирает статус «готов»; неправильный строковый фильтр показывает 0 совпадений.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Сохранить небольшой счётчик с фиксированным BSON-типом
Значение 42 сохраняется как long, а выходы за границы отбрасываются до записи. Отдельный результат MAX+1 объясняет предел арифметики.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
Это конечный 64-битный диапазон, а не произвольная точность. Конструктор не восстанавливает цифры после float и не сохраняет ведущие нули. Если поле по смыслу текстовое, выберите string. Проверка именно BSON long через $type отличается от числового сравнения, где числовые типы могут сопоставляться.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
MongoDB\BSON\Int64::__constructСоздать точное signed64-значение из целого PHP или исходной десятичной строки.
mongodb 1.16.0: публичный конструктор · mongodb
final public function MongoDB\BSON\Int64::__construct(int|string $value)Создать точное signed64-значение из целого PHP или исходной десятичной строки.
Сигнатура и параметры
final public function MongoDB\BSON\Int64::__construct(int|string $value)| Вход / часть контракта | Значение и ограничение |
|---|---|
| value: int|string | Целое в пределах signed64. Для непомещающегося в PHP int значения на 32-битной системе используют строку. В наших x64-программах строка также предотвращает промежуточный float. |
| Строковый формат | Текст должен разбираться как 64-битное целое. Дробь 12.5, abc и числа за диапазоном отвергаются. Приём строки 07 не означает сохранение её оформления: вывод будет 7. |
Что получит вызывающий код
new возвращает объект Int64. Конструктор сам по себе не имеет возвращаемого значения и не записывает данные в MongoDB. Последующее BSON-кодирование объекта задаёт long. Вход 42 остаётся long даже там, где обычный PHP int 42 мог быть закодирован как int32.
Ошибки
MongoDB\Driver\Exception\InvalidArgumentException: не удалось разобрать значение как signed64, оно выходит за диапазон либо нарушен разбор аргументов. Ошибки вызова неподходящего PHP-типа регулируются также правилами PHP. В примере перехватывается только ожидаемый отказ конкретной строки; ошибки записи не скрываются.
Версии и совместимость
Прямой new доступен с mongodb 1.16.0. Указание 1.5.0 у класса относится к более раннему существованию объекта декодирования, а не разрешению публично вызвать конструктор.
Два практических применения
Отфильтровать неверные строки импорта
Две границы и 42 записываются, переполнения/дробь/abc становятся четырьмя отказами. Каждое решение видно в таблице.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Вернуть явный BSON long после обычного чтения
Точный PHP int, полученный Cursor на x64, преобразуется в Int64 перед Canonical-экспортом. Это сохраняет тип и для небольшого номера.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
Не передавайте уже округлённый float через (string), надеясь восстановить исходные цифры. Для идентификаторов заранее определите: это число или текст, нужно ли сохранять ведущие нули. Для счётчика задайте собственные границы до изменения значения.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
MongoDB\BSON\Int64::jsonSerializeПолучить данные Extended JSON, в которых большой long представлен без потери цифр.
mongodb 1.5.0; new с 1.16.0 · mongodb
final public function MongoDB\BSON\Int64::jsonSerialize(): mixedПолучить данные Extended JSON, в которых большой long представлен без потери цифр.
Сигнатура и параметры
final public function MongoDB\BSON\Int64::jsonSerialize(): mixedМетод не принимает параметры.
Что получит вызывающий код
PHP-значение для JSON-кодирования: в текущем API структура с ключом $numberLong и десятичным числом в строке. json_encode($int64) вызывает метод автоматически. Для Int64 это Canonical Extended JSON. Метод не возвращает готовый JSON-текст и не делает запрос в базу.
Ошибки
Официальная страница документирует MongoDB\Driver\Exception\InvalidArgumentException при ошибке разбора аргументов. У метода нет параметров. Последующая ошибка json_encode — отдельный отказ JSON API; в примере JSON_THROW_ON_ERROR не позволяет скрыть его.
Версии и совместимость
Метод существует с mongodb 1.5.0. Для ручного создания объекта в современных примерах нужен публичный конструктор с 1.16.0. Удаление Serializable в 2.0.0 не удаляет JsonSerializable.
Два практических применения
Перенести числовой ID в типизированную интеграцию
json_encode использует сериализатор и сохраняет MAX через $numberLong. В полной программе Canonical-документ восстанавливает Int64 и пригодный для базы фильтр.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Диагностировать ошибку обычного JSON-декодирования
Прямой результат jsonSerialize сравнивается с json_decode(..., true). Полученный array объясняет, почему обработчик не должен считать любой разобранный JSON уже восстановленным BSON-значением.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
json_decode не восстанавливает Int64: он возвращает структуру с $numberLong. Для BSON-восстановления используйте Document::fromJSON. Если браузер ждёт простой ID, отдельно согласуйте строковое поле вместо неявного большого JSON number. Не обобщайте Canonical-поведение Int64 на все BSON-классы.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
MongoDB\BSON\Int64::__toStringПолучить точные цифры long для подписи, журнала или строкового API без промежуточного float.
mongodb 1.5.0; new с 1.16.0 · mongodb
final public function MongoDB\BSON\Int64::__toString(): stringПолучить точные цифры long для подписи, журнала или строкового API без промежуточного float.
Сигнатура и параметры
final public function MongoDB\BSON\Int64::__toString(): stringМетод не принимает параметры.
Что получит вызывающий код
Десятичное строковое представление целого. Явный вызов __toString и приведение (string)$number дают строку цифр, со знаком для отрицательного значения. Этот текст не содержит $numberLong и сам по себе не переносит BSON-тип.
Ошибки
Отдельные исключения метода официальная страница не перечисляет. Недопустимые значения отбрасываются при создании Int64. Проблемы последующего HTML/JSON/файлового вывода обрабатываются на соответствующей границе.
Версии и совместимость
С mongodb 1.5.0. В PHP 8 явный Stringable указан с mongodb 1.12.0. Для самостоятельного new в программах нужен mongodb 1.16.0 или новее.
Два практических применения
Показать точный номер найденного заказа
На экран попадают все цифры 9007199254740993 после реального чтения, а рядом — его статус. Читатель видит, что именно вернул запрос.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Показать допустимые границы и решения импорта
Минимальный отрицательный и максимальный положительный signed64 выводятся без потерь; отвергнутые строки не смешиваются с успешными значениями.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
Строка не сохраняет исходные ведущие нули и не становится автоматически числовым фильтром MongoDB. При выводе в HTML используется экранирование; при передаче в API сохраняется согласованный тип string. Не приводите результат к float ради форматирования большого ID.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
MongoDB\BSON\Decimal128Представить десятичные значения для точного хранения и серверных расчётов цены, количества или измерений.
mongodb 1.2.0 · mongodb
final class MongoDB\BSON\Decimal128 implements MongoDB\BSON\Decimal128Interface, MongoDB\BSON\Type, JsonSerializable, Stringable {
final public function __construct(string $value);
final public function jsonSerialize(): mixed;
final public function __toString(): string;
}Представить десятичные значения для точного хранения и серверных расчётов цены, количества или измерений.
Сигнатура и параметры
final class MongoDB\BSON\Decimal128 implements MongoDB\BSON\Decimal128Interface, MongoDB\BSON\Type, JsonSerializable, Stringable {
final public function __construct(string $value);
final public function jsonSerialize(): mixed;
final public function __toString(): string;
}| Вход / часть контракта | Значение и ограничение |
|---|---|
| value конструктора: string | Исходное десятичное значение. Для цены 19.90 передайте строку '19.90' до любого float-преобразования. |
| Точность и диапазон | До 34 значащих десятичных цифр; конечный диапазон экспонент. Нормальный скорректированный показатель — от −6143 до +6144; субнормальные значения и точное изменение экспоненты имеют дополнительные правила формата. |
| Специальные значения | BSON допускает NaN и ±Infinity. Их допустимость для цены/измерения определяет приложение. |
| Константы / свойства | Собственные публичные константы и свойства значения не объявлены. Доступны три перечисленных метода. |
Что получит вызывающий код
Объект BSON decimal. Decimal128 можно передать в серверную агрегацию и получить обратно точный десятичный результат в пределах формата. __toString возвращает его текст, jsonSerialize — представление для JSON. Это не денежный калькулятор с автоматическими валютами, налогами и произвольной точностью.
Ошибки
Конструктор выбрасывает MongoDB\Driver\Exception\InvalidArgumentException для недопустимой или непредставимой точно десятичной строки. Точное преобразование экспоненты допустимо, потеря значащих цифр — нет. NaN/Infinity могут технически создаваться, поэтому проверка прикладной цены нужна отдельно. Ошибки серверных операторов и подключения не являются ошибками конструктора.
Версии и совместимость
Класс с mongodb 1.2.0; BSON Decimal128 поддерживается сервером MongoDB 3.4+. Decimal128Interface реализуется с 1.3.0. Явный Stringable на PHP 8 — с mongodb 1.12.0. В mongodb 2.0.0 удалён Serializable, но сохраняется JsonSerializable.
Подробные канонические карточки уже находятся в базовой главе: Decimal128::__construct и Decimal128::__toString. Здесь они включены в полный обзор класса и связаны с дальнейшими сценариями.
Два практических применения
Выбрать подходящий тип хранения цены каталога
Сервер показывает decimal/double/string для трёх входных путей; фильтр с явным типом выбирает только правильную цену.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Получить итог конкретного заказа на стороне базы
Две строки A17 дают 40.10 RUB через $multiply/$sum. Другой заказ и валюта исключены до группировки.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
Считайте значащие цифры, а не все символы строки: некоторые 35-значные записи с конечным нулём представимы точно. Строка результата не всегда имеет два знака и может быть научной записью. Цена должна входить исходным текстом; пересборка из float не восстанавливает потерянную точность. Для сумм фиксируйте валюту и правила округления.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
MongoDB\BSON\Decimal128InterfaceОбозначить контракт точного десятичного объекта в параметрах, свойствах и результатах собственных PHP-функций.
mongodb 1.3.0; типы обязательны с 2.0.0 · mongodb
interface MongoDB\BSON\Decimal128Interface {
public function __toString(): string;
}Обозначить контракт точного десятичного объекта в параметрах, свойствах и результатах собственных PHP-функций.
Сигнатура и параметры
interface MongoDB\BSON\Decimal128Interface {
public function __toString(): string;
}Интерфейс используется через перечисленные методы; самостоятельное создание не предусмотрено.
Что получит вызывающий код
Интерфейс объявляет единственный метод __toString(): string. Встроенный Decimal128 его реализует. Интерфейс можно использовать как тип параметра, возвращаемого значения или свойства. Сам интерфейс не создаётся через new и не выполняет запись в MongoDB.
Ошибки
PHP требует совместимые сигнатуры реализации и подходящий тип аргумента. Передача обычной строки в параметр Decimal128Interface даёт TypeError; это не ошибка разбора Decimal128. Интерфейс не описывает отдельную политику исключений прикладного класса и не проверяет финансовые ограничения.
Версии и совместимость
С mongodb 1.3.0. Начиная с 1.15.0 на PHP 8 возвращаемый string был tentative: несовместимые объявления вызывали deprecation. С 2.0.0 возвращаемые типы обязательны; исторический атрибут ReturnTypeWillChange не заменяет корректную современную сигнатуру.
Два практических применения
Общая подпись цены для каталога
nm127Label принимает интерфейс и возвращает 19.90 RUB; строковый ошибочный аргумент явно отклоняется.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Объект представления для отдельного API
PriceCell127 хранит свойство с типом интерфейса, возвращает его через value() и отдельно строит DTO. Вся цепочка выполняется для двух реальных документов и двух валют.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
Это PHP-контракт объекта, а не UI и не формат JSON. В него не входят jsonSerialize, валюта, сложение или SQL-подобные операции. Пользовательское implements само по себе не обещает BSON-кодирование как Decimal128. Требования к цене задаются до слоя представления.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
MongoDB\BSON\Decimal128::jsonSerializeПодготовить десятичное BSON-значение к JSON, сохранив его строковые цифры и метку $numberDecimal.
mongodb 1.2.0 · mongodb
final public function MongoDB\BSON\Decimal128::jsonSerialize(): mixedПодготовить десятичное BSON-значение к JSON, сохранив его строковые цифры и метку $numberDecimal.
Сигнатура и параметры
final public function MongoDB\BSON\Decimal128::jsonSerialize(): mixedМетод не принимает параметры.
Что получит вызывающий код
PHP-структура, пригодная для json_encode. Для Decimal128 это $numberDecimal с десятичной строкой. json_encode($price) вызывает метод автоматически. Конкретная форма Decimal128 одинакова в legacy и Canonical Extended JSON; для полного современного документа формат выбирается через Document явно.
Ошибки
Официальная страница указывает MongoDB\Driver\Exception\InvalidArgumentException при ошибке разбора аргументов; метод не принимает параметры. Ошибки последующего json_encode обрабатываются отдельно, например JSON_THROW_ON_ERROR.
Версии и совместимость
С mongodb 1.2.0. Общий контракт BSON jsonSerialize описан как legacy Extended JSON; совпадение Decimal128 с Canonical не следует распространять на другие BSON-типы. Удаление Serializable в 2.0.0 не отменяет этот метод.
Два практических применения
Передать цену между BSON-приложениями
$numberDecimal сохраняет 19.90; Canonical → fromJSON восстанавливает Decimal128 и пригодный фильтр.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Выбрать совместимый ответ для браузерного API
Результат автоматического json_encode сравнивается с явным DTO. Клиенту, который ждёт amount и currency, передаётся согласованная простая структура, а не неожиданное вложенное поле $numberDecimal.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
Возвращаемое значение — не JSON-текст. json_decode не создаёт Decimal128 автоматически. Специальные значения NaN/Infinity также могут быть представлены, но это не делает их допустимой ценой. Для собственного HTTP API согласуйте amount как строку и currency отдельно.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
MongoDB\BSON\Decimal128Interface::__toStringОбъявить доступ к строковому представлению десятичного объекта без промежуточного float.
mongodb 1.3.0; типы обязательны с 2.0.0 · mongodb
abstract public function MongoDB\BSON\Decimal128Interface::__toString(): stringОбъявить доступ к строковому представлению десятичного объекта без промежуточного float.
Сигнатура и параметры
abstract public function MongoDB\BSON\Decimal128Interface::__toString(): stringМетод не принимает параметры.
Что получит вызывающий код
Строка, которую возвращает реализация. У встроенного Decimal128 — десятичное представление BSON-значения. Вызов (string)$object обращается к методу. Интерфейс не требует локализованного формата, валюты или ровно двух дробных знаков.
Ошибки
Страница интерфейса не задаёт отдельные исключения. Конкретная реализация определяет свои отказы; PHP требует совместимый string-результат. Ошибка передачи строки в параметр Decimal128Interface возникает до тела функции.
Версии и совместимость
С mongodb 1.3.0. Tentative string на PHP 8 с mongodb 1.15.0 стал обязательным с 2.0.0. Современная реализация должна объявлять : string.
Два практических применения
Сформировать подпись товара
nm127Label использует (string)$amount и возвращает 19.90 RUB, тогда как метод value возвращает объект для других потребителей.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Сформировать простой DTO
Тот же строковый контракт даёт amount="19.90" для браузера; валюта передаётся отдельно, без притворного BSON-восстановления обычным json_decode.
Исходные данные, короткий вызов, полная программа и результат · Фактическая таблица
Ограничения и связанные объяснения
Полученная строка сама по себе не переносит BSON-тип. Для полного типизированного обмена нужен Extended JSON. Строку 19.90 нельзя складывать PHP-оператором +, рассчитывая сохранить десятичную модель. Разделяйте серверный расчёт и вывод.
Базовые значения BSON; Document и Extended JSON; От исходной строки до точного результата.
Источники и обсуждения
- MongoDB\BSON\Int64
- MongoDB\BSON\Int64::__construct
- MongoDB\BSON\Int64::jsonSerialize
- MongoDB\BSON\Int64::__toString
- MongoDB\BSON\Decimal128
- MongoDB\BSON\Decimal128Interface
- MongoDB\BSON\Decimal128::jsonSerialize
- MongoDB\BSON\Decimal128Interface::__toString
- Моделирование денежных данных
- MongoDB Extended JSON
- Правила Decimal128
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


