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

Дерево руководства
Глава 181 · После первого запроса и вставки заказа
MongoDB: соединение, таймауты и восстановление после ошибки записи
Реальные ситуации: неподходящий узел, отказ входа, несовместимый TLS, серверный maxTimeMS и разбор частичных результатов двух видов записи.
Что можно сказать о заказе после исключения
Сотрудник отправил три заказа и увидел ошибку. Снова нажимать «Импорт» рискованно: первая строка могла сохраниться до сбоя второй. В этой главе получим три разные отметки в одной ведомости и увидим, как определить судьбу каждой записи. Посмотреть экран для трёх заказов.
- Во входном файле R-301, R-302 и R-303
- MongoDB подтверждает R-301, затем сообщает о конфликте R-302
- Обработчик читает позиции ошибок и сохранённые документы
- Ведомость: «Уже сохранён», «Разобрать конфликт», «К отдельной отправке»
Исключение — объект PHP, которым драйвер сообщает, что вызов не смог завершиться обычным результатом. Для первой ступени рассмотрим только чтение. Если оно не получило соединение, не стоит возвращать экрану число заказов, которое запрос так и не посчитал.
try {
$order = $orders->findOne(['_id' => 'R-101']);
} catch (MongoDB\Driver\Exception\ConnectionException $failure) {
echo 'Карточку сейчас не удалось загрузить';
}Блок try содержит обращение к базе; если оно выбросит указанный тип или его подкласс, управление перейдёт в catch. Успешный поиск отсутствующей записи завершится иначе: findOne вернёт null. Фраза в catch относится к доступности карточки. Наличие заказа и результат записи выясняются отдельно. Основы разбираются в уроке о try и catch.
Четыре этапа, на которых возможен сбой
| Где возникла проблема | Что есть у приложения | Следующая проверка |
|---|---|---|
| Выбор узла или установление соединения | Конкретный класс ConnectionException, конфигурация операции | Какая роль узла нужна, ожидается ли TLS и что заведомо известно о стенде? |
| Вход приложения в MongoDB | AuthenticationException | Проверить профиль сервиса и authSource через закрытый административный канал. |
| На сервере | Серверный код и конкретный тип, например ExecutionTimeoutException | Какая команда выполнялась, какой лимит применён и что сохранилось? |
| В пакете записей | Код элемента, его индекс, возможно частичная квитанция | Сопоставить с исходной строкой и проверить ID и существенные поля в базе. |
Ограничения времени задаются разным участникам. connectTimeoutMS ограничивает установление соединения, serverSelectionTimeoutMS — выбор подходящего узла. socketTimeoutMS относится к сетевому обмену, maxTimeMS исполняется сервером для конкретной команды. Сходные пользовательские слова «не дождались ответа» не делают эти ситуации взаимозаменяемыми.
Порядок catch влияет на результат: обработчик подходящего родительского класса поймает также его подкласс. Если для входа в базу предусмотрено отдельное действие, поставьте AuthenticationException перед ConnectionException. Если у конкретного вида записи есть подробная квитанция, разберите её до общей ветви ServerException.
Как устроен воспроизводимый стенд
Программы предназначены для отдельной одноузловой реплики phpmanRs (PHP CLI 8.5.11, ext-mongodb 2.5.5, PHP-библиотека 2.5.0, MongoDB 8.0.34). В архиве каждой есть README.txt с настройкой нового локального стенда и recovery-lab.php с общим подключением. Если реплика уже запущена, не создавайте её повторно.
composer require mongodb/mongodb:2.5.0
$env:PHPMAN_MONGODB_URI='mongodb://127.0.0.1:27076/?replicaSet=phpmanRs&retryWrites=false'
$env:PHPMAN_MONGODB_DATABASE='php_manual_txn_lab'
php example.phpТерминал выдаст итог предметной задачи. Откройте result.html, чтобы увидеть состояние заказа и отдельную раскрываемую запись инженера. result.json содержит значения той же таблицы, metrics.json хранит проверки и счётчики коллекции. Все номера и товары учебные. Для каждого запуска создаётся, удаляется и проверяется одна собственная случайно названная коллекция.
В отрицательном примере аутентификации клиент делает одну локальную попытку. Создания пользователя в MongoDB нет. Время ожидания ограничено. Секреты, идентификатор входа, URI запросов, сырые ошибки и трассы не попадают в публичный результат. Проверки TLS выполняются против известного учебного endpoint. В опыте блокировки оба клиента живут в одном CLI-процессе и используют независимые соединения.
Отличить недоступный маршрут от пустой карточки
На странице заказа появился таймаут. Прежде чем показывать «Заказ не найден», выясним, успел ли запрос дойти до узла, который может его выполнить.
Исходные данные
В наборе один primary, secondary отсутствуют. Создаём реально существующий R-101 и запрашиваем его у строго вторичного узла. У клиента ограничено время выбора.
- R-101 записан на primary
- Клиент ищет secondary → подходящего узла нет
- Исключение в выборе сервера; контрольное чтение с primary → Готов
Короткий фрагмент
$route=new ReadPreference(ReadPreference::SECONDARY);
$cursor=$manager->executeQuery($namespace, new Query(['_id'=>'R-101']), ['readPreference'=>$route]);Что увидит сотрудник
Карточку R-101 не удалось загрузить с выбранного узла
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Что проверили | Наблюдение |
|---|---|
| Предписанный маршрут | secondary |
| Выбор узла | Подходящий узел отсутствует |
| При отдельном чтении с primary | Готов |
Разбор результата
Успешный find с пустым ответом и ошибка выбора сервера — разные наблюдения. Здесь PHP не получил курсор с результатами из запрошенного маршрута. Родитель ConnectionException помогает сгруппировать сбои соединения; конкретный класс и ограничение топологии объясняют этот случай.
Границы примера
Контрольное чтение выполняет учебная программа. В рабочем сервисе переход к другому маршруту допустим, только если это разрешает политика чтения. Равенство таймера настройке не проверяется: на длительность влияют драйвер и топология.
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-no-secondary', 'Отличить недоступный маршрут от пустой карточки', ['Что проверили', 'Наблюдение'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-101','status'=>'Готов']);
$manager=freshManager($uri,['serverSelectionTimeoutMS'=>450]);
try {
$manager->executeQuery($dbName.'.'.$name,new Query(['_id'=>'R-101'],['limit'=>1]),
['readPreference'=>new ReadPreference(ReadPreference::SECONDARY)])->toArray();
throw new RuntimeException('Expected no eligible secondary');
} catch (ConnectionTimeoutException $error) {
$info=diagnostic($error,'Выбор узла для чтения');
$checks['connection_family']=$error instanceof ConnectionException;
$checks['not_execution_timeout']=!($error instanceof ExecutionTimeoutException);
}
$actual=$orders->findOne(['_id'=>'R-101'],['projection'=>['_id'=>1,'status'=>1]]);
$checks['status_from_primary']=$actual['status']==='Готов';
$checks['order_present']=$actual['_id']==='R-101';
$checks['reported_type']=$info['class']===ConnectionTimeoutException::class;
return [[['Предписанный маршрут','secondary'],['Выбор узла','Подходящий узел отсутствует'],
['При отдельном чтении с primary',$actual['status']]],$checks,'Карточку R-101 не удалось загрузить с выбранного узла',$info];
}
);
Ожидаемый результат
Карточку R-101 не удалось загрузить с выбранного узла
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
Поставьте secondaryPreferred и проверьте документ на этом одноузловом наборе. Для этого варианта ветка исключения должна замениться обычной обработкой результата.
Отделить вход приложения в базу от состояния заказа
Кнопка «Подготовить» открыта авторизованному сотруднику, но приложение не смогло пройти аутентификацию в MongoDB. Просить сотрудника повторно ввести пароль от сайта здесь бессмысленно.
Исходные данные
Черновик R-111 сначала сохраняется рабочим клиентом. Другой клиент использует отдельное случайное имя и пароль для одной учебной попытки к loopback. База не получает команд создания пользователя или изменения ролей.
- Подготовленная команда update → соединение с учётными данными приложения
- AuthenticationException при входе → рабочая команда не прошла
- Доверенный клиент читает R-111 → Черновик
Короткий фрагмент
try {
$manager->executeWriteCommand($dbName,$update);
} catch (AuthenticationException $error) {
$publicMessage='Статус не удалось обновить';
}Что увидит сотрудник
Статус R-111 не обновлён
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Наблюдение | Данные |
|---|---|
| Запрос клиента | Перевести в готовые |
| После отказа | Черновик |
| Следующий шаг | Проверить профиль доступа приложения |
Разбор результата
authSource указывает, в какой базе проверяется учётная запись приложения. Отказ аутентификации отличается от запрета команды уже установленной личности. Сохранённый статус подтверждается отдельным чтением. Для пользователя достаточно результата бизнес-действия; класс и этап сохраняются в технической части.
Границы примера
Стенд не проверяет полный вход зарегистрированного пользователя с ролями и не переводит рабочую базу в режим с авторизацией. Код ошибки подключения может зависеть от версии. Для маршрутизации здесь используется тип исключения, а не совпадение текста сообщения.
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-auth-rejected', 'Отделить вход приложения в базу от состояния заказа', ['Наблюдение', 'Данные'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-111','status'=>'Черновик']);
$testManager=freshManager($uri,[
'authSource'=>$dbName,'username'=>'unregistered_'.bin2hex(random_bytes(16)),
'password'=>bin2hex(random_bytes(20)), 'serverSelectionTimeoutMS'=>800,
]);
try {
$testManager->executeWriteCommand($dbName,new Command(['update'=>$name,
'updates'=>[['q'=>['_id'=>'R-111'],'u'=>['$set'=>['status'=>'Готов']]]]]))->toArray();
throw new RuntimeException('Expected authentication failure');
} catch (AuthenticationException $error) {
$info=diagnostic($error,'Аутентификация приложения');
$checks['connection_family']=$error instanceof ConnectionException;
$checks['specific_auth']=$info['class']===AuthenticationException::class;
}
$actual=$orders->findOne(['_id'=>'R-111'],['projection'=>['_id'=>1,'status'=>1]]);
$checks['draft_remains']=$actual['status']==='Черновик';
$checks['one_order']=$orders->countDocuments([])===1;
$checks['safe_diagnostic_keys']=array_keys($info)===['class','code','stage'];
return [[['Запрос клиента','Перевести в готовые'],['После отказа',$actual['status']],
['Следующий шаг','Проверить профиль доступа приложения']],$checks,'Статус R-111 не обновлён',$info];
}
);
Ожидаемый результат
Статус R-111 не обновлён
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
Разделите обработку в своём приложении на «пользователь сайта не вошёл» и «сервису недоступна база». У этих состояний должны быть разные следующие действия для человека.
Как отличить учётную запись доступа приложения к БД от пользователя формы? Получить типизированный отказ при случайном несуществующем имени, не раскрывая пароль и параметры подключения. Исходная постановка на Stack Overflow.
Прочитать старый обработчик SSL и распознать современный сбой
Старое приложение ловит SSLConnectionException. При настоящем сбое TLS такой обработчик не срабатывает. Проверим, на какой современный тип попадает клиент.
Исходные данные
Учебный endpoint заведомо работает без TLS. Обычный клиент записал контрольный R-121. Независимый клиент ожидает TLS на том же локальном порту и пытается выполнить только ping.
- Клиент запрашивает TLS → endpoint не может завершить соединение
- Истекает выбор узла → реальный ConnectionTimeoutException
- Современный catch ConnectionException срабатывает; историческое имя отсутствует
Короткий фрагмент
try {
$manager->executeCommand($dbName,new Command(['ping'=>1]));
} catch (ConnectionException $error) {
$stage='Установление соединения';
}Что увидит сотрудник
Канал проверки R-121 недоступен
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Наблюдение | Результат |
|---|---|
| Старый класс в 2.5.5 | false |
| Обработчик ветви | ConnectionException |
| Фактическое состояние R-121 | Ожидает проверки |
Разбор результата
Расширение никогда не выбрасывало старый SSLConnectionException; с 2.0 самого класса уже нет. На конкретном стенде неверная конфигурация канала проявилась через таймаут выбора. Одно имя текущего исключения не доказывает, что любой аналогичный отказ вызван TLS.
Границы примера
Этот опыт не имитирует просроченный сертификат или сбой конкретного TLS-сертификата. Для рабочего сервиса надо сверить ожидаемые настройки TLS, имя хоста и доверенные центры сертификации. Понижать защищённое соединение ради повторной отправки заказа не следует.
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-tls-mismatch', 'Прочитать старый обработчик SSL и распознать современный сбой', ['Наблюдение', 'Результат'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-121','stage'=>'Ожидает проверки']);
$tlsClient=freshManager($uri,['tls'=>true,'connectTimeoutMS'=>300,'serverSelectionTimeoutMS'=>500]);
try {
$tlsClient->executeCommand($dbName,new Command(['ping'=>1]))->toArray();
throw new RuntimeException('Expected incompatible TLS endpoint');
} catch (ConnectionException $error) {
$info=diagnostic($error,'Соединение с ожидаемым TLS');
$checks['concrete_timeout']=$error instanceof ConnectionTimeoutException;
$checks['modern_parent']=$error instanceof ConnectionException;
}
$legacy=class_exists('MongoDB\\Driver\\Exception\\SSLConnectionException');
$actual=$orders->findOne(['_id'=>'R-121'],['projection'=>['_id'=>0,'stage'=>1]]);
$checks['old_class_absent']=!$legacy;
$checks['saved_state']=$actual===['stage'=>'Ожидает проверки'];
$checks['type_recorded']=$info['class']===ConnectionTimeoutException::class;
return [[['Старый класс в 2.5.5',$legacy],['Обработчик ветви','ConnectionException'],
['Фактическое состояние R-121',$actual['stage']]],$checks,'Канал проверки R-121 недоступен',$info];
}
);
Ожидаемый результат
Канал проверки R-121 недоступен
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
Сравните эту строку диагностики с выбором secondary. Совпадающий класс ошибки имеет разные объяснения, потому что режим и окружение различны.
Почему сбой на защищённом соединении PHP-драйвер показывает как ошибку выбора сервера? Разделить этап установления соединения и исполнение команды, сравнить фактический класс со старым названием. Исходная постановка на Stack Overflow.
Сверить количество после серверного таймаута
Две независимые клиентские связи внутри одного CLI-процесса обращаются к одному резерву. Первый клиент уже изменил его в открытой транзакции. У второго ограничено время исполнения команды. В рабочем приложении такие обращения могут прийти из разных процессов.
Исходные данные
Исходный резерв — 4. В первой сессии незавершённый $inc делает его равным 5 для этой сессии. Отдельное соединение пробует установить 9 с лимитом maxTimeMS=150.
- Транзакция удерживает запись → внутри неё видно 5
- Серверный update другого клиента не успел → ExecutionTimeoutException, код 50
- Отменяем первую транзакцию → новое чтение показывает 4
Короткий фрагмент
$update=new Command(['update'=>$name,'updates'=>[
['q'=>['_id'=>'R-131'], 'u'=>['$set'=>['reserved'=>9]]],
], 'maxTimeMS'=>150]);
$other->executeWriteCommand($dbName,$update);Что увидит сотрудник
Резерв R-131 после проверки · 4
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Момент | Количество или код |
|---|---|
| Начальное значение | 4 |
| В первой транзакции | 5 |
| Код конкурирующего update | 50 |
| Независимое чтение после abort | 4 |
Разбор результата
Код 50 у этой команды относится к сроку выполнения на сервере. Сервер уже был выбран. socketTimeoutMS установлен заметно больше серверного лимита, чтобы учебный результат отражал именно серверный отказ. abort отменил только изменения первой транзакции.
Границы примера
Один таймаут не даёт универсальной гарантии о состоянии любой записи. Здесь результат удостоверен новым чтением одной известной записи после завершения обеих операций. В другом процессе без собственной контрольной точки нельзя выводить из кода 50 число «4».
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-locked-update', 'Сверить количество после серверного таймаута', ['Момент', 'Количество или код'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-131','reserved'=>4]);
$session=$client->startSession();
try {
$session->startTransaction();
try {
$modified=$orders->updateOne(['_id'=>'R-131'],['$inc'=>['reserved'=>1]],['session'=>$session]);
$inside=$orders->findOne(['_id'=>'R-131'],['session'=>$session])['reserved'];
$contender=freshManager($uri,['socketTimeoutMS'=>3500]);
try {
$update=new Command(['update'=>$name,'updates'=>[['q'=>['_id'=>'R-131'],'u'=>['$set'=>['reserved'=>9]]]],'maxTimeMS'=>150]);
$contender->executeWriteCommand($dbName,$update)->toArray();
throw new RuntimeException('Expected execution timeout');
} catch (ExecutionTimeoutException $error) {
$info=diagnostic($error,'Исполнение update на сервере');
$checks['server_limit_50']=$info['code']===50;
$checks['server_family']=$error instanceof ServerException;
}
} finally {
$session->abortTransaction();
}
} finally {
$session->endSession();
}
$after=$orders->findOne(['_id'=>'R-131'])['reserved'];
$checks['holder_modified_one']=$modified->getModifiedCount()===1;
$checks['uncommitted_five']=$inside===5;
$checks['after_abort_four']=$after===4;
$checks['not_competing_value']=$after!==9;
return [[['Начальное значение',4],['В первой транзакции',$inside],['Код конкурирующего update',$info['code']],
['Независимое чтение после abort',$after]],$checks,'Резерв R-131 после проверки · '.$after,$info];
}
);
Ожидаемый результат
Резерв R-131 после проверки · 4
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
Сохраните в форме старое количество или версию, а после отказа снова прочитайте документ. Решение о новой записи можно принимать только с учётом прочитанного состояния, чтобы не затереть чужую правку.
Отделить ошибку команды от потери данных
Внутренняя проверка заказа навсегда застрянет, если приложение будет снова отправлять команду с неверным именем. Надо распознать отказ сервера и оставить понятную запись для поддержки.
Исходные данные
R-141 существует. Отправляется диагностический вызов с вымышленным именем manualRecoveryUnknown134. Таким вызовом приложение проверяет поведение сервера при неизвестной команде.
- Соединение установлено → сервер разбирает имя команды
- Ответ CommandNotFound, код 59 → конкретный CommandException
- catch ServerException видит серверный отказ; R-141 читается обычным путём
Короткий фрагмент
try {
$manager->executeReadCommand($dbName,$command);
} catch (ServerException $error) {
$code=$error->getCode();
}Что увидит сотрудник
Проверка не прошла · R-141 остаётся подготовленным
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Что известно | Значение |
|---|---|
| Код ответа | 59 |
| Состояние после проверки | Подготовлен |
| Дальше | Исправить команду |
Разбор результата
Тип родителя позволяет обработать группу серверных ошибок. Код 59 и фактический класс сузили причину до команды. Возврат к такой же команде без исправления вновь повторит ту же проблему.
Границы примера
Контрольный заказ не был целью записывающей операции этого теста. Наблюдение «Подготовлен» не превращает все ServerException в гарантию отсутствия изменений. Для частичных записей ниже разбирается другой способ проверки.
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-unknown-command', 'Отделить ошибку команды от потери данных', ['Что известно', 'Значение'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-141','state'=>'Подготовлен']);
try {
$client->getManager()->executeReadCommand($dbName,new Command(['manualRecoveryUnknown134'=>1]))->toArray();
throw new RuntimeException('Expected unknown command');
} catch (ServerException $error) {
$info=diagnostic($error,'Проверка команды сервером');
$checks['actual_command_exception']=$error instanceof MongoDB\Driver\Exception\CommandException;
$checks['server_code_59']=$info['code']===59;
$checks['not_connectivity']=!($error instanceof ConnectionException);
}
$actual=$orders->findOne(['_id'=>'R-141']);
$checks['control_state']=$actual['state']==='Подготовлен';
$checks['one_record']=$orders->countDocuments([])===1;
return [[['Код ответа',$info['code']],['Состояние после проверки',$actual['state']],
['Дальше','Исправить команду']],$checks,'Проверка не прошла · R-141 остаётся подготовленным',$info];
}
);
Ожидаемый результат
Проверка не прошла · R-141 остаётся подготовленным
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
В интерфейсе разделите действие «исправить конфигурацию команды» и действие «обновить страницу». Используйте рассчитанный этап и код, а не строковый разбор сырого текста MongoDB.
Узнать судьбу каждой строки прерванного импорта
Ведомость из трёх заказов оборвалась на второй строке. Сотруднику важно узнать, какая строка уже в базе, какая конфликтует и что ещё допустимо отправить отдельно.
Исходные данные
Заранее сохранён R-302 с товаром «Штатив». Три операции в ordered-пакете имеют позиции 0, 1 и 2. Позиция 1 пытается занять тот же ключ товаром «Кабель».
- Вставлена позиция 0 → R-301
- Позиция 1 → duplicate 11000; дальнейшая обработка ordered-пакета остановлена
- Читаем три известных номера → сохранён, конфликт, отсутствует
Короткий фрагмент
try {
$manager->executeBulkWriteCommand($batch);
} catch (BulkWriteCommandException $error) {
$partial=$error->getPartialResult();
$byIndex=$error->getWriteErrors();
}Что увидит сотрудник
Импорт остановлен на R-302 · сохранён R-301, R-303 ожидает решения
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Номер | После чтения | Решение |
|---|---|---|
| R-301 | Лампа | Уже сохранён |
| R-302 | Штатив | Разобрать конфликт |
| R-303 | Нет записи | К отдельной отправке |
Разбор результата
PartialResult сообщил об одной успешной вставке. Индекс из getWriteErrors указал, к какой строке относится duplicate. Контрольное чтение показало «Штатив» в старой записи. Третьей записи нет. Фраза «К отдельной отправке» предлагает сотруднику подготовить отдельное действие, проверив ключ и намерение.
Границы примера
Один лишь совпадающий _id не доказывает, что ранее выполнена именно эта бизнес-операция. Сравнивайте важные поля и, где требуется, отдельный идентификатор операции. Не отправляйте все три вставки повторно. Программа выполняет пакет один раз.
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-ordered-import', 'Узнать судьбу каждой строки прерванного импорта', ['Номер', 'После чтения', 'Решение'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-302','item'=>'Штатив']);
$input=[['_id'=>'R-301','item'=>'Лампа'],['_id'=>'R-302','item'=>'Кабель'],['_id'=>'R-303','item'=>'Чехол']];
$error=packetFailure($client->getManager(),$dbName.'.'.$name,$input,true);
$info=diagnostic($error,'Упорядоченная пакетная запись');
$partial=$error->getPartialResult();
$writeErrors=$error->getWriteErrors();
if ($partial===null || !isset($writeErrors[1])) { throw new UnexpectedValueException('Unexpected partial receipt'); }
$rows=[];
foreach ($input as $position=>$expected) {
$saved=$orders->findOne(['_id'=>$expected['_id']],['projection'=>['_id'=>1,'item'=>1]]);
$action=match(true) {
isset($writeErrors[$position]) => 'Разобрать конфликт',
$saved===null => 'К отдельной отправке',
$saved['item']===$expected['item'] => 'Уже сохранён',
default => 'Разобрать расхождение',
};
$rows[]=[$expected['_id'],$saved['item']??'Нет записи',$action];
}
$checks['one_confirmed_insert']=$partial->getInsertedCount()===1;
$checks['duplicate_index_one']=array_keys($writeErrors)===[1] && $writeErrors[1]->getCode()===11000;
$checks['no_concern_errors']=$error->getWriteConcernErrors()===[];
$checks['only_item_error']=$error->getErrorReply()===null;
$checks['requested_content_preserved']=$rows[0][1]==='Лампа' && $rows[1][1]==='Штатив';
$checks['remaining_absent']=$rows[2][1]==='Нет записи';
$checks['actual_count_two']=$orders->countDocuments([])===2;
return [$rows,$checks,'Импорт остановлен на R-302 · сохранён R-301, R-303 ожидает решения',$info];
}
);
Ожидаемый результат
Импорт остановлен на R-302 · сохранён R-301, R-303 ожидает решения
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
Поменяйте товар второй строки на «Штатив». Ошибка уникальности сохранится. Каких доказательств вашему сервису хватит, чтобы при сверке признать строку уже принятой? Обсудите это правило до автоматического повтора.
Что стало с записями до и после неудачной строки? Сопоставить индекс ошибки, частичный ответ и фактические документы с каждой исходной строкой. Исходная постановка на Stack Overflow.
Сверить независимые позиции после ошибки в середине
Три независимых импортируемых заказа можно обработать независимо. Если середина уже была принята предыдущим запуском, выясним, сохранился ли последний заказ.
Исходные данные
Сохранённый R-312 содержит стабильный идентификатор операции pack-312 и тот же товар. Этот раз формат пакета допускает обработку остальных элементов после ошибки отдельной вставки.
- Позиции 0 и 2 записаны → число успешных вставок 2
- Индекс ошибки 1 отмечает дубликат
- Сверяем по _id, ожидаемому operation и товару → три совпавших намерения
Короткий фрагмент
$batch = new MongoDB\Driver\BulkWriteCommand(['ordered'=>false]);
foreach ($commands as $record) {
$batch->insertOne($namespace,$record);
}Что увидит сотрудник
Все три намерения найдены · новых вставок 2
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Номер | Ответ операции | Сверка с базой |
|---|---|---|
| R-311 | Вставлен | Намерение подтверждено |
| R-312 | Дубликат | Намерение подтверждено |
| R-313 | Вставлен | Намерение подтверждено |
Разбор результата
В режиме ordered=false порядок фактической работы не следует выводить из порядка таблицы. Индексы ошибок относятся к исходным позициям. Приложение заново строит экран в порядке исходного файла. Ранее принятый заказ в этом пакете не записался, но его данные и идентификатор операции уже совпадают.
Границы примера
Пример относится к независимым вставкам с ошибкой отдельного элемента. Ошибки верхнего уровня, отсутствие частичной квитанции, недоступное соединение и проблемы подтверждения требуют отдельного разбора. Нельзя использовать unordered как замену транзакции там, где все изменения должны быть неделимыми.
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-unordered-import', 'Сверить независимые позиции после ошибки в середине', ['Номер', 'Ответ операции', 'Сверка с базой'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-312','item'=>'Кабель','operation'=>'pack-312']);
$input=[['_id'=>'R-311','item'=>'Чехол','operation'=>'pack-311'],['_id'=>'R-312','item'=>'Кабель','operation'=>'pack-312'],
['_id'=>'R-313','item'=>'Лампа','operation'=>'pack-313']];
$error=packetFailure($client->getManager(),$dbName.'.'.$name,$input,false);
$info=diagnostic($error,'Неупорядоченная пакетная запись');
$partial=$error->getPartialResult();
$errors=$error->getWriteErrors();
if ($partial===null || !isset($errors[1])) { throw new UnexpectedValueException('Missing unordered receipt'); }
$rows=[];
foreach ($input as $i=>$original) {
$saved=$orders->findOne(['_id'=>$original['_id']]);
$same=$saved!==null && $saved['operation']===$original['operation'] && $saved['item']===$original['item'];
$rows[]=[$original['_id'],isset($errors[$i])?'Дубликат':'Вставлен',$same?'Намерение подтверждено':'Нужен разбор'];
}
$checks['two_confirmed']=$partial->getInsertedCount()===2;
$checks['item_duplicate']=array_keys($errors)===[1] && $errors[1]->getCode()===11000;
$checks['final_three']=$orders->countDocuments([])===3;
$checks['last_executed']=$rows[2][1]==='Вставлен';
$checks['intent_matched']=array_column($rows,2)===array_fill(0,3,'Намерение подтверждено');
return [$rows,$checks,'Все три намерения найдены · новых вставок '.$partial->getInsertedCount(),$info];
}
);
Ожидаемый результат
Все три намерения найдены · новых вставок 2
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
Измените operation или item для уже сохранённого R-312. Тогда после дубликата результат сверки должен перейти в «Нужен разбор», а остальные строки сохранят свои статусы.
Обновить обработчик ошибки старого одиночного пакета
В перенесённом со старого PHP-драйвера импорте дубликаты больше не попадают в прежний catch. Требуется сохранить видимый пользователю исход и выбрать современный класс для конкретного вызываемого метода.
Исходные данные
У R-321 уже есть сумма 1400 ₽. Обычный BulkWrite с executeBulkWrite пробует вставить другой заказ с тем же номером. Для этого API используется BulkWriteException, отдельный от BulkWriteCommandException в предыдущих примерах.
- executeBulkWrite → один duplicate 11000
- BulkWriteException → getWriteResult → 0 новых вставок
- Контрольное чтение R-321 сохраняет сумму 1400 ₽
Короткий фрагмент
try {
$manager->executeBulkWrite($namespace,$bulk);
} catch (BulkWriteException $error) {
$writeResult=$error->getWriteResult();
$itemErrors=$writeResult->getWriteErrors();
}Что увидит сотрудник
Сумма R-321 сохранилась прежней · 1400 ₽
Открыть фактический результат и техническую запись · Строки таблицы в JSON
| Наблюдение | Результат |
|---|---|
| Старый класс доступен | false |
| Код ошибки строки | 11000 |
| Фактическая сумма, ₽ | 1400 |
Разбор результата
Раньше существовал абстрактный родитель WriteException. Его не выбрасывали напрямую. Он удалён в 2.0, поэтому современный обработчик ловит соответствующий класс конкретного API. Вероятное изменение суммы после неудачной вставки здесь проверено по документу.
Границы примера
Переписывая старый код, проверьте точное имя выполняющей функции: executeBulkWrite и executeBulkWriteCommand предоставляют разные виды квитанций и исключений. Перехватить общий ServerException удобно для верхней границы приложения, но этого недостаточно для анализа позиции строки.
Самостоятельная программа и архив
<?php
declare(strict_types=1);
require __DIR__.'/recovery-lab.php';
use MongoDB\{Client, Collection};
use MongoDB\Driver\{Manager, Command, Query, ReadPreference, BulkWrite};
use MongoDB\Driver\Exception\{AuthenticationException, ConnectionException, ConnectionTimeoutException, ExecutionTimeoutException, ServerException, BulkWriteCommandException, BulkWriteException};
runRecovery('mongodb134-recovery-legacy-write', 'Обновить обработчик ошибки старого одиночного пакета', ['Наблюдение', 'Результат'],
function (Client $client, Collection $orders, string $uri, string $dbName, string $name): array {
$orders->insertOne(['_id'=>'R-321','amount'=>1400]);
$bulk=new BulkWrite();
$bulk->insert(['_id'=>'R-321','amount'=>1800]);
try {
$client->getManager()->executeBulkWrite($dbName.'.'.$name,$bulk);
throw new RuntimeException('Expected a duplicate write');
} catch (BulkWriteException $error) {
$info=diagnostic($error,'Запись через executeBulkWrite');
$result=$error->getWriteResult();
$errors=$result->getWriteErrors();
$checks['modern_server_branch']=$error instanceof ServerException;
$checks['one_duplicate']=count($errors)===1 && $errors[0]->getCode()===11000;
$checks['no_new_insert']=$result->getInsertedCount()===0;
}
$legacy=class_exists('MongoDB\\Driver\\Exception\\WriteException');
$saved=$orders->findOne(['_id'=>'R-321']);
$checks['removed_name_absent']=!$legacy;
$checks['previous_amount']=$saved['amount']===1400;
$checks['one_order']=$orders->countDocuments([])===1;
return [[['Старый класс доступен',$legacy],['Код ошибки строки',$errors[0]->getCode()],['Фактическая сумма, ₽',$saved['amount']]],
$checks,'Сумма R-321 сохранилась прежней · '.$saved['amount'].' ₽',$info];
}
);
Ожидаемый результат
Сумма R-321 сохранилась прежней · 1400 ₽
Программа выполняет реальную операцию на loopback, сопоставляет отказ и наблюдаемое состояние и строит локальный HTML-отчёт.
Попробуйте изменить
Сравните этот случай с ordered-import. В первом используйте getWriteResult(), во втором — getPartialResult() и getWriteErrors() самого BulkWriteCommandException. Отметьте места, где результат может быть null.
Какой класс искать в обработчике
| Класс | Для какой ситуации |
|---|---|
| MongoDB\Driver\Exception\AuthenticationException | Отделить отказ входа приложения в MongoDB от отсутствия записи или входа посетителя на сайт. |
| MongoDB\Driver\Exception\ConnectionException | Обработать группу отказов при установлении соединения с базой. |
| MongoDB\Driver\Exception\ConnectionTimeoutException | Распознать таймаут установления соединения или выбора сервера. |
| MongoDB\Driver\Exception\ExecutionTimeoutException | Распознать окончание серверного срока выполнения уже отправленной команды. |
| MongoDB\Driver\Exception\ServerException | Выделить ошибки, на которые ответила сама MongoDB. |
| MongoDB\Driver\Exception\BulkWriteCommandException | Собрать состояние частично выполненного пакетного импорта. |
| MongoDB\Driver\Exception\SSLConnectionException | Перенести исторический catch ошибок SSL на применяемую ветвь драйвера. |
| MongoDB\Driver\Exception\WriteException | Разобраться, почему обработчик записи из ext-mongodb 1.x перестал срабатывать. |
Из технической причины в действие пользователя
Установите этап сбоя и сопоставьте его с конкретным действием пользователя. Если запись могла примениться, прочитайте итог по известному ключу операции. Для дальнейшего разбора пригодятся события отказов команд, ошибки подтверждения уже внесённых изменений и результаты пакетного импорта по исходным позициям.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
MongoDB\Driver\Exception\AuthenticationExceptionОтделить отказ входа приложения в MongoDB от отсутствия записи или входа посетителя на сайт.
Класс ext-mongodb с 1.0.0, прямой родитель ConnectionException. На стенде используется 2.5.5. · mongodb
class MongoDB\Driver\Exception\AuthenticationException extends MongoDB\Driver\Exception\ConnectionExceptionКогда возникает и как перехватить
Драйвер выбрасывает AuthenticationException при отказе аутентификации во время подключения. Объект появляется в catch, когда выполняется операция, которая действительно требует соединения. Параметры учётной записи, механизм и authSource задаются у клиента заранее. Отдельного вызова на объекте исключения для повторной авторизации нет.
Что можно узнать и вернуть пользователю
Ловите этот тип до общего ConnectionException, если нужно отдельно уведомить эксплуатацию о проблеме учётных данных сервиса. Информация getCode() относится к наблюдаемому отказу. Ни пароль, ни полный текст строки подключения для пользовательского сообщения не нужны.
Методы и ограничения
Неверный пароль пользователя сайта и невозможность входа приложения в базу имеют разных участников. Отказ авторизованному соединению в конкретной команде из-за прав также требует другой диагностики. В опыте причина создана намеренно и известна; число из лога само по себе её не устанавливает.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Класс ext-mongodb с 1.0.0, прямой родитель ConnectionException. На стенде используется 2.5.5.
Остановить обработку изменения статуса
catch (AuthenticationException $error) {
$publicMessage = 'Статус не удалось обновить';
}Сервис не сообщает покупателю, будто он ввёл неверный пароль от сайта. R-111 остался черновиком.
Открыть полную программу и её результат
Записать только пригодную для поддержки диагностику
$event = ['stage'=>'Вход приложения в БД', 'class'=>$error::class, 'code'=>$error->getCode()];В техническом блоке есть этап и тип отказа. Поддержка проверяет профиль доступа приложения, сохраняя данные соединения за пределами публичного отчёта.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
MongoDB\Driver\Exception\ConnectionExceptionОбработать группу отказов при установлении соединения с базой.
Класс ext-mongodb с 1.0.0. Наследуется от MongoDB\Driver\Exception\RuntimeException. · mongodb
class MongoDB\Driver\Exception\ConnectionException extends MongoDB\Driver\Exception\RuntimeExceptionКогда возникает и как перехватить
Это общий класс исключений связи драйвера с сервером. Подклассы включают AuthenticationException и ConnectionTimeoutException. Используйте тип в catch, когда у приложения общее внешнее действие при недоступности источника, а внутри обработки различайте реальные причины.
Что можно узнать и вернуть пользователю
Попадание в catch возвращает контроль вашему коду, но не даёт рабочего курсора или квитанции. Можно вывести состояние недоступности и записать класс конкретного исключения. Общий интерфейс исключений драйвера и стандартные Throwable-методы остаются доступны.
Методы и ограничения
Сбой соединения не равен пустому результату поиска. При разрыве после отправки операции её итог может быть неопределённым. Контекст конкретного вызова помогает понять, был ли вообще выбран сервер; простой перехват родителя это не устанавливает.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Класс ext-mongodb с 1.0.0. Наследуется от MongoDB\Driver\Exception\RuntimeException.
Современный обработчик проблемы канала
catch (ConnectionException $error) {
$incident = ['class'=>$error::class, 'stage'=>'Установление канала'];
}На учебном порту ожидавший TLS клиент получил конкретный ConnectionTimeoutException; общий родитель его перехватил.
Открыть полную программу и её результат
Не потерять причину при общей обработке соединения
if ($error instanceof AuthenticationException) {
$next = 'Проверить профиль доступа приложения';
}Аутентификация относится к той же ветви ConnectionException, но требует другого действия, чем устранение недоступности нужного узла.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
MongoDB\Driver\Exception\ConnectionTimeoutExceptionРаспознать таймаут установления соединения или выбора сервера.
Класс ext-mongodb с 1.0.0. В 2.5.5 оба примера реально попадают в эту ветвь. · mongodb
final class MongoDB\Driver\Exception\ConnectionTimeoutException extends MongoDB\Driver\Exception\ConnectionExceptionКогда возникает и как перехватить
Этот тип возникает, если соединение с сервером не устанавливается в пределах connectTimeoutMS либо выбор подходящего сервера не завершается в течение serverSelectionTimeoutMS. Обе настройки измеряются в миллисекундах. Создание Manager само по себе не доказывает, что приложение подключилось.
Что можно узнать и вернуть пользователю
Предполагаемый результат запроса в такой ветви не возвращён. Приложение может ограничить время ожидания экрана и передать оператору фактический класс, этап и причину выбора маршрута. Это конкретный final-подкласс ConnectionException.
Методы и ограничения
Нельзя по одному имени отличить неверный endpoint, TLS, режим чтения, сетевую фильтрацию и иные причины. Сравните ожидаемый маршрут с топологией и конфигурацией. socketTimeoutMS относится к сетевому обмену, а maxTimeMS — к выполнению операции на сервере.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Класс ext-mongodb с 1.0.0. В 2.5.5 оба примера реально попадают в эту ветвь.
Не считать пустым список при отсутствии узла
$route = new ReadPreference('secondary');
$cursor = $manager->executeQuery($namespace,$query,['readPreference'=>$route]);В нашей реплике нет secondary. Контрольное чтение с primary находит существующий R-101.
Открыть полную программу и её результат
Сопоставить класс с настройкой канала
$test = freshManager($uri, ['tls'=>true, 'serverSelectionTimeoutMS'=>500]);
$test->executeCommand($dbName,new Command(['ping'=>1]));Учебный endpoint не обслуживает TLS. Наблюдаемый таймаут не превращают в универсальный способ диагностировать сертификаты.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
MongoDB\Driver\Exception\ExecutionTimeoutExceptionРаспознать окончание серверного срока выполнения уже отправленной команды.
Класс ext-mongodb с 1.0.0, с 1.5.0 находится в ветви ServerException. На 2.5.5 класс final. · mongodb
final class MongoDB\Driver\Exception\ExecutionTimeoutException extends MongoDB\Driver\Exception\ServerExceptionКогда возникает и как перехватить
Сервер прекращает выполнение команды или запроса при превышении срока, например maxTimeMS. Драйвер передаёт ExecutionTimeoutException. В учебном update лимит 150 миллисекунд, а socketTimeoutMS клиента значительно больше. Первая транзакция держит изменённый документ на том же сервере.
Что можно узнать и вернуть пользователю
Код серверного отказа доступен в getCode(). При превышении maxTimeMS в проверенном update это 50. У вызова нет успешного результата изменения, поэтому состояние документа читают отдельно. Родитель ServerException полезен для более общей границы обработки.
Методы и ограничения
Лимит отслеживается сервером в точках прерывания операции. Это не секундомер клиентского интерфейса и не разрешение повторить бизнес-действие. Для уже выполненных частей другой команды или многодокументной записи могут понадобиться дополнительные проверки.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Класс ext-mongodb с 1.0.0, с 1.5.0 находится в ветви ServerException. На 2.5.5 класс final.
Показать оператору причину задержки резерва
catch (ExecutionTimeoutException $error) {
$code = $error->getCode();
$stage = 'Исполнение update на сервере';
}Для ожидающего чужой транзакции update реально наблюдается 50, а после отмены держателя резерв равен 4.
Открыть полную программу и её результат
Перечитать значение перед новой редакцией
$saved = $orders->findOne(['_id'=>'R-131'], ['projection'=>['_id'=>0,'reserved'=>1]]);
$currentQuantity = $saved['reserved'];Редактору возвращается фактический остаток после завершения опыта; незавершённое 5 и запрошенное 9 не попадают в карточку как сохранённые значения.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
MongoDB\Driver\Exception\ServerExceptionВыделить ошибки, на которые ответила сама MongoDB.
Класс ext-mongodb с 1.5.0, наследуется от MongoDB\Driver\Exception\RuntimeException. · mongodb
class MongoDB\Driver\Exception\ServerException extends MongoDB\Driver\Exception\RuntimeExceptionКогда возникает и как перехватить
Базовый класс серверных исключений. Тип может быть конкретнее — CommandException, ExecutionTimeoutException или один из типов ошибки записи. Начинайте с более узкого catch там, где от причины зависит разбор квитанции, затем при необходимости обрабатывайте ServerException.
Что можно узнать и вернуть пользователю
Код ошибки для серверного отказа берут из getCode(). Для пакета дополнительно проверяют отдельные ошибки по индексам и сведения о подтверждении. У ServerException самого по себе нет универсального метода, который описывает фактические изменения любых операций.
Методы и ограничения
Ответ сервера свидетельствует, что вызов дошёл до него. Он не доказывает полного отката или полного успеха партии. Проводите различие между неизвестной командой, таймаутом и ошибкой одного элемента.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Класс ext-mongodb с 1.5.0, наследуется от MongoDB\Driver\Exception\RuntimeException.
Выделить ошибку имени команды
catch (ServerException $error) {
$actualType = $error::class;
$serverCode = $error->getCode();
}В этом случае конкретный CommandException и код 59 указывают на недопустимое имя. Повтор без исправления не поможет.
Открыть полную программу и её результат
Отделить срок выполнения у сервера от сетевого выбора
$isServerError = $timeout instanceof ServerException;У реального ExecutionTimeoutException в примере это true. Для ConnectionTimeoutException из выбора узла это другая ветвь.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
MongoDB\Driver\Exception\BulkWriteCommandExceptionСобрать состояние частично выполненного пакетного импорта.
Класс ext-mongodb с 2.1.0; в tagged-исходнике 2.5.5 объявлен final. Для клиентской команды bulkWrite нужен MongoDB Server 8.0+. · mongodb
final class MongoDB\Driver\Exception\BulkWriteCommandException extends MongoDB\Driver\Exception\ServerExceptionКогда возникает и как перехватить
Используется с BulkWriteCommand и Manager::executeBulkWriteCommand(). Свойства с внутренними результатами закрыты. getErrorReply() даёт ?MongoDB\BSON\Document для ошибки команды верхнего уровня; getPartialResult() — ?MongoDB\Driver\BulkWriteCommandResult для определённых успешных операций.
Что можно узнать и вернуть пользователю
getWriteErrors() возвращает ошибки отдельных поручений по исходным индексам. getWriteConcernErrors() отдельно возвращает ошибки выполнения условия подтверждения. В нашем ordered-пакете ошибка индекса 1 имеет код 11000, getPartialResult сообщает об одной вставке, getErrorReply равен null, список ошибок подтверждения пуст.
Методы и ограничения
Частичный результат может отсутствовать. Не разыменовывайте его без проверки. Номера поручений не являются ID заказов, их нужно сопоставить с исходным пакетом. Отдельный executeBulkWrite выбрасывает BulkWriteException и предоставляет другую квитанцию.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Класс ext-mongodb с 2.1.0; в tagged-исходнике 2.5.5 объявлен final. Для клиентской команды bulkWrite нужен MongoDB Server 8.0+.
Назначить отдельное действие каждой строке
$partial = $error->getPartialResult();
$positions = array_keys($error->getWriteErrors());Вставлен R-301, у R-302 конфликт с другим товаром, R-303 отсутствует. Результат команды дополнен чтением трёх ID.
Открыть полную программу и её результат
Установить, принят ли заказ раньше
$errors = $error->getWriteErrors();
$saved = $orders->findOne(['_id'=>$input[$i]['_id']]);Сверка полей и операции pack-312 показывает ранее принятое намерение, а последняя независимая вставка R-313 действительно исполнена.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
MongoDB\Driver\Exception\SSLConnectionExceptionПеренести исторический catch ошибок SSL на применяемую ветвь драйвера.
Введён в 1.0.0, устарел в 1.5.0, удалён в 2.0.0. Только историческое описание. · mongodb
// ext-mongodb 1.x
final class MongoDB\Driver\Exception\SSLConnectionException extends MongoDB\Driver\Exception\ConnectionExceptionКогда возникает и как перехватить
Историческое имя существовало в ext-mongodb 1.x и описывало ошибку SSL-подключения. Расширение никогда не выбрасывало этот тип. Поэтому наличие класса в старой версии не означало, что catch реально обрабатывал проблемы TLS.
Что можно узнать и вернуть пользователю
В современном коде ловят ConnectionException или конкретный наблюдаемый подкласс. Если приложение проверяет установленную версию, class_exists() показывает наличие определения без попытки искусственно вызвать старое исключение.
Методы и ограничения
Не отключайте проверку сертификата в ответ на любой сетевой таймаут. Факторы TLS и выбора конкретного сервера проверяют отдельно. В опыте намеренно направляется TLS-клиент к локальному учебному порту без TLS; сценарий не удостоверяет другие варианты сетевых ошибок.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Введён в 1.0.0, устарел в 1.5.0, удалён в 2.0.0. Только историческое описание.
Распознать мёртвую ветвь старого кода
$present=class_exists('MongoDB\\Driver\\Exception\\SSLConnectionException');На 2.5.5 результат false. Наши приложения не конструируют и не бросают этот удалённый класс.
Открыть полную программу и её результат
Обработать современный отказ
catch (ConnectionException $error) {
$category = 'Проверка канала';
}Срабатывает обработчик существующей ветви. В карточку не попадает сырой диагностический текст драйвера.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
MongoDB\Driver\Exception\WriteExceptionРазобраться, почему обработчик записи из ext-mongodb 1.x перестал срабатывать.
Существовал с ext-mongodb 1.0.0; с 1.5.0 наследовался от ServerException, устарел в 1.20.0 и удалён в 2.0.0. · mongodb
// ext-mongodb 1.x
abstract class MongoDB\Driver\Exception\WriteException extends MongoDB\Driver\Exception\ServerExceptionКогда возникает и как перехватить
Исторический абстрактный класс объединял ошибки записи и имел защищённое свойство writeResult. Метод getWriteResult() предоставлял MongoDB\Driver\WriteResult. Исключение этого абстрактного типа само расширение не бросало напрямую.
Что можно узнать и вернуть пользователю
В ext-mongodb 2.x обработчик для executeBulkWrite должен ловить BulkWriteException и получать getWriteResult() из него. При использовании нового BulkWriteCommand класс BulkWriteCommandException требует своего разбора getPartialResult/getWriteErrors.
Методы и ограничения
Нельзя просто заменить прежнее имя любым типом, в названии которого есть BulkWrite. Сначала проверьте реально вызываемый метод. Общий родитель ServerException пригоден для общей диагностики, но не гарантирует присутствие интересующего getter у каждого исключения.
Для технической диагностики у ветви драйвера есть унаследованные методы getCode(), getMessage(), getPrevious() и методы Throwable для указания места и стека. hasErrorLabel() проверяет конкретную метку, когда есть документированная политика для данной операции. Для безопасного пользовательского сообщения в новых примерах отбираются только нужные сведения.
Версии
Существовал с ext-mongodb 1.0.0; с 1.5.0 наследовался от ServerException, устарел в 1.20.0 и удалён в 2.0.0.
Перенести обработчик одиночного BulkWrite
catch (BulkWriteException $error) {
$details=$error->getWriteResult()->getWriteErrors();
}У текущего R-321 сохраняется прежняя сумма. Исчезновение старого имени подтверждается class_exists() на том же расширении.
Открыть полную программу и её результат
Выбрать обработчик при переходе на новую пакетную команду
catch (BulkWriteCommandException $error) {
$partial=$error->getPartialResult();
}Здесь используется другой API. У исходной партии три позиции и нужен учёт частичного исполнения, включая строку после конфликта.
Открыть полную программу и её результат
Общее основание обработки подробно разобрано в контракте исключений драйвера.
Источники и обсуждения
- AuthenticationException
- ConnectionException
- ConnectionTimeoutException
- ExecutionTimeoutException
- ServerException
- BulkWriteCommandException
- SSLConnectionException
- WriteException
- BulkWriteCommandException, исходник 2.5.5
- getPartialResult
- bulkWrite, сервер 8.0
- Команда update
- Настройки подключения
- AuthenticationException, исходник 2.5.5
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


