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

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

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

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

Глава 170 · После Manager, Query, команд и базовых обновлений

MongoDB: операции выбранного Server и понятный Stable API

Запросы страниц, пакеты изменений, индексы, сохранённые отчёты и границы Stable API — от короткого вызова до видимого результата.

От выбора узла к полезному результату

Каталог должен показать только нужные товары, импорт — сохранить изменения, а отчёт — вернуть полезные цифры. Иногда важно выполнить эти действия на уже выбранном узле и точно понимать, какие правила API действуют. Здесь Manager выбирает Server, Server выполняет запрос или команду, а человек видит реальные товары, остатки, индекс и сохранённый отчёт.

Путь выбранной операции
  1. URI учебной базы → Manager → selectServer(PRIMARY) → конкретный Server
  2. Query/Command/BulkWrite → метод Server → MongoDB выполняет действие
  3. Cursor/WriteResult → ограниченное чтение → таблица товара, отчёта или результата изменения

Можно представить Manager диспетчером, который выбирает подходящую рабочую точку, а Server — обращением к конкретной точке. Аналогия объясняет разделение обязанностей, но не означает отдельный физический компьютер для каждого объекта или вечную неизменность роли узла.

$preference = new MongoDB\Driver\ReadPreference('primary');
$server = $manager->selectServer($preference);
$query = new MongoDB\Driver\Query(['sku' => 'MUG'], ['limit' => 1]);
$cursor = $server->executeQuery($namespace, $query);

Знак $ начинает имя переменной; = сохраняет возвращённый объект. new создаёт настройки ReadPreference или Query. Стрелка -> вызывает метод у объекта слева. selectServer возвращает Server, executeQuery — Cursor с результатом. $manager и $namespace готовит полная программа ниже; короткий фрагмент показывает только связь вызовов. В первом уроке найденный артикул MUG будет виден в таблице.

Начальные объяснения: переменные, массив с именованными ключами, аргументы и return, подключение и простые CRUD-операции. Подробная диагностика узлов уже есть в главе выбора Server; здесь продолжаем выполнением операций.

Выбрать метод по характеру операции

ЗадачаМетод ServerПолезный результат
Выбрать документыexecuteQueryCursor ограниченной страницы
Пакет одной коллекцииexecuteBulkWriteWriteResult с итогами
Пакет нескольких namespace MongoDB8executeBulkWriteCommandBulkWriteCommandResult
Команда только чтенияexecuteReadCommandCursor отчёта/описания
Команда чтения с записьюexecuteReadWriteCommandСохранённый отчёт, отдельно прочитанный Query
Команда структуры/записиexecuteWriteCommandОтвет createIndexes и проверяемое ограничение
Низкоуровневая диагностикаexecuteCommandОтвет команды с явно понятными правилами
Стабильный набор возможностейServerApi в ManagerРабочие API-команды и отказ вне договора

Подготовка и запуск девяти программ

Каждый из девяти архивов самостоятельный: example.php, sr129-helper.php и README. Нужны PHP CLI8.4+ x64, ext-mongodb2.5+ и отдельный MongoDB Community8.0+. Среда проверки пакета — PHP8.5.11 / mongodb2.5.5 / MongoDB8.0.34; Composer не нужен.

$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 запускаются из распакованного каталога. README объясняет установку и запуск отдельного mongod. Программа создаёт собственные случайные коллекции: одну, либо две для разных namespace/сохранения отчёта. finally удаляет только их точные имена, затем проверяется отсутствие. Пользовательские коллекции и настройки сервера не меняются.

result.html — понятная таблица; result.json — те же столбцы и значения; metrics.json — версии, проверки и SHA256 файлов. Host/port записываются из фактического Server и сравниваются с URI; они не выдуманы ради ожидаемой таблицы. Ссылки на странице открывают сохранённые результаты запуска, не подключая браузер читателя к MongoDB.

Получить Server от Manager и проверить, кто вернул документ

Отчёт вернул товар, но при диагностике нужно понимать, какой узел ответил. Научимся получить объект Server от Manager и связать реальную выборку с её сервером.

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

В собственной коллекции один товар MUG. URI указывает отдельный локальный mongod. Порт читается из URI и сверяется с наблюдаемым ответом, а не подменяется константой в отчёте.

Как идут данные
  1. Manager: selectServer получает ReadPreference::PRIMARY и возвращает Server
  2. Server: executeQuery отправляет ограниченный поиск _id=1 этому узлу
  3. Cursor: getServer сообщает узел выполненной операции; таблица показывает товар и совпадение адреса

Самый короткий полезный вызов

$server = $manager->selectServer(
    new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY)
);
$cursor = $server->executeQuery($namespace, $query);
// Manager выбирает; Server выполняет; Cursor содержит результат.

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

ПроверкаРезультат
Полученный товарMUG
Ответил выбранный Servertrue
Порт совпадает с URItrue
new Server недоступенtrue

Пошаговый разбор

Manager управляет топологией и выбирает подходящий узел. Server представляет конкретный известный этому Manager сервер. Это разные обязанности: Server не является вторым объектом конфигурации подключения.

Конструктор Server private. new Server() нельзя использовать вместо Manager. Рабочие способы получения — Manager::selectServer(), Manager::getServers() и Server уже выполненного Cursor. Список известных узлов сам по себе не означает, что любой из них годится для записи.

Вызов -> выполняет метод объекта слева. selectServer возвращает значение, которое сохраняется в $server. Затем executeQuery возвращает Cursor; его данные читаются, а getServer позволяет сравнить endpoint с выбранным объектом.

PRIMARY в современной ReadPreference — строковый режим. Исторические RP_PRIMARY и целочисленный режим из старых примеров не переносятся механически в mongodb2.x. Проверяйте совместимость расширения и Composer-библиотеки, если используете её.

Границы и ошибки

Здесь standalone: в нём нет secondary или выбора между репликами. Успешный запуск доказывает адрес и результат этого запроса, а не задержку репликации, failover или высокую доступность. Server не закрепляет за приложением роль primary навечно: топология может измениться.

Полная самостоятельная программа и архив
Получить Server от Manager и проверить, кто вернул документСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
sr129Insert($manager,$namespace,[['_id'=>1,'sku'=>'MUG','title'=>'Кружка']]);
$cursor=$server->executeQuery($namespace,new MongoDB\Driver\Query(['_id'=>1],['projection'=>['_id'=>1,'sku'=>1],'limit'=>1,'batchSize'=>2,'maxTimeMS'=>1500]));
$docs=sr129Rows($cursor);$actual=$cursor->getServer();$parts=parse_url($settings['uri']);
$private=false;try{new MongoDB\Driver\Server();}catch(Error){$private=true;}
$same=$actual->getHost()===$server->getHost()&&$actual->getPort()===$server->getPort();
$rows=[['Полученный товар',$docs[0]['sku']],['Ответил выбранный Server',$same],['Порт совпадает с URI',$actual->getPort()===$parts['port']],['new Server недоступен',$private]];
$checks=['exact_doc'=>$docs===[['_id'=>1,'sku'=>'MUG']],'same_server'=>$same,'port_checked_against_uri'=>$actual->getPort()===$parts['port'],'host_is_loopback'=>$actual->getHost()==='127.0.0.1','private_constructor'=>$private,'standalone_not_replica'=>$server->getType()===MongoDB\Driver\Server::TYPE_STANDALONE];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
sr129Report('mongodb129-server-selected-server', ['Проверка', 'Результат'], $rows, $checks, $runtime);
echo 'mongodb129-server-selected-server: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-selected-server: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Поменяйте SKU на BOOK — отчёт должен вернуть BOOK при том же наблюдаемом endpoint. В реальном сервисе сохраняйте адрес для диагностики, но не показывайте внутреннюю топологию всем посетителям.

Реальная задача разработчика: Как понять, какой сервер ответил на запрос. Разработчику нужен адрес реально ответившего узла, а не просто список подключений. Вопрос о старом драйвере служит мотивацией; современный пример использует Cursor::getServer и не заявляет измерение репликации на standalone. Обсуждение даёт контекст; современный контракт сверяется с официальными источниками, а код и объяснения здесь самостоятельные.

Реальная задача разработчика: Почему старый целочисленный режим ReadPreference стал deprecated. Обновление окружения проявило несовместимость старого режима в библиотеке. Пример использует современную строку/константу; удалять строки vendor наугад не предлагается. Обсуждение даёт контекст; современный контракт сверяется с официальными источниками, а код и объяснения здесь самостоятельные.

Получить только видимые товары и нужные поля страницы

Экран каталога магазина A должен показать две доступные позиции, затем следующую страницу. Скрытые данные, чужие товары и черновики не должны покидать базу в этой выборке.

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

Пять товаров: три активных магазина A по ценам100/200/300, один чужой и один черновик. Текст internal намеренно добавлен, чтобы проверить projection.

Как идут данные
  1. Условие: tenant=A и active=true выполняются в MongoDB
  2. Страница: sort price,_id + limit2 + projection возвращают Кружку и Тарелку
  3. Продолжение: после пары price=200,_id=3 следующая выборка возвращает Чайник

Самый короткий полезный вызов

$query = new MongoDB\Driver\Query(
    ['tenant' => 'A', 'active' => true],
    ['projection' => ['_id' => 1, 'title' => 1, 'price' => 1],
     'sort' => ['price' => 1, '_id' => 1], 'limit' => 2]
);
$cursor = $server->executeQuery($namespace, $query);

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

СтраницаIDНазваниеЦена, условные единицы
12Кружка100
13Тарелка200
21Чайник300

Пошаговый разбор

Query хранит условие и параметры чтения. Server::executeQuery выполняет его на выбранном узле. namespace включает имя базы и коллекции; это не имя одного поля.

projection перечисляет нужные поля: _id,title,price. sort задаёт порядок, limit ограничивает число документов. Сервер фильтрует и сортирует данные до передачи; не нужно загружать весь каталог и затем сортировать PHP-массив.

Для следующей страницы используется последняя пара price/_id: либо цена больше, либо цена та же и ID больше. _id снимает неоднозначность равных цен. Это пример продолжения по ключу, а не загрузка и пропуск всех предыдущих строк.

Третий аргумент executeQuery — массив options или null. В текущем2.x отдельный объект ReadPreference на его месте недопустим. Если указать readPreference внутри массива, другой Server автоматически выбран не будет.

Границы и ошибки

Поле tenant в коде задано приложением; пользовательский ввод нельзя без проверки превращать в условие чужого магазина. Составной индекс под фильтр/сортировку и explain нужны для реального объёма. Изменение цен между страницами может менять порядок: это не согласованный снимок каталога.

Индексы и план чтения · Ограничение памяти при чтении.

Полная самостоятельная программа и архив
Получить только видимые товары и нужные поля страницыСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
sr129Insert($manager,$namespace,[['_id'=>1,'tenant'=>'A','active'=>true,'title'=>'Чайник','price'=>300,'internal'=>'hidden'],['_id'=>2,'tenant'=>'A','active'=>true,'title'=>'Кружка','price'=>100,'internal'=>'hidden'],['_id'=>3,'tenant'=>'A','active'=>true,'title'=>'Тарелка','price'=>200,'internal'=>'hidden'],['_id'=>4,'tenant'=>'B','active'=>true,'title'=>'Чужой товар','price'=>10],['_id'=>5,'tenant'=>'A','active'=>false,'title'=>'Черновик','price'=>50]]);
$filter=['tenant'=>'A','active'=>true];$projection=['_id'=>1,'title'=>1,'price'=>1];
$first=sr129Query($server,$namespace,$filter,$projection,2,['price'=>1,'_id'=>1]);
$last=$first[count($first)-1];
$nextFilter=$filter+['$or'=>[['price'=>['$gt'=>$last['price']]],['price'=>$last['price'],'_id'=>['$gt'=>$last['_id']]]]];
$second=sr129Query($server,$namespace,$nextFilter,$projection,2,['price'=>1,'_id'=>1]);
$rows=[];foreach([1=>$first,2=>$second] as $page=>$docs){foreach($docs as $doc){$rows[]=[$page,$doc['_id'],$doc['title'],$doc['price']];}}
$checks=['page_one_ids'=>array_column($first,'_id')===[2,3],'page_two_id'=>array_column($second,'_id')===[1],'only_requested_fields'=>array_keys($first[0])===['_id','title','price'],'no_unrelated_or_draft'=>array_column(array_merge($first,$second),'_id')===[2,3,1],'page_bound'=>count($first)<=2&&count($second)<=2];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
sr129Report('mongodb129-server-query-page', ['Страница', 'ID', 'Название', 'Цена, условные единицы'], $rows, $checks, $runtime);
echo 'mongodb129-server-query-page: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-query-page: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Сделайте две одинаковые цены. Пара price/_id сохранит определённый порядок. Проверьте, что соседняя страница продолжает с большей пары; скрытое internal по-прежнему не возвращается.

Реальная задача разработчика: Почему третий аргумент executeQuery оказался несовместимым. В старом окружении библиотека и расширение ожидали разные формы третьего аргумента. Для2.5.5 показывается nullablearray options и точная история изменения API. Обсуждение даёт контекст; современный контракт сверяется с официальными источниками, а код и объяснения здесь самостоятельные.

Применить пакет изменений каталога и увидеть итог

При обновлении каталога нужно уменьшить остаток MUG, добавить PLATE и убрать DRAFT, затем убедиться, какие действия реально выполнены.

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

До пакета: MUG qty5 и DRAFT qty0. Три операции относятся к одному namespace. Ожидается MUG qty3 и PLATE qty7.

Как идут данные
  1. Подготовка: BulkWrite накапливает update,insert,delete без немедленной отправки
  2. Отправка: Server::executeBulkWrite применяет пакет с подтверждением w=1
  3. Контроль: WriteResult даёт1insert/1update/1delete; Query показывает реальные остатки

Самый короткий полезный вызов

$bulk = new MongoDB\Driver\BulkWrite();
$bulk->update(['_id' => 1, 'qty' => ['$gte' => 2]],
    ['$inc' => ['qty' => -2]], ['multi' => false, 'upsert' => false]);
$result = $server->executeBulkWrite($namespace, $bulk,
    ['writeConcern' => new MongoDB\Driver\WriteConcern(1, 3000)]);
// Подтверждённое изменение MUG: 5 → 3.

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

Артикул после пакетаОстаток
MUG3
PLATE7

Пошаговый разбор

BulkWrite — список действий. Само добавление update ещё не меняет базу; вызов executeBulkWrite отправляет пакет. Драйвер старается объединять действия одного вида, но пакет не обязательно равен одному сетевому сообщению.

Уменьшение остатка защищено условием qty >= 2 и точным _id. $inc меняет значение сервером; multi=false и upsert=false ограничивают смысл. Если товар уже не подходит, MatchedCount будет0 — приложение должно обработать это как отсутствие выполненного резерва.

WriteResult отделяет совпавшие документы от изменённых. Это важнее самого факта «ошибки не было». Пустой BulkWrite специально отклоняется InvalidArgumentException; отправлять нечего.

ordered=true задаёт порядок и остановку после ошибки. Успешные предыдущие действия при этом не откатываются. Для общей атомарности нескольких документов нужен другой механизм, рассмотренный в главе транзакций.

Границы и ошибки

Этот набор неизменяем извне. w=1 означает подтверждение сервером, а не доказанную репликацию/журналирование. Объект BulkWrite одноразовый: для новой попытки создают новый пакет, а решение о повторе учитывает, что уже могло выполниться.

Условные атомарные изменения одного документа · Когда нужна транзакция нескольких действий · Частичный успех, WriteResult и ошибки подтверждения.

Полная самостоятельная программа и архив
Применить пакет изменений каталога и увидеть итогСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
sr129Insert($manager,$namespace,[['_id'=>1,'sku'=>'MUG','qty'=>5],['_id'=>2,'sku'=>'DRAFT','qty'=>0]]);
$bulk=new MongoDB\Driver\BulkWrite(['ordered'=>true]);
$bulk->update(['_id'=>1,'qty'=>['$gte'=>2]],['$inc'=>['qty'=>-2]],['multi'=>false,'upsert'=>false]);
$bulk->insert(['_id'=>3,'sku'=>'PLATE','qty'=>7]);$bulk->delete(['_id'=>2],['limit'=>1]);
$result=$server->executeBulkWrite($namespace,$bulk,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,3000)]);
$docs=sr129Query($server,$namespace,[],['_id'=>1,'sku'=>1,'qty'=>1],3);
$rows=[];foreach($docs as $doc){$rows[]=[$doc['sku'],$doc['qty']];}
$empty=false;try{$server->executeBulkWrite($namespace,new MongoDB\Driver\BulkWrite);}catch(MongoDB\Driver\Exception\InvalidArgumentException){$empty=true;}
$checks=['one_insert'=>$result->getInsertedCount()===1,'one_update'=>$result->getMatchedCount()===1&&$result->getModifiedCount()===1,'one_delete'=>$result->getDeletedCount()===1,'stock_result'=>array_column($docs,'qty')===[3,7],'deleted_draft'=>array_column($docs,'_id')===[1,3],'empty_batch_rejected'=>$empty];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
sr129Report('mongodb129-server-bulk-stock', ['Артикул после пакета', 'Остаток'], $rows, $checks, $runtime);
echo 'mongodb129-server-bulk-stock: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-bulk-stock: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Поставьте MUG qty1. Условие уменьшения не совпадёт, поэтому нынешняя проверка одного update остановит программу. Измените сценарий интерфейса так, чтобы показать «недостаточно остатка», а не объявлять резерв успешным.

Записать товар и запись журнала одним BulkWriteCommand

Импорт создаёт товар и запись журнала в разных коллекциях. Покажем API MongoDB8, где namespace указывается для каждой операции внутри одного BulkWriteCommand.

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

Две новые собственные коллекции: каталог и журнал. В каталоге MUG qty4; в журнале событие catalog-import для MUG. Их случайные имена не выходят за список принадлежащих программе.

Как идут данные
  1. Две операции: insertOne получает namespace каталога, затем namespace журнала
  2. Server: executeBulkWriteCommand отправляет команду bulkWrite MongoDB8
  3. Результат: insertedCount=2 и отдельное чтение подтверждают обе записи

Самый короткий полезный вызов

$bulk = new MongoDB\Driver\BulkWriteCommand();
$bulk->insertOne($catalogNamespace, ['_id' => 1, 'sku' => 'MUG', 'qty' => 4]);
$bulk->insertOne($journalNamespace, ['_id' => 1, 'event' => 'catalog-import']);
$result = $server->executeBulkWriteCommand($bulk);
// Два namespace внутри одной команды; это ещё не общая транзакция.

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

Назначение коллекцииАртикулСохранено
КаталогMUGqty=4
ЖурналMUGcatalog-import

Пошаговый разбор

BulkWriteCommand отличается от BulkWrite: namespace передаётся каждой операции. executeBulkWriteCommand поэтому не принимает отдельный namespace первым аргументом. Не смешивайте эти две сигнатуры.

Требуются ext-mongodb2.1+ и сервер MongoDB8.0+. Названия похожи, но новый метод использует серверную команду bulkWrite. В примере не нужен Composer.

BulkWriteCommandResult содержит итоги пакета. Счётчик вставок подтверждает две вставки; затем читаются оба документа с projection и limit1. Так проверяется полезный результат, а не только число операций.

Отдельная запись журнала в том же пакете удобна для импорта, но не гарантирует «товар и журнал существуют вместе». Если одно действие отказало после другого, возможен частичный результат. Критический бизнес-инвариант требует транзакции или специально спроектированного протокола.

Границы и ошибки

На MongoDB7 и старее этот API не заменяет старый BulkWrite. Пустой или уже отправленный BulkWriteCommand недопустим. Подробный разбор частичных ошибок приведён в связанной главе результатов записи; нельзя без анализа повторять весь пакет.

Условные атомарные изменения одного документа · Когда нужна транзакция нескольких действий · Частичный успех, WriteResult и ошибки подтверждения.

Полная самостоятельная программа и архив
Записать товар и запись журнала одним BulkWriteCommandСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
$journalNs=sr129Create($manager,$database,$owned);
$bulk=new MongoDB\Driver\BulkWriteCommand;
$bulk->insertOne($namespace,['_id'=>1,'sku'=>'MUG','qty'=>4]);
$bulk->insertOne($journalNs,['_id'=>1,'event'=>'catalog-import','sku'=>'MUG']);
$result=$server->executeBulkWriteCommand($bulk,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,3000)]);
$product=sr129Query($server,$namespace,['_id'=>1],['_id'=>0,'sku'=>1,'qty'=>1],1)[0];
$entry=sr129Query($server,$journalNs,['_id'=>1],['_id'=>0,'event'=>1,'sku'=>1],1)[0];
$rows=[['Каталог',$product['sku'],'qty='.$product['qty']],['Журнал',$entry['sku'],$entry['event']]];
$checks=['inserted_two'=>$result->getInsertedCount()===2,'two_own_namespaces'=>$namespace!==$journalNs&&count($owned)===2,'catalog_document'=>$product===['sku'=>'MUG','qty'=>4],'journal_document'=>$entry===['event'=>'catalog-import','sku'=>'MUG']];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===2;
sr129Report('mongodb129-server-bulk-command', ['Назначение коллекции', 'Артикул', 'Сохранено'], $rows, $checks, $runtime);
echo 'mongodb129-server-bulk-command: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-bulk-command: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Измените артикул одновременно в обеих операциях. Результат должен показать одинаковую связь товара и журнала. Затем сформулируйте, допустимо ли для вашего импорта отсутствие одной из двух записей.

Проверить узел и собрать маленький отчёт командой чтения

Панель магазина должна показать сумму только оплаченных заказов. Одновременно диагностика должна различать «узел отвечает» и «нужные данные действительно прочитаны».

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

Четыре заказа: два оплаченных A на100 и250, черновик A на900 и оплаченный B на800. Пустой диагностический ping не заменяет запрос этих данных.

Как идут данные
  1. ping: executeCommand возвращает подтверждение ответа узла
  2. aggregate: executeReadCommand: match A/paid,group →2заказа/350
  3. HTML: видны разные результаты диагностики и предметного отчёта

Самый короткий полезный вызов

$cursor = $server->executeReadCommand($database, $aggregateCommand,
    ['readConcern' => new MongoDB\Driver\ReadConcern('local')]);
// match shop=A,status=paid → group → 2 заказа и сумма350.
// executeCommand(ping) отдельно проверяет ответ узла, не доступ к заказам.

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

ПоказательРезультат
Узел доступенtrue
Оплаченных заказов магазина A2
Сумма магазина A350

Пошаговый разбор

Command содержит документ команды; имя первой операции aggregate относится к MongoDB, а не к PHP-функции. $match отбирает нужные записи до группировки, $group считает на сервере, $limit ограничивает выход.

executeReadCommand применяет правила команды чтения: readPreference и readConcern берутся из операции/активной транзакции/URI по контракту. В примере local указан явно. Это уровень чтения, а не правило выбора другого объекта Server.

executeCommand является общим низкоуровневым методом. Вне активной транзакции он не наследует concern-настройки из URI так, как специализированные методы. Поэтому для отчёта применён read-метод, а generic оставлен для ping.

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

Границы и ошибки

Ping может пройти при отсутствии прав на рабочую коллекцию. Проверка связи не гарантирует успех следующего запроса. Суммы здесь целые условные единицы; финансовая модель с валютой/Decimal128 требует отдельного контракта. local не обещает большинства реплик или транзакционного снимка.

Полная самостоятельная программа и архив
Проверить узел и собрать маленький отчёт командой чтенияСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
sr129Insert($manager,$namespace,[['_id'=>1,'shop'=>'A','status'=>'paid','amount'=>100],['_id'=>2,'shop'=>'A','status'=>'paid','amount'=>250],['_id'=>3,'shop'=>'A','status'=>'draft','amount'=>900],['_id'=>4,'shop'=>'B','status'=>'paid','amount'=>800]]);
$ping=sr129Rows($server->executeCommand($database,new MongoDB\Driver\Command(['ping'=>1])))[0];
$command=new MongoDB\Driver\Command(['aggregate'=>$collection,'pipeline'=>[['$match'=>['shop'=>'A','status'=>'paid']],['$group'=>['_id'=>'$shop','orders'=>['$sum'=>1],'total'=>['$sum'=>'$amount']]],['$limit'=>1]],'cursor'=>['batchSize'=>2],'maxTimeMS'=>1500]);
$report=sr129Rows($server->executeReadCommand($database,$command,['readConcern'=>new MongoDB\Driver\ReadConcern('local')]))[0];
$rows=[['Узел доступен',(bool)$ping['ok']],['Оплаченных заказов магазина A',$report['orders']],['Сумма магазина A',$report['total']]];
$checks=['ping_ok'=>(bool)$ping['ok']===true,'selected_only_shop_a'=>$report['_id']==='A','paid_count'=>$report['orders']===2,'paid_total'=>$report['total']===350,'source_intact'=>count(sr129Query($server,$namespace,[],['_id'=>1],5))===4];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
sr129Report('mongodb129-server-read-command', ['Показатель', 'Результат'], $rows, $checks, $runtime);
echo 'mongodb129-server-read-command: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-read-command: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Смените статус черновика на paid: результат A станет3заказа и1250. Заказ магазина B должен остаться за пределами суммы.

Создать индекс собственной коллекции и проверить его правило

Нужно запретить два товара с одинаковым артикулом и безопасно повторять подготовку коллекции при запуске импорта.

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

Два разных SKU в собственной коллекции. Создаётся уникальный индекс sku_unique, затем та же подготовка повторяется; попытка второго MUG должна отказать.

Как идут данные
  1. Подготовка: executeWriteCommand выполняет createIndexes с unique=true
  2. Проверка: executeReadCommand listIndexes показывает фактическое правило
  3. Поведение: повтор подготовки успешен, повторный SKU отклонён, товаров по-прежнему2

Самый короткий полезный вызов

$command = new MongoDB\Driver\Command([
    'createIndexes' => $collection,
    'indexes' => [['key' => ['sku' => 1], 'name' => 'sku_unique', 'unique' => true]]
]);
$cursor = $server->executeWriteCommand($database, $command);
// Индекс не допускает второй документ с тем же sku.

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

ДействиеРезультат
Индексsku_unique
Правило uniquetrue
Повтор подготовки успешенtrue
Повторный MUG отклонёнtrue
Товаров осталось2

Пошаговый разбор

executeWriteCommand нужен для команд записи вроде создания индекса или коллекции. Документация не рекомендует использовать его для сырых insert/update/delete: эти операции выполняются BulkWrite.

createIndexes получает имя коллекции и список индексов. key sku:1 задаёт поле; name — стабильное имя; unique=true создаёт ограничение. Стандартный индекс _id остаётся.

Повтор точно такого определения безопасен в нашей неизменяемой схеме. Другие параметры под тем же именем могут вызвать конфликт — не скрывайте его как успешную подготовку.

Попытка вставки MUG с новым _id отклоняется кодом11000, поскольку конфликтует sku. Пример ловит только этот ожидаемый отказ; ошибка связи или другое нарушение не превращается в «индекс работает».

Границы и ошибки

Индекс создаётся только в собственной маленькой коллекции. На рабочем объёме создание индекса требует планирования нагрузки и разбора существующих дублей. unique не означает автоматическое ускорение любого запроса; выбор индекса проверяют explain.

Полная самостоятельная программа и архив
Создать индекс собственной коллекции и проверить его правилоСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
sr129Insert($manager,$namespace,[['_id'=>1,'sku'=>'MUG','qty'=>4],['_id'=>2,'sku'=>'PLATE','qty'=>7]]);
$command=new MongoDB\Driver\Command(['createIndexes'=>$collection,'indexes'=>[['key'=>['sku'=>1],'name'=>'sku_unique','unique'=>true]],'maxTimeMS'=>1500]);
$first=sr129Rows($server->executeWriteCommand($database,$command,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,3000)]))[0];
$second=sr129Rows($server->executeWriteCommand($database,$command,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,3000)]))[0];
$indexes=sr129Rows($server->executeReadCommand($database,new MongoDB\Driver\Command(['listIndexes'=>$collection,'cursor'=>['batchSize'=>10],'maxTimeMS'=>1500])));
$found=null;foreach($indexes as $index){if($index['name']==='sku_unique'){$found=$index;}}
$duplicate=false;$bulk=new MongoDB\Driver\BulkWrite;$bulk->insert(['_id'=>3,'sku'=>'MUG','qty'=>1]);
try{$server->executeBulkWrite($namespace,$bulk);}catch(MongoDB\Driver\Exception\BulkWriteException $error){$duplicate=$error->getCode()===11000;if(!$duplicate){throw $error;}}
$docs=sr129Query($server,$namespace,[],['_id'=>1,'sku'=>1],3);
$rows=[['Индекс',$found['name']],['Правило unique',$found['unique']],['Повтор подготовки успешен',(bool)$second['ok']],['Повторный MUG отклонён',$duplicate],['Товаров осталось',count($docs)]];
$checks=['write_command_ok'=>(bool)$first['ok']===true,'unique_index'=>$found!==null&&$found['key']===['sku'=>1]&&$found['unique']===true,'repeat_is_safe'=>(bool)$second['ok']===true,'duplicate_rejected'=>$duplicate,'catalog_intact'=>array_column($docs,'_id')===[1,2]];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
sr129Report('mongodb129-server-write-command', ['Действие', 'Результат'], $rows, $checks, $runtime);
echo 'mongodb129-server-write-command: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-write-command: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Переименуйте повторный SKU в BOOK: вставка станет допустимой, а проверку ожидаемого дубля нужно изменить. Так различаются конфликт _id и конфликт прикладного поля sku.

Сохранить и обновить готовый отчёт через aggregate и merge

Для панели нужен готовый небольшой отчёт по магазину, который можно сохранить и позже обновить из заказов, не пересчитывая весь набор в PHP.

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

Три заказа: A100,A250,B800. Отчёт хранится во второй собственной коллекции под _id=A. Затем одна сумма A меняется с250 на300.

Как идут данные
  1. Чтение: match shop=A →group даёт orders2,total350
  2. Запись: финальный $merge вставляет или заменяет отчёт A
  3. Обновление: после изменения исходника повторный merge даёт400 и сохраняет одну запись

Самый короткий полезный вызов

$cursor = $server->executeReadWriteCommand($database, $mergeCommand,
    ['writeConcern' => new MongoDB\Driver\WriteConcern(1, 3000)]);
// aggregate читает заказы, $merge пишет отчёт в отдельную свою коллекцию.
// Итог читается отдельным Query: A,2заказа,сумма350.

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

ЭтапМагазинЗаказовСумма
Первое сохранениеA2350
После изменения исходной суммыA2400

Пошаговый разбор

Этот aggregate одновременно читает и пишет: стадия $merge обязана быть последней и указывает отдельную коллекцию результата. Поэтому используется executeReadWriteCommand.

on:_id задаёт ключ соответствия, whenMatched:replace заменяет существующий отчёт, whenNotMatched:insert создаёт новый. Мы заранее создаём выходную коллекцию и регистрируем её как собственную, чтобы очистка не затронула чужое.

Cursor такой команды не содержит строк отчёта: команда записала их в коллекцию. Отдельный ограниченный Query читает сохранённый результат. Пустой Cursor здесь не означает «отчёт не создан».

После изменения заказа повторная команда заменяет A, а не добавляет дубликат. Это полезно для пересчёта выбранной группы. Но $merge не удаляет ранее сохранённые группы, отсутствующие в новом результате: это не полная синхронизация всех данных.

Границы и ошибки

Чтение исходника и запись отчёта не превращаются в общую транзакцию. Здесь данные меняются последовательно в собственной лаборатории. В реальном сервисе нужны правила свежести, конкурентного пересчёта и удаления устаревших групп. Standalone-пример не проверяет маршрутизацию merge в replica set.

Условные атомарные изменения одного документа · Когда нужна транзакция нескольких действий · Частичный успех, WriteResult и ошибки подтверждения.

Полная самостоятельная программа и архив
Сохранить и обновить готовый отчёт через aggregate и mergeСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
$reportNs=sr129Create($manager,$database,$owned);$reportName=$owned[1];
sr129Insert($manager,$namespace,[['_id'=>1,'shop'=>'A','amount'=>100],['_id'=>2,'shop'=>'A','amount'=>250],['_id'=>3,'shop'=>'B','amount'=>800]]);
$command=new MongoDB\Driver\Command(['aggregate'=>$collection,'pipeline'=>[['$match'=>['shop'=>'A']],['$group'=>['_id'=>'$shop','total'=>['$sum'=>'$amount'],'orders'=>['$sum'=>1]]],['$limit'=>1],['$merge'=>['into'=>$reportName,'on'=>'_id','whenMatched'=>'replace','whenNotMatched'=>'insert']]],'cursor'=>['batchSize'=>2],'maxTimeMS'=>1500]);
$firstCursor=sr129Rows($server->executeReadWriteCommand($database,$command,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,3000)]));
$before=sr129Query($server,$reportNs,['_id'=>'A'],['_id'=>1,'total'=>1,'orders'=>1],1)[0];
$bulk=new MongoDB\Driver\BulkWrite;$bulk->update(['_id'=>2],['$set'=>['amount'=>300]],['multi'=>false,'upsert'=>false]);$server->executeBulkWrite($namespace,$bulk);
$nextCursor=sr129Rows($server->executeReadWriteCommand($database,$command,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,3000)]));
$after=sr129Query($server,$reportNs,[],['_id'=>1,'total'=>1,'orders'=>1],2);
$rows=[['Первое сохранение',$before['_id'],$before['orders'],$before['total']],['После изменения исходной суммы',$after[0]['_id'],$after[0]['orders'],$after[0]['total']]];
$checks=['merge_no_document_cursor'=>$firstCursor===[]&&$nextCursor===[],'initial_report'=>$before['total']===350&&$before['orders']===2,'refreshed_report'=>$after[0]['total']===400&&$after[0]['orders']===2,'one_report_not_duplicate'=>count($after)===1&&$after[0]['_id']==='A','only_own_targets'=>count($owned)===2&&$namespace!==$reportNs];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===2;
sr129Report('mongodb129-server-readwrite-command', ['Этап', 'Магазин', 'Заказов', 'Сумма'], $rows, $checks, $runtime);
echo 'mongodb129-server-readwrite-command: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-readwrite-command: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Удалите все заказы A перед пересчётом. Старая строка отчёта сама не исчезнет: отдельно определите политику для отсутствующих групп, вместо ожидания полного зеркалирования от $merge.

Различить незаданные и явные настройки Stable API

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

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

Три ServerApi: только V1; V1,false,false; V1,true,true. В собственной коллекции лежит MUG, который читается строгим API-клиентом.

Как идут данные
  1. Настройка: ServerApi сохраняет версию1 и nullable-флаги
  2. Manager: объект передан в третьем аргументе driverOptions.serverApi
  3. Проверка: bsonSerialize показывает заданные поля; strict-клиент действительно читает MUG

Самый короткий полезный вызов

$api = new MongoDB\Driver\ServerApi(
    MongoDB\Driver\ServerApi::V1, true, true
);
$manager = new MongoDB\Driver\Manager($uri, [], ['serverApi' => $api]);
echo json_encode($api->bsonSerialize(), JSON_THROW_ON_ERROR);
// {"version":"1","strict":true,"deprecationErrors":true}

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

Настройка / операцияНаблюдаемое представление
По умолчанию{"version":"1"}
Явное false{"version":"1","strict":false,"deprecationErrors":false}
Строгий API{"version":"1","strict":true,"deprecationErrors":true}
Чтение строгим клиентомMUG

Пошаговый разбор

Stable API — версия договора команд MongoDB. Число1 не означает MongoDB1, PHP1 или версию расширения. Константа называется V1 и содержит строку "1".

strict=true запрещает команды/возможности вне заявленного API. deprecationErrors=true просит сервер отклонять возможности, устаревшие в данном API. Это не управление PHP E_DEPRECATED и не выключение предупреждений приложения.

null означает «не задавали». В сериализованном представлении такие поля отсутствуют. Явный false сохраняется как false. Значение по умолчанию на сервере для этих флагов false, но происхождение настройки различается.

bsonSerialize возвращает stdClass с version,strict,deprecationErrors. Это описание PHP-объекта, не готовая команда с wire-полями apiVersion/apiStrict. Настройка подключается через Manager, а не вставляется вручную в каждую Query.

В драйвере2.5.5 ServerApi реализует MongoDB\BSON\Serializable, но не старый PHP Serializable, который всё ещё показан на просмотренной странице PHP.net. Текущий контракт подтверждён tagged stub/source. Не путайте BSON-сериализацию и пользовательский JSON-DTO.

Границы и ошибки

Stable API поддерживается MongoDB5.0+ и только в пределах заявленного набора команд и совместимого драйвера. Он не фиксирует схему ваших документов, время выполнения запросов, доступность сервера или поведение внешних библиотек. Unsupported версия2 намеренно отклоняется конструктором.

Полная самостоятельная программа и архив
Различить незаданные и явные настройки Stable APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
sr129Insert($manager,$namespace,[['_id'=>1,'sku'=>'MUG']]);
$default=new MongoDB\Driver\ServerApi(MongoDB\Driver\ServerApi::V1);
$explicit=new MongoDB\Driver\ServerApi(MongoDB\Driver\ServerApi::V1,false,false);
$strict=new MongoDB\Driver\ServerApi(MongoDB\Driver\ServerApi::V1,true,true);
$shapes=[];$rows=[];foreach(['По умолчанию'=>$default,'Явное false'=>$explicit,'Строгий API'=>$strict] as $label=>$api){$object=$api->bsonSerialize();$shapes[$label]=(array)$object;$rows[]=[$label,json_encode($object,JSON_THROW_ON_ERROR)];}
$apiManager=sr129ApiManager($settings,$strict);$apiServer=$apiManager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
$docs=sr129Query($apiServer,$namespace,['_id'=>1],['_id'=>0,'sku'=>1],1);$rows[]=['Чтение строгим клиентом',$docs[0]['sku']];
$invalid=false;try{new MongoDB\Driver\ServerApi('2');}catch(MongoDB\Driver\Exception\InvalidArgumentException){$invalid=true;}
$checks=['defaults_omit_null'=>$shapes['По умолчанию']===['version'=>'1'],'false_is_explicit'=>$shapes['Явное false']===['version'=>'1','strict'=>false,'deprecationErrors'=>false],'true_is_explicit'=>$shapes['Строгий API']===['version'=>'1','strict'=>true,'deprecationErrors'=>true],'returns_stdclass'=>$strict->bsonSerialize() instanceof stdClass,'typed_api_reads'=>$docs===[['sku'=>'MUG']],'unknown_version_rejected'=>$invalid];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
sr129Report('mongodb129-server-stable-config', ['Настройка / операция', 'Наблюдаемое представление'], $rows, $checks, $runtime);
echo 'mongodb129-server-stable-config: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-stable-config: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Сравните V1,null,false с V1,false,null. В первом объекте останется deprecationErrors:false, во втором strict:false. Явно выбирайте настройки, которые должен контролировать конфигурационный файл.

Отделить рабочие команды Stable API от диагностики вне API

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

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

Один MUG qty4. Строгий Server взят из Manager с ServerApi(V1,true,true); обычный диагностический Server — из исходного Manager.

Как идут данные
  1. Рабочие операции: find и count проходят через strict-клиент
  2. Граница API: buildInfo через тот же Server возвращает APIStrictError323
  3. Диагностика: обычный клиент получает версию; товар после отказа не изменился

Самый короткий полезный вызов

$api = new MongoDB\Driver\ServerApi(MongoDB\Driver\ServerApi::V1, true, true);
// Для Server, полученного от такого Manager:
// executeQuery(find) работает; executeCommand(buildInfo) даёт APIStrictError323.
// Не отключаем strict молча после ошибки: диагностика имеет отдельный клиент.

Фрагмент выделяет текущую операцию. Полная программа ниже содержит подключение, создание данных, ограниченные чтения и очистку собственной лаборатории.

Что получится при выполнении

Команда / проверкаРезультат
find через строгий клиентMUG
count через строгий клиент1
buildInfo через строгий клиентAPIStrictError 323
buildInfo через обычный клиентtrue
Данные после отказа сохраненыtrue

Пошаговый разбор

buildInfo не входит в Stable API V1 в проверенном сервере8.0.34. Поэтому отказ strict-клиента является ожидаемой проверкой контракта, а не доказательством, что база отключена.

Ловится конкретный CommandException с кодом323. Другие ошибки передаются выше. Не надо перехватывать всё подряд и повторять команду уже без strict: это незаметно отменяет выбранный договор приложения.

В примере диагностика использует отдельный заранее созданный обычный Manager. В реальном сервисе отделите этот маршрут по назначению и разрешениям. Возможности вне API не получают его гарантий совместимости.

Для deprecationErrors включён реальный флаг и выполнены разрешённые команды. Мы не объявляем любой устаревший синтаксис PHP серверной API-deprecation и не запускаем опасную историческую команду ради искусственного отказа.

Границы и ошибки

Это проверка конкретных команд в MongoDB8.0.34, а не сертификат всего приложения на Stable API. Нужен разбор используемых команд/опций и поддерживаемых версий. Успех count не означает, что следует пересчитывать огромную коллекцию на каждый HTTP-запрос.

Полная самостоятельная программа и архив
Отделить рабочие команды Stable API от диагностики вне APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/sr129-helper.php';
$settings=sr129Settings();$database=$settings['database'];
$manager=new MongoDB\Driver\Manager($settings['uri'],['serverSelectionTimeoutMS'=>1500,'connectTimeoutMS'=>500,'socketTimeoutMS'=>2000,'retryReads'=>false]);
$owned=[];
try{
$version=sr129Command($manager,$database,['buildInfo'=>1])[0]['version'];
if(version_compare($version,'8.0','<')){throw new RuntimeException('MongoDB8.0+ required');}
$server=$manager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
if($server->getType()!==MongoDB\Driver\Server::TYPE_STANDALONE){throw new RuntimeException('Separate standalone lab required');}
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>$version,'execution'=>'database','observed_host'=>$server->getHost(),'observed_port'=>$server->getPort()];
$namespace=sr129Create($manager,$database,$owned);$collection=$owned[0];
sr129Insert($manager,$namespace,[['_id'=>1,'sku'=>'MUG','qty'=>4]]);
$api=new MongoDB\Driver\ServerApi(MongoDB\Driver\ServerApi::V1,true,true);
$apiManager=sr129ApiManager($settings,$api);$apiServer=$apiManager->selectServer(new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::PRIMARY));
$product=sr129Query($apiServer,$namespace,['_id'=>1],['_id'=>0,'sku'=>1,'qty'=>1],1)[0];
$count=sr129Rows($apiServer->executeReadCommand($database,new MongoDB\Driver\Command(['count'=>$collection,'query'=>['_id'=>1],'maxTimeMS'=>1500])))[0];
$errorCode=0;try{sr129Rows($apiServer->executeCommand($database,new MongoDB\Driver\Command(['buildInfo'=>1])));}catch(MongoDB\Driver\Exception\CommandException $error){$errorCode=$error->getCode();if($errorCode!==323){throw $error;}}
$normal=sr129Rows($server->executeCommand($database,new MongoDB\Driver\Command(['buildInfo'=>1])))[0];
$after=sr129Query($apiServer,$namespace,['_id'=>1],['_id'=>0,'sku'=>1,'qty'=>1],1)[0];
$rows=[['find через строгий клиент',$product['sku']],['count через строгий клиент',(int)$count['n']],['buildInfo через строгий клиент','APIStrictError '.$errorCode],['buildInfo через обычный клиент',isset($normal['version'])],['Данные после отказа сохранены',$after===$product]];
$checks=['stable_find_succeeds'=>$product===['sku'=>'MUG','qty'=>4],'stable_count_succeeds'=>(int)$count['n']===1,'strict_rejects_outside_api'=>$errorCode===323,'normal_diagnostics_allowed'=>isset($normal['version'])&&$normal['version']===$version,'data_unchanged'=>$after===$product];
}finally{
    foreach(array_reverse($owned) as $name){sr129Command($manager,$database,['drop'=>$name],true);}
}
$removed=true;foreach($owned as $name){if(sr129Exists($manager,$database,$name)){$removed=false;}}
$checks['only_owned_collections_removed']=$removed && count($owned)===1;
sr129Report('mongodb129-server-strict-api', ['Команда / проверка', 'Результат'], $rows, $checks, $runtime);
echo 'mongodb129-server-strict-api: saved result.html and result.json
';
Ожидаемый результат
mongodb129-server-strict-api: saved result.html and result.json

Реальный запуск в собственной учебной MongoDB-коллекции. Результат показан в HTML и JSON. Helper и README входят в архив; неожиданные отказы не подменяются успешными строками.

Таблица фактического результата · JSON результата

Попробуйте изменить. Включите strict=false в отдельной копии: buildInfo сможет пройти, но теперь команды вне API разрешены. Объясните в конфигурации, почему выбран такой режим, вместо молчаливого fallback.

Полные контракты операций Server и настроек ServerApi

Здесь собраны новые карточки. Уже существующие getHost/getPort/getType/getServerDescription и другие методы диагностики сохраняют прежние адреса. Таблица типов узла и полный synopsis класса приведены в карточке Server.

APIЗадача
MongoDB\Driver\ServerВыполнить операцию на конкретном выбранном узле и связать её с наблюдаемым ответом.
MongoDB\Driver\Server::__constructПонять происхождение Server и не создавать его через new.
MongoDB\Driver\Server::executeQueryПолучить ограниченную страницу документов на выбранном сервере.
MongoDB\Driver\Server::executeBulkWriteОтправить подготовленные изменения одной коллекции.
MongoDB\Driver\Server::executeBulkWriteCommandОтправить операции для разных коллекций через bulkWrite MongoDB8.
MongoDB\Driver\Server::executeCommandОтправить низкоуровневую команду осознанно, с явными ожиданиями опций.
MongoDB\Driver\Server::executeReadCommandВыполнить команду чтения и получить её ограниченный результат.
MongoDB\Driver\Server::executeReadWriteCommandПрочитать исходные данные и сохранить результат командой.
MongoDB\Driver\Server::executeWriteCommandВыполнить команду изменения структуры или состояния базы.
MongoDB\Driver\ServerApiОбъявить стабильный договор команд приложения при создании Manager.
MongoDB\Driver\ServerApi::__constructСоздать настройки версии API, strict и deprecationErrors.
MongoDB\Driver\ServerApi::bsonSerializeПолучить документ настроек и различить пропуск поля от false.

Закрепить назначение запроса и его границы

Для своего запроса сначала определите нужные документы и поля, затем характер действия: чтение, запись или оба. Обычно Manager выбирает узел за приложение; если нужен конкретный Server, ответственность за выбор становится явной. Выберите подходящий execute-метод и наблюдаемый результат, а Stable API настройте как отдельный договор используемых возможностей.

Продолжение: Выбор серверов · Жизненный цикл Cursor · Результаты записи и понятная диагностика ошибок · Подтверждения и транзакции.

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

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

MongoDB\Driver\ServerВыполнить операцию на конкретном выбранном узле и связать её с наблюдаемым ответом.

mongodb1.0.0 · mongodb

final class MongoDB\Driver\Server {
    final private function __construct();
    final public function executeBulkWrite(string $namespace, MongoDB\Driver\BulkWrite $bulk, ?array $options = null): MongoDB\Driver\WriteResult;
    final public function executeBulkWriteCommand(MongoDB\Driver\BulkWriteCommand $bulk, ?array $options = null): MongoDB\Driver\BulkWriteCommandResult;
    final public function executeCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeQuery(string $namespace, MongoDB\Driver\Query $query, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeReadCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeReadWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function getHost(): string;
    final public function getInfo(): array;
    final public function getLatency(): ?int;
    final public function getPort(): int;
    final public function getServerDescription(): MongoDB\Driver\ServerDescription;
    final public function getTags(): array;
    final public function getType(): int;
    final public function isArbiter(): bool;
    final public function isHidden(): bool;
    final public function isPassive(): bool;
    final public function isPrimary(): bool;
    final public function isSecondary(): bool;
}

Выполнить операцию на конкретном выбранном узле и связать её с наблюдаемым ответом.

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

final class MongoDB\Driver\Server {
    final private function __construct();
    final public function executeBulkWrite(string $namespace, MongoDB\Driver\BulkWrite $bulk, ?array $options = null): MongoDB\Driver\WriteResult;
    final public function executeBulkWriteCommand(MongoDB\Driver\BulkWriteCommand $bulk, ?array $options = null): MongoDB\Driver\BulkWriteCommandResult;
    final public function executeCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeQuery(string $namespace, MongoDB\Driver\Query $query, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeReadCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeReadWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function executeWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor;
    final public function getHost(): string;
    final public function getInfo(): array;
    final public function getLatency(): ?int;
    final public function getPort(): int;
    final public function getServerDescription(): MongoDB\Driver\ServerDescription;
    final public function getTags(): array;
    final public function getType(): int;
    final public function isArbiter(): bool;
    final public function isHidden(): bool;
    final public function isPassive(): bool;
    final public function isPrimary(): bool;
    final public function isSecondary(): bool;
}
Вход / часть APIСмысл
ПолучениеManager::selectServer/getServers или Cursor::getServer; не new Server
Типfinal-класс; операции привязаны к этому узлу
КонстантыПолный перечень типов сервера приведён ниже; это сведения о топологии, не разрешения на запись

Что получает вызывающий код

Объект Server содержит ссылку на конкретный сервер Manager. Методы запросов/команд возвращают Cursor, методы пакетов — соответствующий результат записи. Диагностические getters сохраняют свои ранее созданные карточки.

Ошибки и ограничения вызова

Private-конструктор недоступен. Выбор узла может отказать до операции, а связь/авторизация/команда — во время неё. Наличие Server не гарантирует постоянную доступность или способность выполнять запись.

Версии

Класс с mongodb1.0.0; read/write command-методы с1.4.0; TYPE_LOAD_BALANCER с1.11.0; executeBulkWriteCommand с2.1.0 и MongoDB8.0. Исторические object-options заменены массивами в2.0.

Константа ServerЗначениеСмысл
TYPE_UNKNOWN0Тип пока неизвестен
TYPE_STANDALONE1Отдельный сервер
TYPE_MONGOS2Маршрутизатор mongos
TYPE_POSSIBLE_PRIMARY3Возможный primary по сведениям другого участника, ещё не проверенный
TYPE_RS_PRIMARY4Primary replica set
TYPE_RS_SECONDARY5Secondary replica set
TYPE_RS_ARBITER6Арбитр replica set
TYPE_RS_OTHER7Другой участник, например запускающийся/recovering/hidden; запросы недоступны, сведения о хостах могут помогать обнаружению
TYPE_RS_GHOST8Неинициализированный/удалённый или временно неготовый участник; запросы и hostlist для обнаружения недоступны
TYPE_LOAD_BALANCER9Балансировщик; константа с1.11.0

Существующие карточки getHost/getPort/getType и описаний сервера.

Два практических применения

Связать товар с ответившим узлом

Cursor::getServer сравнивается с выбранным endpoint; реальный MUG подтверждает, что выполнен запрос.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Выполнить чтение и сохранение отчёта на выбранном узле

Server запускает aggregate+$merge; отдельный Query показывает350, затем400 в своей коллекции.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

ReadPreference не перевыбирает сервер внутри его execute-методов. Роль узла может измениться, поэтому не кэшируйте Server как вечную гарантию primary. Для обычного приложения часто достаточно Manager/библиотеки; конкретный Server нужен для осознанного управления и диагностики.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::__constructПонять происхождение Server и не создавать его через new.

mongodb1.0.0 · mongodb

final private function MongoDB\Driver\Server::__construct()

Понять происхождение Server и не создавать его через new.

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

final private function MongoDB\Driver\Server::__construct()

Параметров нет.

Что получает вызывающий код

Пользовательский вызов не предусмотрен. Server создаётся драйвером при работе Manager с соединением; selectServer и getServers предоставляют эти объекты. Конструктор не принимает URI или другие параметры.

Ошибки и ограничения вызова

new MongoDB\Driver\Server() вызывает PHP Error из-за private. Ошибка selectServer при отсутствии подходящего узла относится к Manager; не маскируйте её пустым объектом Server.

Версии

С mongodb1.0.0. Современный путь остаётся Manager → Server; старые примеры с публичным new к этому классу не применяются.

Два практических применения

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

selectServer возвращает рабочий Server, а попытка new отдельно показывает ожидаемый запрет.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Получить Server с настройками Stable API

Server выбирается из специально настроенного Manager; таким образом его запросы получают договор API, а не выдуманные аргументы new Server.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Не обходите private через reflection/unserialize. Адрес выбирается в Manager, а не в конструкторе Server. Список getServers не заменяет проверку пригодности узла для конкретной операции.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::executeQueryПолучить ограниченную страницу документов на выбранном сервере.

mongodb1.0.0 · mongodb

final public function MongoDB\Driver\Server::executeQuery(string $namespace, MongoDB\Driver\Query $query, ?array $options = null): MongoDB\Driver\Cursor

Получить ограниченную страницу документов на выбранном сервере.

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

final public function MongoDB\Driver\Server::executeQuery(string $namespace, MongoDB\Driver\Query $query, ?array $options = null): MongoDB\Driver\Cursor
Вход / часть APIСмысл
namespaceПолное имя база.коллекция
queryQuery с условием, projection, sort, limit и другими параметрами чтения
options.readPreferenceReadPreference; не перенаправляет на другой Server
options.sessionSession этой операции
options=nullПустые опции; readPreference и readConcern объекта Query по умолчанию определяются активной транзакцией, затем URI

Что получает вызывающий код

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

Ошибки и ограничения вызова

ConnectionException — ошибка связи, кроме авторизации; AuthenticationException — отказ авторизации; InvalidArgumentException — неверные аргументы/опции. Остальные отказы команды или запроса относятся к RuntimeException и её специализированным подклассам. Сетевой отказ не является пустым успешным результатом.

Версии

С mongodb1.0.0; массив options с1.4.0. Передача отдельного объекта вместо options объявлена устаревшей в1.21.0 и удалена в2.0.0. Ранее третьим аргументом мог быть ReadPreference; в2.x требуется массив/null.

Два практических применения

Показать каталог без скрытых полей

Сервер вернул2нужных товара через projection/sort/limit; чужой tenant и черновик исключены.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Продолжить выдачу по ключу

Следующая пара price/_id возвращает Чайник, без загрузки предыдущих страниц в PHP.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Операция остаётся на этом Server. readPreference в допустимых опциях влияет на протокол secondary/mongos, но не выбирает другой узел. При записи вызывающий код должен выбрать сервер, способный писать; обычная запись в secondary не разрешена. readConcern задаётся в Query, не как произвольная опция Server::executeQuery.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::executeBulkWriteОтправить подготовленные изменения одной коллекции.

mongodb1.0.0 · mongodb

final public function MongoDB\Driver\Server::executeBulkWrite(string $namespace, MongoDB\Driver\BulkWrite $bulk, ?array $options = null): MongoDB\Driver\WriteResult

Отправить подготовленные изменения одной коллекции.

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

final public function MongoDB\Driver\Server::executeBulkWrite(string $namespace, MongoDB\Driver\BulkWrite $bulk, ?array $options = null): MongoDB\Driver\WriteResult
Вход / часть APIСмысл
namespaceОдин полный namespace база.коллекция
bulkBulkWrite с действиями в этой коллекции
options.sessionSession; нельзя совмещать с w=0
options.writeConcernWriteConcern; по умолчанию активная транзакция, затем URI
options=nullБез явного переопределения опций

Что получает вызывающий код

WriteResult с подтверждёнными итогами вставок/изменений/удалений. Драйвер группирует однотипные операции для сокращения обменов; один BulkWrite не обещает один сетевой запрос.

Ошибки и ограничения вызова

InvalidArgumentException: пустой или уже выполненный BulkWrite, неправильные аргументы, session вместе с неподтверждаемой записью. BulkWriteException: ошибка записи или подтверждения; прочие отказы могут быть RuntimeException. ConnectionException/AuthenticationException сохраняют отдельный смысл.

Версии

С mongodb1.0.0; массив options с1.4.0. Передача отдельного объекта вместо options объявлена устаревшей в1.21.0 и удалена в2.0.0. Пустой пакет с1.3.0 вызывает InvalidArgumentException (раньше BulkWriteException); запрет session+w=0 уточнён в1.4.4.

Два практических применения

Изменить остаток без чтения всего каталога

Точный _id и qty>=2 защищают $inc: MUG5→3, matched/modified=1.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Обновить состав каталога

Тот же пакет добавляет PLATE и удаляет DRAFT, а итоговая таблица показывает оставшиеся позиции.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Объект пакета одноразовый. Частичный успех не откатывается автоматически; повтор требует анализа результата и идемпотентности. Операция остаётся на этом Server. readPreference в допустимых опциях влияет на протокол secondary/mongos, но не выбирает другой узел. При записи вызывающий код должен выбрать сервер, способный писать; обычная запись в secondary не разрешена.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::executeBulkWriteCommandОтправить операции для разных коллекций через bulkWrite MongoDB8.

mongodb2.1.0; MongoDB8.0+ · mongodb

final public function MongoDB\Driver\Server::executeBulkWriteCommand(MongoDB\Driver\BulkWriteCommand $bulk, ?array $options = null): MongoDB\Driver\BulkWriteCommandResult

Отправить операции для разных коллекций через bulkWrite MongoDB8.

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

final public function MongoDB\Driver\Server::executeBulkWriteCommand(MongoDB\Driver\BulkWriteCommand $bulk, ?array $options = null): MongoDB\Driver\BulkWriteCommandResult
Вход / часть APIСмысл
bulkBulkWriteCommand с namespace внутри каждой операции
options.sessionSession; нельзя совмещать с w=0
options.writeConcernWriteConcern; по умолчанию активная транзакция, затем URI
options=nullБез явного переопределения опций

Что получает вызывающий код

BulkWriteCommandResult с подтверждёнными итогами вставок/изменений/удалений. Команда может включать разные namespace, но не становится общей транзакцией.

Ошибки и ограничения вызова

InvalidArgumentException: пустой или уже выполненный BulkWriteCommand, неправильные аргументы, session вместе с неподтверждаемой записью. BulkWriteCommandException: отказ команды, отдельной записи или write concern. ConnectionException/AuthenticationException сохраняют отдельный смысл.

Версии

С mongodb2.1.0; требуется MongoDB8.0+.

Два практических применения

Создать запись каталога

insertOne получает namespace каталога и сохраняет MUG qty4.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Записать событие импорта

Вторая операция того же пакета использует namespace журнала и сохраняет catalog-import. Чтение подтверждает обе записи.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Объект пакета одноразовый. Частичный успех не откатывается автоматически; повтор требует анализа результата и идемпотентности. Операция остаётся на этом Server. readPreference в допустимых опциях влияет на протокол secondary/mongos, но не выбирает другой узел. При записи вызывающий код должен выбрать сервер, способный писать; обычная запись в secondary не разрешена.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::executeCommandОтправить низкоуровневую команду осознанно, с явными ожиданиями опций.

mongodb1.0.0 · mongodb

final public function MongoDB\Driver\Server::executeCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor

Отправить низкоуровневую команду осознанно, с явными ожиданиями опций.

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

final public function MongoDB\Driver\Server::executeCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor
Вход / часть APIСмысл
dbИмя базы, не полный namespace
commandMongoDB\Driver\Command с документом команды
options.sessionSession операции
options.readConcernReadConcern (поддержка сервером3.2+; более старый сервер откажет)
options.readPreferenceReadPreference; не перевыбирает Server
options.writeConcernWriteConcern подтверждения записи

Что получает вызывающий код

Cursor результата команды; форма документов зависит от самой команды. Не применяет специальную логику. Настройки concern/readPreference наследуются из активной транзакции; вне неё URI-настройки не подставляются, используется первичный режим.

Ошибки и ограничения вызова

ConnectionException — ошибка связи, кроме авторизации; AuthenticationException — отказ авторизации; InvalidArgumentException — неверные аргументы/опции. Остальные отказы команды или запроса относятся к RuntimeException и её специализированным подклассам. Сетевой отказ не является пустым успешным результатом. При активной транзакции session нельзя отдельно передавать readConcern/writeConcern в опциях операции: они задаются при старте транзакции, иначе InvalidArgumentException. Session с неподтверждаемой записью также недопустима там, где операция пишет.

Версии

С mongodb1.0.0; массив options с1.4.0. Передача отдельного объекта вместо options объявлена устаревшей в1.21.0 и удалена в2.0.0. Ранее third-argument ReadPreference; в1.4.4 уточнён запрет session+w=0.

Два практических применения

Проверить ответ выбранного узла

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

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Разобрать границу API диагностики

buildInfo с обычным клиентом работает, со strict-клиентом даёт323. Ошибку не скрывает автоматический fallback.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Операция остаётся на этом Server. readPreference в допустимых опциях влияет на протокол secondary/mongos, но не выбирает другой узел. При записи вызывающий код должен выбрать сервер, способный писать; обычная запись в secondary не разрешена. Выбирайте специализированный read/write метод, когда известен характер команды.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::executeReadCommandВыполнить команду чтения и получить её ограниченный результат.

mongodb1.4.0 · mongodb

final public function MongoDB\Driver\Server::executeReadCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor

Выполнить команду чтения и получить её ограниченный результат.

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

final public function MongoDB\Driver\Server::executeReadCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor
Вход / часть APIСмысл
dbИмя базы, не полный namespace
commandMongoDB\Driver\Command с документом команды
options.sessionSession операции
options.readConcernReadConcern (поддержка сервером3.2+; более старый сервер откажет)
options.readPreferenceReadPreference; не перевыбирает Server

Что получает вызывающий код

Cursor результата команды; форма документов зависит от самой команды. Применяет правила чтения; readPreference и readConcern по умолчанию берутся из активной транзакции, затем URI.

Ошибки и ограничения вызова

ConnectionException — ошибка связи, кроме авторизации; AuthenticationException — отказ авторизации; InvalidArgumentException — неверные аргументы/опции. Остальные отказы команды или запроса относятся к RuntimeException и её специализированным подклассам. Сетевой отказ не является пустым успешным результатом. При активной транзакции session нельзя отдельно передавать readConcern/writeConcern в опциях операции: они задаются при старте транзакции, иначе InvalidArgumentException. Session с неподтверждаемой записью также недопустима там, где операция пишет.

Версии

С mongodb1.4.0. Для write/readwrite в1.4.4 уточнён отказ session вместе с неподтверждаемой записью. Используйте современные массивы options.

Два практических применения

Получить сумму выбранного магазина

aggregate исключает черновик и чужой магазин до группировки:2заказа,350.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

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

listIndexes возвращает фактический sku_unique с unique=true; это подтверждение, что подготовка действительно создала индекс.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Операция остаётся на этом Server. readPreference в допустимых опциях влияет на протокол secondary/mongos, но не выбирает другой узел. При записи вызывающий код должен выбрать сервер, способный писать; обычная запись в secondary не разрешена. Команда чтения не гарантирует согласованный снимок нескольких последующих операций.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::executeReadWriteCommandПрочитать исходные данные и сохранить результат командой.

mongodb1.4.0 · mongodb

final public function MongoDB\Driver\Server::executeReadWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor

Прочитать исходные данные и сохранить результат командой.

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

final public function MongoDB\Driver\Server::executeReadWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor
Вход / часть APIСмысл
dbИмя базы, не полный namespace
commandMongoDB\Driver\Command с документом команды
options.sessionSession операции
options.readConcernReadConcern (поддержка сервером3.2+; более старый сервер откажет)
options.writeConcernWriteConcern подтверждения записи

Что получает вызывающий код

Cursor результата команды; форма документов зависит от самой команды. Применяет правила команды чтения с записью; readConcern/writeConcern по умолчанию определяются активной транзакцией, затем URI.

Ошибки и ограничения вызова

ConnectionException — ошибка связи, кроме авторизации; AuthenticationException — отказ авторизации; InvalidArgumentException — неверные аргументы/опции. Остальные отказы команды или запроса относятся к RuntimeException и её специализированным подклассам. Сетевой отказ не является пустым успешным результатом. При активной транзакции session нельзя отдельно передавать readConcern/writeConcern в опциях операции: они задаются при старте транзакции, иначе InvalidArgumentException. Session с неподтверждаемой записью также недопустима там, где операция пишет.

Версии

С mongodb1.4.0. Для write/readwrite в1.4.4 уточнён отказ session вместе с неподтверждаемой записью. Используйте современные массивы options.

Два практических применения

Сохранить вычисленный отчёт

aggregate+$merge создаёт запись A с2заказами и350; отдельное чтение показывает её.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Обновить отчёт после изменения заказа

Повторная команда заменяет запись A на400 без дубликата; отсутствующие группы сами не удаляются.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Операция остаётся на этом Server. readPreference в допустимых опциях влияет на протокол secondary/mongos, но не выбирает другой узел. При записи вызывающий код должен выбрать сервер, способный писать; обычная запись в secondary не разрешена. Пустой Cursor после $merge означает отсутствие возвращаемых документов; сохранённый результат читается отдельно.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\Server::executeWriteCommandВыполнить команду изменения структуры или состояния базы.

mongodb1.4.0 · mongodb

final public function MongoDB\Driver\Server::executeWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor

Выполнить команду изменения структуры или состояния базы.

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

final public function MongoDB\Driver\Server::executeWriteCommand(string $db, MongoDB\Driver\Command $command, ?array $options = null): MongoDB\Driver\Cursor
Вход / часть APIСмысл
dbИмя базы, не полный namespace
commandMongoDB\Driver\Command с документом команды
options.sessionSession операции
options.writeConcernWriteConcern подтверждения записи

Что получает вызывающий код

Cursor результата команды; форма документов зависит от самой команды. Применяет правила записи; writeConcern по умолчанию определяется активной транзакцией, затем URI.

Ошибки и ограничения вызова

ConnectionException — ошибка связи, кроме авторизации; AuthenticationException — отказ авторизации; InvalidArgumentException — неверные аргументы/опции. Остальные отказы команды или запроса относятся к RuntimeException и её специализированным подклассам. Сетевой отказ не является пустым успешным результатом. При активной транзакции session нельзя отдельно передавать readConcern/writeConcern в опциях операции: они задаются при старте транзакции, иначе InvalidArgumentException. Session с неподтверждаемой записью также недопустима там, где операция пишет.

Версии

С mongodb1.4.0. Для write/readwrite в1.4.4 уточнён отказ session вместе с неподтверждаемой записью. Используйте современные массивы options.

Два практических применения

Подготовить уникальный артикул

createIndexes задаёт sku_unique; реальная попытка дубля получает11000.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

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

Второе создание того же определения успешно, индекс и два исходных товара сохраняются.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Операция остаётся на этом Server. readPreference в допустимых опциях влияет на протокол secondary/mongos, но не выбирает другой узел. При записи вызывающий код должен выбрать сервер, способный писать; обычная запись в secondary не разрешена. Для insert/update/delete используйте executeBulkWrite; этот command-метод для них не предназначен.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\ServerApiОбъявить стабильный договор команд приложения при создании Manager.

mongodb1.10.0 · mongodb

final class MongoDB\Driver\ServerApi implements MongoDB\BSON\Serializable {
    public const V1 = '1';
    final public function __construct(string $version, ?bool $strict = null, ?bool $deprecationErrors = null);
    final public function bsonSerialize(): stdClass;
}

Объявить стабильный договор команд приложения при создании Manager.

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

final class MongoDB\Driver\ServerApi implements MongoDB\BSON\Serializable {
    public const V1 = '1';
    final public function __construct(string $version, ?bool $strict = null, ?bool $deprecationErrors = null);
    final public function bsonSerialize(): stdClass;
}
Вход / часть APIСмысл
V1Строка1; единственная поддерживаемая версия API в текущем драйвере
НазначениеОбъект передаётся как driverOptions.serverApi в Manager
Текущий интерфейсMongoDB\BSON\Serializable; старый PHP Serializable в synopsis PHP.net не соответствует драйверу2.5.5

Что получает вызывающий код

Объект настроек API, который применяется Manager к операциям. Это не подключение и не версия сервера. bsonSerialize даёт представление настроек; полезный эффект подтверждается реальным разрешённым запросом и ожидаемым strict-отказом.

Ошибки и ограничения вызова

Неподдерживаемая версия вызывает InvalidArgumentException в проверенном2.5.5. Серверные ошибки совместимости приходят при выполнении операции; конструктор не проверяет сеть и доступность базы.

Версии

Класс с mongodb1.10.0; Stable API введён MongoDB5.0. Сверенная текущая форма2.5.5 подтверждена официальными tagged stub/source. Дополнительные PHP-механизмы __serialize/__unserialize/__set_state видны в stub, но не заменяют настройку Manager или BSON-контракт.

Два практических применения

Проверить фактическую конфигурацию

Три формы показывают различие null/false/true; strict-клиент читает MUG.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Найти команду за пределами договора

find/count проходят, buildInfo отклоняется323, обычная диагностика выделена отдельно.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Гарантия касается поддерживаемого набора команд/опций и совместимого драйвера. Она не фиксирует вашу схему, индексы, производительность или доступность. Команды вне API требуют отдельного решения, а не молчаливого отключения strict.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\ServerApi::__constructСоздать настройки версии API, strict и deprecationErrors.

mongodb1.10.0 · mongodb

final public function MongoDB\Driver\ServerApi::__construct(string $version, ?bool $strict = null, ?bool $deprecationErrors = null)

Создать настройки версии API, strict и deprecationErrors.

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

final public function MongoDB\Driver\ServerApi::__construct(string $version, ?bool $strict = null, ?bool $deprecationErrors = null)
Вход / часть APIСмысл
versionПоддерживаемая версия; используйте ServerApi::V1
strict=nullНе задан; серверный default false. true запрещает команды/возможности вне версии API
deprecationErrors=nullНе задан; серверный default false. true отклоняет deprecated возможности именно данного API
Передача в ManagerТретий аргумент: ["serverApi" => $api]; не произвольный ключ Query

Что получает вызывающий код

new возвращает ServerApi. Сам конструктор не отправляет команду. Только Manager, созданный с этим объектом, использует настройки для последующих операций.

Ошибки и ограничения вызова

В2.5.5 неизвестная версия вроде2 даёт InvalidArgumentException; неверные PHP-типы/разбор аргументов также отклоняются. Ошибка строгой серверной команды возникает позже и не означает отказ конструктора.

Версии

С mongodb1.10.0, серверная Stable API MongoDB5.0+. Nullable-флаги отличаются от явно переданного false в сериализованном описании.

Два практических применения

Задать и проверить осознанные defaults

V1 без флагов оставляет их незаданными, V1,false,false явно сохраняетfalse; вывод показывает обе формы.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Ограничить приложение стабильными командами

V1,true,true разрешает чтение товара и намеренно отказывает диагностическому buildInfo.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

deprecationErrors не лечит PHP E_DEPRECATED и несовместимость Composer-пакета с расширением. strict не является режимом авторизации или валидации схемы документов.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

MongoDB\Driver\ServerApi::bsonSerializeПолучить документ настроек и различить пропуск поля от false.

mongodb1.10.0 · mongodb

final public function MongoDB\Driver\ServerApi::bsonSerialize(): stdClass

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

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

final public function MongoDB\Driver\ServerApi::bsonSerialize(): stdClass

Параметров нет.

Что получает вызывающий код

stdClass для BSON-сериализации: version и только заданные strict/deprecationErrors. В2.5.5 незаданные/null-поля отсутствуют, false сохраняется. Это PHP-объект, не JSON-текст; json_encode отдельно даёт понятную диагностическую строку.

Ошибки и ограничения вызова

Параметров нет; официальная страница указывает InvalidArgumentException при ошибке разбора аргументов. Ошибки json_encode и выполнения команд имеют собственный контракт.

Версии

С mongodb1.10.0. Точная структура текущей версии сверена tagged исходником2.5.5 и реальным probe. Названия version/strict/deprecationErrors не совпадают с wire-полями apiVersion/apiStrict/apiDeprecationErrors.

Два практических применения

Выявить настройку, оставленную по умолчанию

В первом документе только version:1, поэтому unset и false можно различить при диагностике.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Подтвердить явно включённый строгий договор

Третий документ содержит обаtrue; тот же объект подключён к Manager и используется для реального чтения MUG.

Короткий вызов, исходные данные, полная программа и результат · Таблица реального запуска

Границы и продолжение

Не добавляйте результат напрямую к произвольной команде и не считайте его удалённым отчётом сервера. Это представление локального объекта настроек. Для применения передайте ServerApi в Manager.

Выбор узла и диагностика Server · Manager, Query, BulkWrite · Чтение Cursor · Общая атомарность и транзакции.

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

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

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

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

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