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

Дерево руководства
Глава 146 · После первого каталога MongoDB и чтения через драйвер
MongoDB: ObjectId, даты UTC и Decimal128 — значения формы и интеграции
Найти товар по ID, сохранить местное время, выбрать календарный день и посчитать точную десятичную сумму.
Зачем ID, дате и сумме собственные типы
Ссылка на товар, время встречи и цена приходят в PHP как текст. Читателю нужно получить конкретный товар, правильные часы и точный итог счёта. MongoDB различает строку, идентификатор, дату и десятичное число: одинаковое изображение в отладчике ещё не означает одинаковые данные для поиска и расчёта.
До этой главы полезно пройти первый документ и коллекцию и запись и чтение через драйвер. Здесь разобраны три новых типа значений. PHP-массив хранит поля документа; объекты MongoDB\BSON в отдельных полях говорят драйверу, каким BSON-типом передать значение.
- Форма/URL: строка 65a000000000000000000001
- PHP: new ObjectId с этой строкой
- BSON: 12-байтовый ObjectId в условии _id
- MongoDB: найден документ Кружка
- PHP: (string) прочитанного ObjectId
- Ссылка/JSON: снова текст 65a000000000000000000001
| Потребность | PHP перед записью | Что хранится в BSON | Что получит пользователь |
|---|---|---|---|
| Открыть конкретную карточку | new ObjectId("65a000000000000000000001") | ObjectId, не string | Кружка по точному ID |
| Назначить встречу | new UTCDateTime($провереннаяДата) | Date: миллисекунды UTC | 15:30 в Москве = 12:30 UTC |
| Посчитать стоимость | new Decimal128("19.90") | Decimal128, не double | 19.90 × 3 = 59.70 |
Можно представить тип как подпись на контейнере: «идентификатор», «миллисекунды», «десятичная сумма». Подпись определяет, какие операции имеют смысл. Но это только модель: BSON реально кодирует тип и байты значения, не произвольную поясняющую надпись; PHP-объект сам по себе не является записью на сервере.
Самое короткое действие: текст → ObjectId → текст
<?php
$id = new MongoDB\BSON\ObjectId('65a000000000000000000001');
echo (string) $id; // 65a000000000000000000001$id — переменная; = сохраняет результат; new создаёт значение класса; скобки передают аргумент; ; заканчивает инструкцию. (string) запрашивает строковое представление, echo выводит его. Этот вызов конструктора ещё ничего не записывает: сервер появляется в полном примере ниже. В нём те же ID и преобразование проверяются после настоящей записи/чтения.
Запись $id->getTimestamp() вызывает метод именно этого объекта. Полные имена MongoDB\BSON\… относятся к расширению, а не к библиотеке Composer. Двойной :: в справочном имени обозначает метод класса; в показанных программах используется созданный объект и ->. Подробнее о синтаксисе: переменные, класс и объект, параметры и возвращаемое значение.
Подготовка и самостоятельный запуск
Нужны PHP 8.4+ CLI x64, расширение mongodb 2.5+ и отдельный локальный MongoDB 8.0. Контрольная среда: PHP 8.5.11, mongodb 2.5.5, MongoDB 8.0.34. Composer не нужен: используются классы расширения. Порядок установки сервера/DLL есть в README каждого архива и в первой главе MongoDB.
Распакуйте архив, задайте PHPMAN_MONGODB_URI и PHPMAN_MONGODB_DATABASE, выполните php example.php, откройте result.html. JSON содержит те же строки; metrics.json записывает версии, проверки и SHA256. Helper bv117-helper.php принимает только явно заданный 127.0.0.1:порт, без учётных данных. Это ограничение локального упражнения, а не совет отключать защиту рабочей базы.
Программа создаёт новую коллекцию mongodb117_bson_… со случайным суффиксом. Владение фиксируется после успешного create; finally удаляет только своё точное имя, а listCollections подтверждает отсутствие. Нужны соответствующие права своей учебной базы, listCollections и buildInfo. При аварийном прекращении процесса finally не гарантируется; удалять неизвестные объекты по префиксу нельзя.
Принять ID из ссылки и найти нужный документ
Пользователь открывает ссылку на товар. В адресе ID — текст, а в коллекции поле _id хранит ObjectId. Нужно найти ровно этот товар и отличить неверную запись ID от правильно записанного, но отсутствующего товара.
Входные данные
Одна карточка «Кружка» с _id ObjectId("65a000000000000000000001"). Для сравнения программа запрашивает ObjectId, исходную строку и другой валидный ID.
- URL: текст 65a000000000000000000001
- new ObjectId проверяет форму
- Фильтр передаёт BSON ObjectId
- Сервер сопоставляет тип и значение
- Кружка → HTML; ID → строка для ссылки
Короткое действие
$text = '65a000000000000000000001';
$id = new MongoDB\BSON\ObjectId($text);
$filter = ['_id' => $id]; // BSON ObjectId, не строка.
// В полном примере такой фильтр находит Кружку.
$linkId = (string) $id; // Строка для URL: 65a000000000000000000001Это выделенный механизм. Значения вроде $input, $document и $result принадлежат полному сценарию ниже; если нужна запускаемая программа целиком, используйте её архив.
Что получится
| Действие | Результат |
|---|---|
| Типизированный фильтр | Кружка |
| Только строка в фильтре | 0 документов |
| Валидный, но отсутствующий ID | 0 документов |
| Неверный формат | InvalidArgumentException |
| ID для ссылки | 65a000000000000000000001 |
| Компонент времени ID, секунд | 1704984576 |
| ID без аргумента | 24 hex-символа |
Разбор по шагам
new создаёт объект класса MongoDB\BSON\ObjectId из установленного расширения. В скобках передаётся аргумент — строка из 24 шестнадцатеричных символов. Сам конструктор не подключается к базе и не ищет товар.
Массив ['_id' => $id] описывает условие запроса. Драйвер кодирует объект как BSON ObjectId: такой фильтр находит Кружку. Условие ['_id' => $input] передаёт BSON string и не находит документ. Похожая строка не превращается в ObjectId автоматически.
Неверный формат перехватывается отдельно от пустого результата. В обработчике HTTP это разные решения: ошибка входного параметра и отсутствие доступного документа. Пример CLI показывает различие, не назначая HTTP-коды за всё приложение.
Приведение (string) вызывает __toString и возвращает текст для ссылки. ObjectId без аргумента генерирует новый ID; пример проверяет форму, не обещая заранее известное случайное значение. getTimestamp показывает временную часть ID в секундах — ниже разберём, почему это не дата заказа.
Границы и типичные ошибки
Валидный ObjectId не доказывает наличие документа, владение им или право просмотра. Проверяйте строковый вход, затем фильтруйте разрешённую область данных. Не передавайте массив из $_GET прямо в условие. ID не пароль; новая генерация при чтении не исправит неверную ссылку.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/bv117-helper.php';
[$manager,$database]=bv117Environment();
$server=bv117Command($manager,$database,['buildInfo'=>1])[0]['version'];
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$server];
$collection='mongodb117_bson_'.bin2hex(random_bytes(8));
$namespace=$database.'.'.$collection; $owned=false; $details=[];
try {
if(bv117Exists($manager,$database,$collection)){throw new RuntimeException('Fixture name collision');}
bv117Command($manager,$database,['create'=>$collection]);
$owned=true;
$input='65a000000000000000000001';
$id=new MongoDB\BSON\ObjectId($input);
bv117Insert($manager,$namespace,[['_id'=>$id,'title'=>'Кружка']]);
$options=['projection'=>['_id'=>1,'title'=>1],'limit'=>1];
$typed=bv117Read($manager,$namespace,['_id'=>$id],$options);
$plain=bv117Read($manager,$namespace,['_id'=>$input],$options);
$absent=bv117Read($manager,$namespace,['_id'=>new MongoDB\BSON\ObjectId('65a000000000000000000099')],$options);
$invalidCaught=false;
try{new MongoDB\BSON\ObjectId('not-an-object-id');}
catch(MongoDB\Driver\Exception\InvalidArgumentException $failure){$invalidCaught=true;}
$generated=new MongoDB\BSON\ObjectId();
$generatedValid=preg_match('/\A[0-9a-f]{24}\z/D',(string)$generated)===1;
$stored=$typed[0]['_id'];
$rows=[
['Типизированный фильтр',$typed[0]['title']],
['Только строка в фильтре',(string)count($plain).' документов'],
['Валидный, но отсутствующий ID',(string)count($absent).' документов'],
['Неверный формат',$invalidCaught?'InvalidArgumentException':'ошибка'],
['ID для ссылки',(string)$stored],
['Компонент времени ID, секунд',(string)$stored->getTimestamp()],
['ID без аргумента',$generatedValid?'24 hex-символа':'ошибка'],
];
$checks=['typed_id_found'=>count($typed)===1 && $typed[0]['title']==='Кружка',
'string_id_not_equal'=>$plain===[],'valid_absent_id'=>$absent===[],
'invalid_id_caught'=>$invalidCaught,'objectid_roundtrip'=>$stored instanceof MongoDB\BSON\ObjectId && (string)$stored===$input,
'timestamp_component'=>$stored->getTimestamp()===1704984576,'generated_shape'=>$generatedValid];
} finally {
if($owned){bv117Command($manager,$database,['drop'=>$collection]);}
}
$checks['owned_collection_removed']=!bv117Exists($manager,$database,$collection);
bv117Report('mongodb117-bson-objectid-route', ['Действие', 'Результат'], $rows, $checks, $runtime, $details);
echo 'mongodb117-bson-objectid-route: saved result.html and result.json
';
Ожидаемый результат
mongodb117-bson-objectid-route: saved result.html and result.json
Запустите php example.php и откройте result.html. В архив включены bv117-helper.php и README с установкой/запуском. Данные создаются заново в отдельной собственной коллекции; соседние программы запускать не требуется.
Открыть фактический результат HTML · Скачать результат JSON
Упражнение с ориентиром результата. Замените искомый ID на валидный ...0099: получите пустую выборку. Затем передайте not-an-object-id: выполнится обработка неверного аргумента. Почему это нельзя объединять в «ошибку базы»?
Практическая проблема: MongoDB check if id is a valid BSON PHP. Неверный ID из входа вызывает исключение конструктора; нужно различать форму ID и отсутствие документа. Урок отдельно показывает эти два исхода, не копируя ответ. Вопрос задаёт ситуацию; правила проверены по первичным источникам, код и данные урока оригинальные.
Сохранить время пользователя и вернуть его в нужной зоне
Пользователь выбирает время встречи 15:30 в Москве. В базе нужен однозначный момент, а на экране — привычные пользователю часы. Если перепутать секунды и миллисекунды, встреча незаметно окажется в 1970 году.
Входные данные
Строка 2026-10-29 15:30:00.123 и зона Europe/Moscow. starts_at и timezone_id сохраняются раздельно. Поле seconds_as_ms намеренно хранит неправильные единицы для видимого сравнения.
- Форма: 15:30:00.123 + зона
- DateTimeImmutable разбирает календарь
- UTCDateTime берёт момент в миллисекундах
- BSON Date: UTC 12:30:00.123
- toDateTimeImmutable → UTC
- setTimezone → 15:30 в Москве
Короткое действие
$local = new DateTimeImmutable(
'2026-10-29 15:30:00.123', new DateTimeZone('Europe/Moscow')
);
$bsonDate = new MongoDB\BSON\UTCDateTime($local);
$utc = $bsonDate->toDateTimeImmutable();
echo $utc->format('Y-m-d H:i:s.v P');
// 2026-10-29 12:30:00.123 +00:00Это выделенный механизм. Значения вроде $input, $document и $result принадлежат полному сценарию ниже; если нужна запускаемая программа целиком, используйте её архив.
Что получится
| Шаг | Значение |
|---|---|
| Форма + зона | 2026-10-29 15:30:00.123 Europe/Moscow |
| После чтения: UTC | 2026-10-29 12:30:00.123 +00:00 |
| Показ пользователю | 2026-10-29 15:30:00.123 +03:00 |
| Миллисекунды BSON | 1793277000123 |
| Секунды DateTime | 1793277000 |
| Секунды ошибочно приняты за мс: год | 1970 |
| 30 февраля | отклонено до записи |
Разбор по шагам
Дата из формы сама по себе не содержит зоны. bv117Date — функция архива: createFromFormat читает установленный формат, getLastErrors обнаруживает ошибки и переполнения вроде 30 февраля, повторное форматирование подтверждает совпадение. ! сбрасывает неуказанные поля; v означает миллисекунды.
DateTimeImmutable — PHP-объект календарного времени. UTCDateTime представляет BSON Date. Передача DateTimeImmutable в конструктор избавляет от ручного умножения секунд: драйвер берёт абсолютный момент с миллисекундами.
BSON Date хранит миллисекунды от начала эпохи Unix. Имя Europe/Moscow записано отдельно в timezone_id. Один момент можно показать в разных зонах; setTimezone возвращает новый DateTimeImmutable, не меняя исходный UTC-объект.
(string) $date выдаёт 1793277000123, а getTimestamp у PHP-даты — 1793277000 секунд. Передав второе целое в UTCDateTime, программа намеренно получает 1970 год. Это корректный тип с неверным смыслом единиц, поэтому исключение не обязательно.
Границы и типичные ошибки
С mongodb 2.0 строка и float не принимаются конструктором UTCDateTime: сначала разберите календарную строку в DateTimeInterface. BSON Date имеет точность миллисекунд. Неоднозначный час при осеннем переводе часов нельзя разрешить одной проверкой формата — определите выбор смещения или запросите его у отправителя.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/bv117-helper.php';
[$manager,$database]=bv117Environment();
$server=bv117Command($manager,$database,['buildInfo'=>1])[0]['version'];
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$server];
$collection='mongodb117_bson_'.bin2hex(random_bytes(8));
$namespace=$database.'.'.$collection; $owned=false; $details=[];
try {
if(bv117Exists($manager,$database,$collection)){throw new RuntimeException('Fixture name collision');}
bv117Command($manager,$database,['create'=>$collection]);
$owned=true;
$text='2026-10-29 15:30:00.123'; $zone='Europe/Moscow';
$local=bv117Date($text,$zone);
$correct=new MongoDB\BSON\UTCDateTime($local);
$wrong=new MongoDB\BSON\UTCDateTime($local->getTimestamp()); // Намеренно неверные единицы.
bv117Insert($manager,$namespace,[['_id'=>201,'starts_at'=>$correct,'timezone_id'=>$zone,'seconds_as_ms'=>$wrong]]);
$document=bv117Read($manager,$namespace,['_id'=>201],
['projection'=>['_id'=>0,'starts_at'=>1,'timezone_id'=>1,'seconds_as_ms'=>1],'limit'=>1])[0];
$utc=$document['starts_at']->toDateTimeImmutable();
$display=$utc->setTimezone(new DateTimeZone($document['timezone_id']));
$badDateCaught=false;
try{bv117Date('2026-02-30 15:30:00.123',$zone);}
catch(InvalidArgumentException $failure){$badDateCaught=true;}
$rows=[
['Форма + зона',$text.' '.$zone],
['После чтения: UTC',$utc->format('Y-m-d H:i:s.v P')],
['Показ пользователю',$display->format('Y-m-d H:i:s.v P')],
['Миллисекунды BSON',(string)$document['starts_at']],
['Секунды DateTime',(string)$utc->getTimestamp()],
['Секунды ошибочно приняты за мс: год',$document['seconds_as_ms']->toDateTimeImmutable()->format('Y')],
['30 февраля',$badDateCaught?'отклонено до записи':'ошибка'],
];
$checks=['stored_date_type'=>$document['starts_at'] instanceof MongoDB\BSON\UTCDateTime,
'utc_exact'=>$utc->format('Y-m-d H:i:s.v P')==='2026-10-29 12:30:00.123 +00:00',
'local_roundtrip'=>$display->format('Y-m-d H:i:s.v')===$text,
'milliseconds_exact'=>(string)$document['starts_at']==='1793277000123',
'immutable_utc_unchanged'=>$utc->getOffset()===0 && $display->getOffset()===10800,
'seconds_mistake_visible'=>$document['seconds_as_ms']->toDateTimeImmutable()->format('Y')==='1970',
'invalid_calendar_date_caught'=>$badDateCaught];
} finally {
if($owned){bv117Command($manager,$database,['drop'=>$collection]);}
}
$checks['owned_collection_removed']=!bv117Exists($manager,$database,$collection);
bv117Report('mongodb117-bson-local-time', ['Шаг', 'Значение'], $rows, $checks, $runtime, $details);
echo 'mongodb117-bson-local-time: saved result.html and result.json
';
Ожидаемый результат
mongodb117-bson-local-time: saved result.html and result.json
Запустите php example.php и откройте result.html. В архив включены bv117-helper.php и README с установкой/запуском. Данные создаются заново в отдельной собственной коллекции; соседние программы запускать не требуется.
Открыть фактический результат HTML · Скачать результат JSON
Упражнение с ориентиром результата. Покажите starts_at в UTC и Europe/Berlin. Абсолютный момент и строка миллисекунд должны совпасть, хотя часы будут другими.
Практическая проблема: How to store correctly MongoDB ISODate from PHP script. Передача секунд как миллисекунд даёт дату около 1970 года. В оригинальном примере правильное и намеренно ошибочное поле показаны рядом после записи и чтения. Вопрос задаёт ситуацию; правила проверены по первичным источникам, код и данные урока оригинальные.
Выбрать местный день полуинтервалом, включая переход часов
Отчёт должен показать события за календарный день пользователя. День при переходе на летнее время может длиться 23 часа; прибавить 86 400 секунд и назвать это «следующий день» неправильно.
Входные данные
29 марта 2026 года в Europe/Berlin. События стоят за миллисекунду до начала, точно в начале, за миллисекунду до конца и точно в начале следующего дня.
- Дата отчёта + Europe/Berlin
- Локальная полночь 29 марта
- Следующая местная полночь 30 марта
- Обе границы → UTCDateTime
- Сервер: at >= начало, at < конец
- Получены только ID 2 и 3
Короткое действие
$zone = new DateTimeZone('Europe/Berlin');
$start = new DateTimeImmutable('2026-03-29 00:00:00', $zone);
$nextDay = $start->modify('+1 day');
$filter = ['at' => [
'$gte' => new MongoDB\BSON\UTCDateTime($start),
'$lt' => new MongoDB\BSON\UTCDateTime($nextDay),
]];
// День начинается включительно, следующая полночь не включается.Это выделенный механизм. Значения вроде $input, $document и $result принадлежат полному сценарию ниже; если нужна запускаемая программа целиком, используйте её архив.
Что получится
| Запись | Местное время Europe/Berlin | UTC | Входит в день? |
|---|---|---|---|
| 1 | 2026-03-28 23:59:59.999 +01:00 | 2026-03-28T22:59:59.999Z | нет |
| 2 | 2026-03-29 00:00:00.000 +01:00 | 2026-03-28T23:00:00.000Z | да |
| 3 | 2026-03-29 23:59:59.999 +02:00 | 2026-03-29T21:59:59.999Z | да |
| 4 | 2026-03-30 00:00:00.000 +02:00 | 2026-03-29T22:00:00.000Z | нет |
| Длина окна | 23 часа | 82800000 мс | — |
Разбор по шагам
Полуинтервал [начало, конец) включает левую границу и исключает правую. В Query этому соответствуют $gte и $lt. Событие на следующей полуночи не попадёт одновременно в соседние отчёты.
Сначала строятся две календарные полуночи в Europe/Berlin: начало и modify("+1 day"). Затем обе превращаются в BSON Date. Смещение меняется с +01:00 на +02:00; расстояние между моментами — 82 800 000 миллисекунд, то есть 23 часа.
Условие выполняет MongoDB. В программе отдельно читаются четыре крошечные контрольные строки для объяснения границ в таблице. Рабочий отчёт должен получать только отфильтрованные документы, выбранные поля и нужный лимит, с подходящим индексом.
Сортировка at, затем _id задаёт устойчивый порядок при равном времени. Проверяется и соседнее окно: ID 4 принадлежит следующему дню. Операторы $gte/$lt заключены в одинарные кавычки PHP, чтобы знак $ не стал началом переменной.
Границы и типичные ошибки
Это календарный день, а не «последние 24 часа». Сравнивайте даты BSON с датами BSON: строка "2026-03-29" не задаёт момент автоматически. Индекс под tenant/status/at выбирают по реальному фильтру; проверка четырёх границ не является замером производительности.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/bv117-helper.php';
[$manager,$database]=bv117Environment();
$server=bv117Command($manager,$database,['buildInfo'=>1])[0]['version'];
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$server];
$collection='mongodb117_bson_'.bin2hex(random_bytes(8));
$namespace=$database.'.'.$collection; $owned=false; $details=[];
try {
if(bv117Exists($manager,$database,$collection)){throw new RuntimeException('Fixture name collision');}
bv117Command($manager,$database,['create'=>$collection]);
$owned=true;
$zone=new DateTimeZone('Europe/Berlin');
$begin=new DateTimeImmutable('2026-03-29 00:00:00',$zone);
$end=$begin->modify('+1 day');
$from=new MongoDB\BSON\UTCDateTime($begin); $until=new MongoDB\BSON\UTCDateTime($end);
$fromMs=(int)(string)$from; $untilMs=(int)(string)$until;
bv117Insert($manager,$namespace,[
['_id'=>1,'at'=>new MongoDB\BSON\UTCDateTime($fromMs-1)],
['_id'=>2,'at'=>$from],
['_id'=>3,'at'=>new MongoDB\BSON\UTCDateTime($untilMs-1)],
['_id'=>4,'at'=>$until],
]);
$options=['projection'=>['_id'=>1,'at'=>1],'sort'=>['at'=>1,'_id'=>1],'limit'=>10];
$selected=bv117Read($manager,$namespace,['at'=>['$gte'=>$from,'$lt'=>$until]],$options);
$ids=array_column($selected,'_id');
$all=bv117Read($manager,$namespace,[],$options);
$rows=[];
foreach($all as $event){
$rows[]=[$event['_id'],$event['at']->toDateTimeImmutable()->setTimezone($zone)->format('Y-m-d H:i:s.v P'),
bv117Utc($event['at']),in_array($event['_id'],$ids,true)?'да':'нет'];
}
$rows[]=['Длина окна',(string)(($untilMs-$fromMs)/3600000).' часа',(string)($untilMs-$fromMs).' мс','—'];
$nextEnd=new MongoDB\BSON\UTCDateTime($end->modify('+1 day'));
$next=bv117Read($manager,$namespace,['at'=>['$gte'=>$until,'$lt'=>$nextEnd]],$options);
$checks=['only_day_boundary_rows'=>$ids===[2,3],
'calendar_day_23_hours'=>$untilMs-$fromMs===82800000,
'start_end_offsets'=>$begin->getOffset()===3600 && $end->getOffset()===7200,
'adjacent_day_no_overlap'=>array_column($next,'_id')===[4],
'start_inclusive_end_exclusive'=>!in_array(1,$ids,true) && !in_array(4,$ids,true)];
$details=['from_ms'=>(string)$from,'until_ms'=>(string)$until,'selected_ids'=>$ids];
} finally {
if($owned){bv117Command($manager,$database,['drop'=>$collection]);}
}
$checks['owned_collection_removed']=!bv117Exists($manager,$database,$collection);
bv117Report('mongodb117-bson-day-range', ['Запись', 'Местное время Europe/Berlin', 'UTC', 'Входит в день?'], $rows, $checks, $runtime, $details);
echo 'mongodb117-bson-day-range: saved result.html and result.json
';
Ожидаемый результат
mongodb117-bson-day-range: saved result.html and result.json
Запустите php example.php и откройте result.html. В архив включены bv117-helper.php и README с установкой/запуском. Данные создаются заново в отдельной собственной коллекции; соседние программы запускать не требуется.
Открыть фактический результат HTML · Скачать результат JSON
Упражнение с ориентиром результата. Выберите 25 октября 2026 года в Europe/Berlin и расставьте события относительно новых границ: расстояние между полуночами будет 25 часов. Согласуйте ожидаемую таблицу с датой, сохранив правило >= и <.
Посчитать счёт из строковых Decimal128 без PHP float
Счёт содержит три тетради по 19.90 и три пакета по 0.10. Нужно получить 59.70, 0.30 и итог 60.00 без промежуточного преобразования цен в двоичный PHP float.
Входные данные
Цены — строки с точкой, количества — малые целые, валюта RUB. Политика цены: неотрицательное значение до 9999999.99, ровно два дробных разряда.
- Строка "19.90" из формы
- Проверка прикладного формата
- new Decimal128("19.90")
- Сервер: цена × 3 = 59.70
- Сервер: сумма строк = 60.00
- __toString → текст в HTML/API
Короткое действие
$price = new MongoDB\BSON\Decimal128('19.90'); // Именно строка.
$document = ['unit_price' => $price, 'quantity' => 3];
$stage = ['$project' => [
'amount' => ['$multiply' => ['$unit_price', '$quantity']],
]];
// После записи и серверного расчёта: (string) $result['amount'] === '59.70'.Это выделенный механизм. Значения вроде $input, $document и $result принадлежат полному сценарию ниже; если нужна запускаемая программа целиком, используйте её архив.
Что получится
| Строка | Товар/условие | Цена/статус | Количество | Итог |
|---|---|---|---|---|
| 1 | Тетрадь | 19.90 | 3 | 59.70 |
| 2 | Пакет | 0.10 | 3 | 0.30 |
| Итого | RUB | 6 | 60.00 | |
| Отказ | 19,90 / NaN / Infinity / -1.00 | вход отклонён | NULL | 4 отказа |
| Синтаксис Decimal | not-decimal | InvalidArgumentException | NULL | не создан |
Разбор по шагам
Decimal128 представляет десятичное число: это не PHP float и не простой текст. bv117Price проверяет правило цены, затем создаёт BSON Decimal128. При записи MongoDB получает decimal, что программа показывает через $type.
В pipeline $project вычисляет сумму строки через $multiply, а $group с $sum получает итог. Пути $unit_price и $quantity обозначают поля документа. Количество 3 остаётся целым; десятичные операнды и результат не проходят через PHP float.
Прочитанный Decimal128 превращается в строку для вывода. 60.00 относится к этим значениям и операциям, а не к универсальному форматированию валют. Не делайте number_format((float) $amount), если хотите сохранить десятичную точность.
19,90, NaN, Infinity и -1.00 отвергает правило цены. Сам Decimal128 допускает NaN и Infinity. Попытка создать not-decimal отдельно показывает InvalidArgumentException конструктора — это другой уровень проверки.
Границы и типичные ошибки
Decimal128 имеет конечную точность до 34 значащих цифр. Округление, валюта, налоги и деление требуют прикладных правил. Не складывайте разные валюты. Целые копейки остаются разумной моделью при фиксированной шкале; выбор следует из диапазона и требований.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/bv117-helper.php';
[$manager,$database]=bv117Environment();
$server=bv117Command($manager,$database,['buildInfo'=>1])[0]['version'];
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$server];
$collection='mongodb117_bson_'.bin2hex(random_bytes(8));
$namespace=$database.'.'.$collection; $owned=false; $details=[];
try {
if(bv117Exists($manager,$database,$collection)){throw new RuntimeException('Fixture name collision');}
bv117Command($manager,$database,['create'=>$collection]);
$owned=true;
bv117Insert($manager,$namespace,[
['_id'=>1,'title'=>'Тетрадь','unit_price'=>bv117Price('19.90'),'quantity'=>3,'currency'=>'RUB'],
['_id'=>2,'title'=>'Пакет','unit_price'=>bv117Price('0.10'),'quantity'=>3,'currency'=>'RUB'],
]);
$lines=bv117Aggregate($manager,$database,$collection,[
['$match'=>['currency'=>'RUB']],['$sort'=>['_id'=>1]],
['$project'=>['_id'=>1,'title'=>1,'unit_price'=>1,'quantity'=>1,
'amount'=>['$multiply'=>['$unit_price','$quantity']],
'stored_type'=>['$type'=>'$unit_price']]],
]);
$totals=bv117Aggregate($manager,$database,$collection,[
['$match'=>['currency'=>'RUB']],
['$group'=>['_id'=>'$currency','quantity'=>['$sum'=>'$quantity'],
'amount'=>['$sum'=>['$multiply'=>['$unit_price','$quantity']]]]],
]);
$rejected=[];
foreach(['19,90','NaN','Infinity','-1.00'] as $input){
try{bv117Price($input);}
catch(InvalidArgumentException $failure){$rejected[]=$input;}
}
$invalidDecimal=false;
try{new MongoDB\BSON\Decimal128('not-decimal');}
catch(MongoDB\Driver\Exception\InvalidArgumentException $failure){$invalidDecimal=true;}
$rows=[];
foreach($lines as $line){$rows[]=[$line['_id'],$line['title'],(string)$line['unit_price'],$line['quantity'],(string)$line['amount']];}
$rows[]=['Итого','RUB','',$totals[0]['quantity'],(string)$totals[0]['amount']];
$rows[]=['Отказ',implode(' / ',$rejected),'вход отклонён',null,(string)count($rejected).' отказа'];
$rows[]=['Синтаксис Decimal','not-decimal',$invalidDecimal?'InvalidArgumentException':'ошибка',null,'не создан'];
$checks=['decimal_storage'=>array_column($lines,'stored_type')===['decimal','decimal'],
'decimal_server_result'=>$lines[0]['amount'] instanceof MongoDB\BSON\Decimal128 && $totals[0]['amount'] instanceof MongoDB\BSON\Decimal128,
'line_amounts'=>array_map(fn($line)=>(string)$line['amount'],$lines)===['59.70','0.30'],
'total_exact'=>(string)$totals[0]['amount']==='60.00',
'quantity_six'=>$totals[0]['quantity']===6,
'domain_rejections'=>$rejected===['19,90','NaN','Infinity','-1.00'],
'constructor_invalid_decimal_caught'=>$invalidDecimal];
} finally {
if($owned){bv117Command($manager,$database,['drop'=>$collection]);}
}
$checks['owned_collection_removed']=!bv117Exists($manager,$database,$collection);
bv117Report('mongodb117-bson-decimal-invoice', ['Строка', 'Товар/условие', 'Цена/статус', 'Количество', 'Итог'], $rows, $checks, $runtime, $details);
echo 'mongodb117-bson-decimal-invoice: saved result.html and result.json
';
Ожидаемый результат
mongodb117-bson-decimal-invoice: saved result.html and result.json
Запустите php example.php и откройте result.html. В архив включены bv117-helper.php и README с установкой/запуском. Данные создаются заново в отдельной собственной коллекции; соседние программы запускать не требуется.
Открыть фактический результат HTML · Скачать результат JSON
Упражнение с ориентиром результата. Поставьте количество пакетов 7: строка должна дать 0.70, итог — 60.40. Обновите ожидаемые результаты; цену всё так же передавайте строкой.
Практическая проблема: MongoDB storing data in decimal format. Разработчик ожидает decimal, но сохраняет числовое значение другим BSON-типом. Урок получает цену из строки, проверяет $type и выполняет арифметику на сервере. Вопрос задаёт ситуацию; правила проверены по первичным источникам, код и данные урока оригинальные.
Вернуть типы входному JSON и явно подготовить ответ API
Интеграция присылает JSON со строками ID, времени и суммы. После сохранения без преобразования внешний вид может казаться правильным, но запросы и расчёты получат другие типы.
Входные данные
id="65a000000000000000000001", occurred_at="2026-10-29T12:30:00.123Z", amount="0.10". Документ 501 хранит ObjectId/Date/Decimal128, сравнительный 502 — исходные строки.
- JSON → PHP-массив строк
- Проверка обязательных строковых полей
- ObjectId / UTCDateTime / Decimal128
- BSON хранит три разных типа
- Курсор возвращает типизированные значения
- API явно экспортирует ID, ISO UTC и decimal-строку
Короткое действие
$input = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
$id = new MongoDB\BSON\ObjectId($input['id']);
$price = new MongoDB\BSON\Decimal128($input['amount']);
// После проверки формата даты:
$date = new MongoDB\BSON\UTCDateTime($parsedDate);
// Для обычного JSON API договор задаём явно:
$api = ['id' => (string) $id, 'amount' => (string) $price];Это выделенный механизм. Значения вроде $input, $document и $result принадлежат полному сценарию ниже; если нужна запускаемая программа целиком, используйте её архив.
Что получится
| Документ/выход | Поле | Фактический тип | Представление |
|---|---|---|---|
| 501 | entity_id | objectId | 65a000000000000000000001 |
| 501 | occurred_at | date | 2026-10-29T12:30:00.123Z |
| 501 | amount | decimal | 0.10 |
| 502 | entity_id | string | 65a000000000000000000001 |
| 502 | occurred_at | string | 2026-10-29T12:30:00.123Z |
| 502 | amount | string | 0.10 |
| JSON API | amount | string | 0.10 |
| Диагностика | epoch_ms | строка миллисекунд | 1793277000123 |
Разбор по шагам
json_decode с true и JSON_THROW_ON_ERROR читает структуру JSON; он не угадывает ObjectId, дату или цену внутри строки. Входная JSON-схема и схема базы — разные контракты.
Z в согласованной дате означает UTC. В createFromFormat T и Z экранированы как буквальные символы, зона UTC задана явно; v сохраняет миллисекунды. Это не универсальный разбор всех ISO-строк с любыми смещениями.
Фильтр с типизированным entity_id находит 501, со строкой — 502. $type показывает objectId/date/decimal у первой записи и string/string/string у второй. Type map array меняет структуру документов, но не превращает специальные BSON-значения в строки.
API собирается явно: (string) для ID и суммы, toDateTimeImmutable()->format(...) для времени. Это прикладной JSON-договор, не автоматический BSON-дамп. Обратный decode подтверждает совпадение исходных полей. Сумма "0.10" остаётся строкой.
Для числа миллисекунд есть публичный (string) $occurredAt. Не читайте внутренние поля из var_dump и не используйте Reflection вместо документированного API расширения.
Границы и типичные ошибки
Extended JSON — отдельное представление BSON с маркерами типов. Здесь выбран простой прикладной JSON-договор. Внешний массив нельзя целиком отдавать в фильтр/обновление: нужны разрешённые поля, ограничения и права. HTTP-обработчик дополнительно ограничивает размер тела; повторная доставка сообщения требует идемпотентности.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/bv117-helper.php';
[$manager,$database]=bv117Environment();
$server=bv117Command($manager,$database,['buildInfo'=>1])[0]['version'];
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$server];
$collection='mongodb117_bson_'.bin2hex(random_bytes(8));
$namespace=$database.'.'.$collection; $owned=false; $details=[];
try {
if(bv117Exists($manager,$database,$collection)){throw new RuntimeException('Fixture name collision');}
bv117Command($manager,$database,['create'=>$collection]);
$owned=true;
$json='{"id":"65a000000000000000000001","occurred_at":"2026-10-29T12:30:00.123Z","amount":"0.10"}';
$input=json_decode($json,true,512,JSON_THROW_ON_ERROR);
foreach(['id','occurred_at','amount'] as $field){if(!isset($input[$field]) || !is_string($input[$field])){throw new InvalidArgumentException('String integration field required');}}
$id=new MongoDB\BSON\ObjectId($input['id']);
$when=new MongoDB\BSON\UTCDateTime(bv117Date($input['occurred_at'],'UTC','Y-m-d\TH:i:s.v\Z'));
$amount=bv117Price($input['amount']);
bv117Insert($manager,$namespace,[
['_id'=>501,'entity_id'=>$id,'occurred_at'=>$when,'amount'=>$amount],
['_id'=>502,'entity_id'=>$input['id'],'occurred_at'=>$input['occurred_at'],'amount'=>$input['amount']],
]);
$types=bv117Aggregate($manager,$database,$collection,[
['$sort'=>['_id'=>1]],['$project'=>['_id'=>1,'id_type'=>['$type'=>'$entity_id'],
'date_type'=>['$type'=>'$occurred_at'],'amount_type'=>['$type'=>'$amount']]],
]);
$options=['projection'=>['_id'=>1,'entity_id'=>1,'occurred_at'=>1,'amount'=>1],'limit'=>2];
$typed=bv117Read($manager,$namespace,['entity_id'=>$id],$options);
$plain=bv117Read($manager,$namespace,['entity_id'=>$input['id']],$options);
$document=$typed[0];
$api=['id'=>(string)$document['entity_id'],'occurred_at'=>bv117Utc($document['occurred_at']),'amount'=>(string)$document['amount']];
$export=json_encode($api,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR);
$again=json_decode($export,true,512,JSON_THROW_ON_ERROR);
$rows=[];
foreach($types as $type){
$rows[]=[$type['_id'],'entity_id',$type['id_type'],$input['id']];
$rows[]=[$type['_id'],'occurred_at',$type['date_type'],$input['occurred_at']];
$rows[]=[$type['_id'],'amount',$type['amount_type'],$input['amount']];
}
$rows[]=['JSON API','amount',get_debug_type($again['amount']),$again['amount']];
$rows[]=['Диагностика','epoch_ms','строка миллисекунд',(string)$document['occurred_at']];
$details=['explicit_api_json'=>$export,'typed_query_ids'=>array_column($typed,'_id'),'string_query_ids'=>array_column($plain,'_id')];
$checks=['typed_fields_preserved'=>$types[0]['id_type']==='objectId' && $types[0]['date_type']==='date' && $types[0]['amount_type']==='decimal',
'raw_strings_stay_strings'=>$types[1]['id_type']==='string' && $types[1]['date_type']==='string' && $types[1]['amount_type']==='string',
'type_map_keeps_bson_values'=>$document['entity_id'] instanceof MongoDB\BSON\ObjectId && $document['occurred_at'] instanceof MongoDB\BSON\UTCDateTime && $document['amount'] instanceof MongoDB\BSON\Decimal128,
'typed_and_plain_filters_differ'=>array_column($typed,'_id')===[501] && array_column($plain,'_id')===[502],
'api_roundtrip_explicit'=>$again===$input,
'date_ms_string'=>(string)$document['occurred_at']==='1793277000123'];
} finally {
if($owned){bv117Command($manager,$database,['drop'=>$collection]);}
}
$checks['owned_collection_removed']=!bv117Exists($manager,$database,$collection);
bv117Report('mongodb117-bson-integration-roundtrip', ['Документ/выход', 'Поле', 'Фактический тип', 'Представление'], $rows, $checks, $runtime, $details);
echo 'mongodb117-bson-integration-roundtrip: saved result.html and result.json
';
Ожидаемый результат
mongodb117-bson-integration-roundtrip: saved result.html and result.json
Запустите php example.php и откройте result.html. В архив включены bv117-helper.php и README с установкой/запуском. Данные создаются заново в отдельной собственной коллекции; соседние программы запускать не требуется.
Открыть фактический результат HTML · Скачать результат JSON
Упражнение с ориентиром результата. Оставьте amount строкой "0.10", но передайте id JSON-массивом: вход должен быть отклонён до конструирования BSON. Почему строка "0.10" и JSON-число 0.10 означают разные договоры?
Практическая проблема: MongoDB BSON UTCDateTime has the milliseconds property, also it doesn't have it. Видимое отладчиком milliseconds ошибочно принимают за обычное доступное свойство. Показан документированный __toString, без чтения внутренностей через Reflection. Вопрос задаёт ситуацию; правила проверены по первичным источникам, код и данные урока оригинальные.
Не путать время ObjectId с датой события из архива
После импорта старых заказов сортировка по _id показывает не хронологию заказов. Храните дату события отдельно, а временную часть ObjectId используйте с пониманием её происхождения.
Входные данные
Три явно заданных ObjectId с компонентами января 2024 года. Даты заказов — январь 2020 и октябрь 2026. Первые два ID имеют одинаковую временную секунду, но разные остальные байты.
- Источник сообщает created_at
- ObjectId служит идентификатором
- getTimestamp читает часть ID
- Сравнение обнаруживает иной момент
- Сервер сортирует created_at, _id
- Архивный заказ показан первым
Короткое действие
$idSeconds = $document['_id']->getTimestamp();
$eventDate = $document['created_at']->toDateTimeImmutable();
// Для хронологии заказа используем отдельную дату события:
$options = ['sort' => ['created_at' => 1, '_id' => 1], 'limit' => 3];
// getTimestamp диагностирует часть ID, а не доказывает время заказа.Это выделенный механизм. Значения вроде $input, $document и $result принадлежат полному сценарию ниже; если нужна запускаемая программа целиком, используйте её архив.
Что получится
| Сортировка | Позиция | Событие | created_at, UTC | Компонент ObjectId, UTC |
|---|---|---|---|---|
| По _id | 1 | Текущий заказ | 2026-10-02 08:00:00 | 2024-01-11 14:49:36 |
| По _id | 2 | Вчерашний заказ | 2026-10-01 08:00:00 | 2024-01-11 14:49:36 |
| По _id | 3 | Архивный заказ | 2020-01-02 08:00:00 | 2024-01-11 14:49:37 |
| По created_at | 1 | Архивный заказ | 2020-01-02 08:00:00 | 2024-01-11 14:49:37 |
| По created_at | 2 | Вчерашний заказ | 2026-10-01 08:00:00 | 2024-01-11 14:49:36 |
| По created_at | 3 | Текущий заказ | 2026-10-02 08:00:00 | 2024-01-11 14:49:36 |
Разбор по шагам
ObjectId занимает 12 байт: четыре для секунд эпохи Unix, при обычной генерации далее пять случайных байт процесса и трёхбайтовый счётчик. Это механизм идентификаторов, а не подтверждение времени бизнес-события.
Конструктор принимает готовый ID, как здесь. Импорт может сохранить старый ID или создать новый. Поэтому getTimestamp не отвечает, когда клиент оформил заказ: отдельно записывайте created_at по правилам источника.
getTimestamp возвращает секунды; DateTimeImmutable("@".$seconds) показывает компонент в UTC. Не передавайте эти секунды прямо в UTCDateTime, который ожидает миллисекунды. Колонка намеренно называется «Компонент ObjectId», а не «Дата заказа».
Даже автоматически созданные ObjectId не гарантируют строгую хронологию разных клиентов: точность временной части — секунда, часы могут расходиться. Сортировка created_at, _id задаёт порядок событий и устойчивый выбор при одинаковом created_at.
Границы и типичные ошибки
Если бизнес-даты нет, её нельзя достоверно восстановить по ID. Можно показать явно помеченную оценку, но не подменять неизвестное время. Здесь нет миграции чужих данных: все даты и ID создаются внутри собственной учебной коллекции.
Полная самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/bv117-helper.php';
[$manager,$database]=bv117Environment();
$server=bv117Command($manager,$database,['buildInfo'=>1])[0]['version'];
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$server];
$collection='mongodb117_bson_'.bin2hex(random_bytes(8));
$namespace=$database.'.'.$collection; $owned=false; $details=[];
try {
if(bv117Exists($manager,$database,$collection)){throw new RuntimeException('Fixture name collision');}
bv117Command($manager,$database,['create'=>$collection]);
$owned=true;
$hexes=['65a000000000000000000001','65a000000000000000000002','65a000010000000000000003'];
$fixture=[
['_id'=>new MongoDB\BSON\ObjectId($hexes[0]),'title'=>'Текущий заказ','created_at'=>new MongoDB\BSON\UTCDateTime(bv117Date('2026-10-02 08:00:00.000','UTC'))],
['_id'=>new MongoDB\BSON\ObjectId($hexes[1]),'title'=>'Вчерашний заказ','created_at'=>new MongoDB\BSON\UTCDateTime(bv117Date('2026-10-01 08:00:00.000','UTC'))],
['_id'=>new MongoDB\BSON\ObjectId($hexes[2]),'title'=>'Архивный заказ','created_at'=>new MongoDB\BSON\UTCDateTime(bv117Date('2020-01-02 08:00:00.000','UTC'))],
];
bv117Insert($manager,$namespace,$fixture);
$projection=['_id'=>1,'title'=>1,'created_at'=>1];
$byId=bv117Read($manager,$namespace,[],['projection'=>$projection,'sort'=>['_id'=>1],'limit'=>3]);
$byEvent=bv117Read($manager,$namespace,[],['projection'=>$projection,'sort'=>['created_at'=>1,'_id'=>1],'limit'=>3]);
$rows=[];
foreach(['По _id'=>$byId,'По created_at'=>$byEvent] as $order=>$documents){
foreach($documents as $index=>$document){
$component=(new DateTimeImmutable('@'.$document['_id']->getTimestamp()))->setTimezone(new DateTimeZone('UTC'));
$rows[]=[$order,$index+1,$document['title'],
$document['created_at']->toDateTimeImmutable()->format('Y-m-d H:i:s'),
$component->format('Y-m-d H:i:s')];
}
}
$checks=['id_order_is_not_event_order'=>array_column($byId,'title')===['Текущий заказ','Вчерашний заказ','Архивный заказ'],
'event_order'=>array_column($byEvent,'title')===['Архивный заказ','Вчерашний заказ','Текущий заказ'],
'same_second_different_ids'=>$byId[0]['_id']->getTimestamp()===$byId[1]['_id']->getTimestamp() && (string)$byId[0]['_id']!==(string)$byId[1]['_id'],
'timestamp_seconds_known'=>$byId[2]['_id']->getTimestamp()===1704984577,
'business_date_preserved'=>bv117Utc($byEvent[0]['created_at'])==='2020-01-02T08:00:00.000Z'];
$details=['ids_by_event'=>array_map(fn($d)=>(string)$d['_id'],$byEvent)];
} finally {
if($owned){bv117Command($manager,$database,['drop'=>$collection]);}
}
$checks['owned_collection_removed']=!bv117Exists($manager,$database,$collection);
bv117Report('mongodb117-bson-imported-event-time', ['Сортировка', 'Позиция', 'Событие', 'created_at, UTC', 'Компонент ObjectId, UTC'], $rows, $checks, $runtime, $details);
echo 'mongodb117-bson-imported-event-time: saved result.html and result.json
';
Ожидаемый результат
mongodb117-bson-imported-event-time: saved result.html and result.json
Запустите php example.php и откройте result.html. В архив включены bv117-helper.php и README с установкой/запуском. Данные создаются заново в отдельной собственной коллекции; соседние программы запускать не требуется.
Открыть фактический результат HTML · Скачать результат JSON
Упражнение с ориентиром результата. Дайте двум заказам одинаковый created_at и сохраните _id вторым ключом сортировки. Объясните, почему временная часть ID не должна заменять исходную дату импорта.
Восемь методов ObjectId, UTCDateTime и Decimal128
Карточки сохраняют параметры, возврат, ошибки и версионные условия соответствующих страниц PHP.net. У каждого метода две прикладные ситуации, связанные с самостоятельными программами и их результатами.
| Метод | Для чего нужен |
|---|---|
| MongoDB\BSON\ObjectId::__construct | Создать BSON ObjectId из входной строки или сгенерировать новый идентификатор. |
| MongoDB\BSON\ObjectId::__toString | Вернуть шестнадцатеричный текст ObjectId для адреса или согласованного API. |
| MongoDB\BSON\ObjectId::getTimestamp | Прочитать временную часть ID в секундах и отделить её от даты события. |
| MongoDB\BSON\UTCDateTime::__construct | Создать BSON Date из миллисекунд или разобранного PHP-момента времени. |
| MongoDB\BSON\UTCDateTime::__toString | Получить строку миллисекунд для диагностики и согласованного машинного формата. |
| MongoDB\BSON\UTCDateTime::toDateTimeImmutable | Превратить прочитанный BSON Date в неизменяемый PHP-объект даты в UTC. |
| MongoDB\BSON\Decimal128::__construct | Создать BSON Decimal128 из исходной десятичной строки. |
| MongoDB\BSON\Decimal128::__toString | Вернуть текст Decimal128 без промежуточного PHP float. |
Как применить типы в своём сервисе
Сначала договоритесь о смысле поля: идентификатор какой сущности, момент в какой зоне, сумма в какой валюте. Затем проверяйте вход и создавайте нужный BSON-тип; после чтения явно формируйте данные интерфейса. Видимый текст — только одна из форм одного значения.
Продолжение: атомарные изменения и результат записи, расчёты на сервере, индекс под фильтр и сортировку, даты PHP и массивы и JSON. Типы BSON не заменяют проверку прав, конкуренции или схемы входа.
Эта глава закрывает перечисленные восемь методов. Int64, Binary/UUID, Regex, JavaScript, Timestamp, MinKey/MaxKey, сериализация/Extended JSON, остальные методы BSON и полный набор операторов MongoDB требуют своих разделов. Их нельзя считать раскрытыми только потому, что эта глава содержит работающий обмен данными.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
MongoDB\BSON\ObjectId::__constructСоздать BSON ObjectId из входной строки или сгенерировать новый идентификатор.
С mongodb 1.0.0. Здесь используется современное расширение mongodb, не устаревший класс MongoId. · mongodb
final public MongoDB\BSON\ObjectId::__construct(?string $id = null)Создать BSON ObjectId из входной строки или сгенерировать новый идентификатор.
final public MongoDB\BSON\ObjectId::__construct(?string $id = null)Параметры
| Аргумент | Смысл |
|---|---|
| id | Строка из 24 шестнадцатеричных символов. Без аргумента или с null расширение генерирует ObjectId. |
Результат
Конструктор инициализирует объект; результат выражения new — MongoDB\BSON\ObjectId. Сам по себе он не ищет и не записывает документ.
Ошибки и границы
MongoDB\Driver\Exception\InvalidArgumentException заявлен при разборе аргументов и неверной 24-символьной hex-строке. Вход сначала должен быть строкой. Не превращайте отсутствующий ID формы в null: это запустило бы генерацию нового ID вместо обработки ошибки.
Версии
С mongodb 1.0.0. Здесь используется современное расширение mongodb, не устаревший класс MongoId.
Применение 1: Открыть товар по ссылке
$id = new MongoDB\BSON\ObjectId($input);
$filter = ['_id' => $id];Типизированное условие находит Кружку; условие со строкой даёт 0 документов. Неверный текст перехвачен отдельно.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Принять ID внешнего события
$entityId = new MongoDB\BSON\ObjectId($input['id']);
$document = ['entity_id' => $entityId];У документа 501 сервер показывает entity_id=objectId, у сравнительного 502 — string. Фильтры находят разные документы.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
MongoDB\BSON\ObjectId::__toStringВернуть шестнадцатеричный текст ObjectId для адреса или согласованного API.
С mongodb 1.0.0. · mongodb
final public MongoDB\BSON\ObjectId::__toString(): stringВернуть шестнадцатеричный текст ObjectId для адреса или согласованного API.
final public MongoDB\BSON\ObjectId::__toString(): stringПараметры
Параметров нет. Метод вызывается у уже созданного значения соответствующего BSON-типа.
Результат
Строковое шестнадцатеричное представление ID. Приведение (string) вызывает этот метод; сохранённый BSON при этом не меняется.
Ошибки и границы
На официальной странице отдельный список исключений не указан. Метод не выполняет запрос и не подтверждает права. Строка удобна на границе интерфейса; в фильтре поля ObjectId её снова представляют соответствующим типом.
Версии
С mongodb 1.0.0.
Применение 1: Подготовить найденный ID для ссылки
$linkId = (string) $stored;В строке «ID для ссылки» — 65a000000000000000000001. Отладочное представление var_dump для этого не нужно.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Сформировать поле ответа интеграции
$api = ['id' => (string) $document['entity_id']];Явно собранный JSON снова содержит исходный текст id. Схема ответа не зависит от отладочного отображения объекта.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
MongoDB\BSON\ObjectId::getTimestampПрочитать временную часть ID в секундах и отделить её от даты события.
С mongodb 1.2.0. · mongodb
final public MongoDB\BSON\ObjectId::getTimestamp(): intПрочитать временную часть ID в секундах и отделить её от даты события.
final public MongoDB\BSON\ObjectId::getTimestamp(): intПараметры
Параметров нет. Метод вызывается у уже созданного значения соответствующего BSON-типа.
Результат
Целое число секунд эпохи Unix из старших 32 бит ObjectId; эти байты читаются как беззнаковое 32-битное число в порядке big-endian.
Ошибки и границы
Документация перечисляет MongoDB\Driver\Exception\InvalidArgumentException при разборе аргументов. На 32-битной PHP некоторые значения выглядят отрицательными из-за знакового int; sprintf("%u", $value) даёт беззнаковое десятичное представление. Программы требуют PHP x64. Метод читает ID, а не дату заказа.
Версии
С mongodb 1.2.0.
Применение 1: Диагностировать выбранный ID
$seconds = $stored->getTimestamp();Фиксированный ID содержит 1704984576 секунд. Это отдельная диагностическая строка рядом с результатом поиска.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Разобрать хронологию после импорта
$seconds = $document['_id']->getTimestamp();
$idTime = new DateTimeImmutable('@' . $seconds);Компонент указывает январь 2024, а created_at архивного заказа — январь 2020. Нужная выборка сортируется по created_at, _id.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
MongoDB\BSON\UTCDateTime::__constructСоздать BSON Date из миллисекунд или разобранного PHP-момента времени.
С mongodb 1.0.0. С 1.2.0 аргумент необязателен, default null, добавлен DateTimeInterface; ранее принимались int/float/string. С 1.20.0 добавлен Int64, string/float устарели. С 2.0.0 string/float больше не принимаются. · mongodb
final public MongoDB\BSON\UTCDateTime::__construct(int|MongoDB\BSON\Int64|DateTimeInterface|null $milliseconds = null)Создать BSON Date из миллисекунд или разобранного PHP-момента времени.
final public MongoDB\BSON\UTCDateTime::__construct(int|MongoDB\BSON\Int64|DateTimeInterface|null $milliseconds = null)Параметры
| Аргумент | Смысл |
|---|---|
| milliseconds: int | Миллисекунды от 1970-01-01T00:00:00Z. Отрицательные значения — даты до эпохи; нужен 64-битный диапазон. |
| milliseconds: Int64 | MongoDB\BSON\Int64, в том числе для совместимости с 32-битной PHP. |
| milliseconds: DateTimeInterface | Миллисекунды вычисляются из абсолютного момента DateTime/DateTimeImmutable. |
| milliseconds: null / без аргумента | Текущее время. Для повторяемых примеров момент задан явно. |
Результат
Через new получается UTCDateTime. В BSON записывается Date, имя зоны отдельно не сохраняется.
Ошибки и границы
Документация перечисляет MongoDB\Driver\Exception\InvalidArgumentException при разборе аргументов. Секунды нельзя выдавать за миллисекунды. Современный конструктор не разбирает календарную строку: сначала проверьте её и создайте DateTimeInterface.
Версии
С mongodb 1.0.0. С 1.2.0 аргумент необязателен, default null, добавлен DateTimeInterface; ранее принимались int/float/string. С 1.20.0 добавлен Int64, string/float устарели. С 2.0.0 string/float больше не принимаются.
Применение 1: Сохранить встречу
$moment = new MongoDB\BSON\UTCDateTime($local);
// $local уже проверен как DateTimeImmutable с нужной зоной.15:30:00.123 в Москве сохраняются как UTC 12:30:00.123. Сравнительный ошибочный ввод секунд приводит к 1970 году.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Построить границы отчёта
$from = new MongoDB\BSON\UTCDateTime($begin);
$until = new MongoDB\BSON\UTCDateTime($begin->modify('+1 day'));
$filter = ['at' => ['$gte' => $from, '$lt' => $until]];Два BSON Date задают календарный день Берлина длиной 23 часа; выбраны события 2 и 3.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
MongoDB\BSON\UTCDateTime::__toStringПолучить строку миллисекунд для диагностики и согласованного машинного формата.
С mongodb 1.0.0. Возвращаемая строка не означает, что конструктор любой версии принимает её напрямую. · mongodb
final public MongoDB\BSON\UTCDateTime::__toString(): stringПолучить строку миллисекунд для диагностики и согласованного машинного формата.
final public MongoDB\BSON\UTCDateTime::__toString(): stringПараметры
Параметров нет. Метод вызывается у уже созданного значения соответствующего BSON-типа.
Результат
Строковое количество миллисекунд с начала эпохи Unix. Это не ISO-дата и не секунды.
Ошибки и границы
Отдельный список исключений на официальной странице не указан. Не обращайтесь к внутреннему milliseconds, увиденному в отладчике. Календарный вывод даёт toDateTimeImmutable. Для восстановления из строки в mongodb 2.x нужен проверенный int/Int64, не прежний строковый аргумент конструктора.
Версии
С mongodb 1.0.0. Возвращаемая строка не означает, что конструктор любой версии принимает её напрямую.
Применение 1: Показать ошибку единиц
$milliseconds = (string) $document['starts_at'];1793277000123 миллисекунд сравниваются с 1793277000 секундами; таблица показывает причину ошибочного 1970 года.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Прочитать машинную диагностику через публичный API
$epochMs = (string) $document['occurred_at'];Таблица содержит 1793277000123. Ответ интеграции отдельно использует согласованный ISO UTC, а не неявный дамп объекта.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
MongoDB\BSON\UTCDateTime::toDateTimeImmutableПревратить прочитанный BSON Date в неизменяемый PHP-объект даты в UTC.
С mongodb 1.20.0. Архивы требуют mongodb 2.5+. · mongodb
final public MongoDB\BSON\UTCDateTime::toDateTimeImmutable(): DateTimeImmutableПревратить прочитанный BSON Date в неизменяемый PHP-объект даты в UTC.
final public MongoDB\BSON\UTCDateTime::toDateTimeImmutable(): DateTimeImmutableПараметры
Параметров нет. Метод вызывается у уже созданного значения соответствующего BSON-типа.
Результат
DateTimeImmutable в часовом поясе UTC с тем же моментом. Для иной зоны setTimezone возвращает новый объект.
Ошибки и границы
Документация перечисляет MongoDB\Driver\Exception\InvalidArgumentException при разборе аргументов. Метод не восстанавливает исходную зону: BSON Date её не хранит. Не путайте неизменяемый результат с toDateTime, возвращающим DateTime.
Версии
С mongodb 1.20.0. Архивы требуют mongodb 2.5+.
Применение 1: Показать время встречи
$utc = $document['starts_at']->toDateTimeImmutable();
$shown = $utc->setTimezone(new DateTimeZone($document['timezone_id']));Момент отображён как 12:30 UTC и 15:30 в Москве. Исходный объект сохраняет нулевое смещение.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Показать границы отчёта
$utc = $event['at']->toDateTimeImmutable();
$local = $utc->setTimezone(new DateTimeZone('Europe/Berlin'));Четыре события показаны в двух зонах; видно, почему полночь следующего дня исключена из окна.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
MongoDB\BSON\Decimal128::__constructСоздать BSON Decimal128 из исходной десятичной строки.
С mongodb 1.2.0; BSON Decimal128 совместим с MongoDB 3.4+, на более старом сервере использование типа приводит к ошибке. Здесь сервер 8.0. · mongodb
final public MongoDB\BSON\Decimal128::__construct(string $value)Создать BSON Decimal128 из исходной десятичной строки.
final public MongoDB\BSON\Decimal128::__construct(string $value)Параметры
| Аргумент | Смысл |
|---|---|
| value | Десятичная строка. Правило допустимой цены проверяется отдельно от синтаксиса типа. |
Результат
Через new получается Decimal128; драйвер записывает его как BSON decimal. Сам объект не выполняет арифметику PHP.
Ошибки и границы
MongoDB\Driver\Exception\InvalidArgumentException заявлен при разборе аргументов или неверной десятичной строке. Decimal128 допускает NaN/Infinity, но наша цена их отвергает. Преобразование float в строку не восстановит уже потерянную точность: берите исходный текст формы/API.
Версии
С mongodb 1.2.0; BSON Decimal128 совместим с MongoDB 3.4+, на более старом сервере использование типа приводит к ошибке. Здесь сервер 8.0.
Применение 1: Сохранить цену строки счёта
$price = new MongoDB\BSON\Decimal128('19.90');
// В полной программе сначала проверяется правило bv117Price.Цена хранится как decimal; сервер получает 59.70 за три тетради. Отдельно показаны отказы правила цены и синтаксиса конструктора.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Принять сумму сообщения
$amount = bv117Price($input['amount']);У типизированной записи сервер показывает decimal, у исходной строки — string. Выход API снова содержит точный текст "0.10".
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
MongoDB\BSON\Decimal128::__toStringВернуть текст Decimal128 без промежуточного PHP float.
С mongodb 1.2.0. BSON-тип поддерживается сервером MongoDB 3.4+. · mongodb
final public MongoDB\BSON\Decimal128::__toString(): stringВернуть текст Decimal128 без промежуточного PHP float.
final public MongoDB\BSON\Decimal128::__toString(): stringПараметры
Параметров нет. Метод вызывается у уже созданного значения соответствующего BSON-типа.
Результат
Строковое представление десятичного значения. Оно подходит для HTML или строкового API, но не является универсальным форматированием валюты.
Ошибки и границы
Официальная страница не выделяет исключений метода. Decimal128 может содержать специальные значения или экспоненциальную запись: результат не обязательно цена с двумя дробными знаками. Не складывайте такие строки оператором PHP +, если нужно сохранить десятичную модель вычислений.
Версии
С mongodb 1.2.0. BSON-тип поддерживается сервером MongoDB 3.4+.
Применение 1: Показать серверный итог
$totalText = (string) $totals[0]['amount'];Таблица показывает 59.70 и 0.30, итог 60.00. Цена и итог не приводятся к PHP float.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Применение 2: Вернуть сумму в JSON API
$api = ['amount' => (string) $document['amount']];После записи и чтения возвращается строка "0.10". Получатель не должен угадывать масштаб по двоичному JSON-числу.
Пояснение и полный самостоятельный пример · Таблица реального запуска
Фрагменты выделяют вызов внутри связанных программ: вход и объекты подготовлены выше по их сценарию. Полный код, набор данных и README находятся в архивах; эти фрагменты не обозначены отдельными самостоятельными программами.
Источники и обсуждения
- MongoDB\BSON\ObjectId::__construct
- MongoDB\BSON\ObjectId::__toString
- MongoDB\BSON\ObjectId::getTimestamp
- MongoDB\BSON\UTCDateTime::__construct
- MongoDB\BSON\UTCDateTime::__toString
- MongoDB\BSON\UTCDateTime::toDateTimeImmutable
- MongoDB\BSON\Decimal128::__construct
- MongoDB\BSON\Decimal128::__toString
- MongoDB: типы BSON
- PHP: преобразование PHP и BSON
- PHP: DateTimeImmutable::createFromFormat
- MongoDB: денежные данные
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


