Заметки
Ошибка PHP в бизнес-процессе может скрыть факт запуска
Почему при ошибке в действии PHP-код бизнес-процесса может не остаться понятного следа, что нужная часть процесса запускалась.
Если действие PHP код падает до записи лога или служебной отметки,
потом сложно понять, запускался ли этот участок бизнес-процесса вообще.
Суть проблемы
В бизнес-процессе может быть действие с произвольным PHP-кодом. Если этот код завершается ошибкой, выполнение обрывается раньше, чем процесс успевает записать диагностическую информацию.
Коротко
Проблема не только в самой ошибке PHP. Проблема в том, что после неё может быть непонятно, дошёл ли бизнес-процесс до этого действия.
Типовая цепочка выглядит так:
- бизнес-процесс запускается по сделке, лиду или элементу списка;
- процесс доходит до действия
PHP код; - внутри PHP возникает ошибка;
- следующие действия не выполняются;
- служебная переменная, лог или отметка о старте не записываются;
- при разборе кажется, что бизнес-процесс мог вообще не запускаться.
Поэтому старт важного участка лучше фиксировать до рискованного кода, а не после него.
Где встречается
Чаще всего это заметно в коробочном Bitrix24:
- в действии
PHP кодвнутри бизнес-процесса; - в процессах, которые запускаются автоматически;
- при обращении к CRM из PHP-кода;
- при работе с пользовательскими полями;
- при вызове методов модулей без проверки подключения;
- при использовании данных из переменных бизнес-процесса;
- при запуске бизнес-процесса из другого бизнес-процесса.
Особенно неприятно это в автоматических процессах: пользователь не видит ошибку напрямую, а в истории элемента может не быть понятного объяснения.
Причина
Действие PHP код выполняется как часть бизнес-процесса. Если внутри него возникает
ошибка, код ниже ошибки уже не выполняется.
Ошибка обрывает выполнение
Если сначала выполнить рискованное действие, а потом записать лог, то при ошибке лог может просто не появиться.
<?php
use Bitrix\Main\Loader;
Loader::includeModule('crm');
$deal_id = (int)$this->GetVariable('DEAL_ID');
updateDealData($deal_id);
$this->WriteToTrackingService('PHP-действие выполнено');
/**
* Обновляет данные сделки.
*/
function updateDealData(int $deal_id): void
{
if ($deal_id <= 0) {
throw new RuntimeException('Не передан ID сделки');
}
$deal = new CCrmDeal(false);
$deal->Update($deal_id, [
'UF_CRM_EXAMPLE_FIELD' => 'Y',
]);
}
Если ошибка произойдёт внутри updateDealData, строка с
WriteToTrackingService может не выполниться.
Лог мог не успеть записаться
В журнал бизнес-процесса нужно писать не только успешное завершение, но и факт входа в важный участок логики.
Иначе при разборе останется непонятный разрыв: бизнес-процесс вроде должен был сработать, но по журналу не видно, дошёл ли он до PHP-действия.
Решение
Перед рискованным кодом нужно заранее записывать техническую отметку: в лог бизнес-процесса, переменную, поле элемента или отдельный файл логов.
Фиксировать старт заранее
Минимальный порядок такой:
- получить нужные входные данные;
- записать в лог, что PHP-действие началось;
- завернуть основной код в
try/catch; - при ошибке записать текст ошибки;
- после успешного выполнения записать отдельный лог завершения.
Так потом видно три состояния: действие не запускалось, действие стартовало и упало, действие стартовало и завершилось.
Пример для PHP-кода
В действии бизнес-процесса PHP код открывающий и закрывающий теги PHP указывать
не нужно. В примере ниже они показаны только для оформления заметки.
<?php
use Bitrix\Main\Loader;
const CRM_MODULE_ID = 'crm';
const LOG_PREFIX = '[BP PHP]';
Loader::includeModule(CRM_MODULE_ID);
$deal_id = (int)$this->GetVariable('DEAL_ID');
writeProcessLog($this, 'Действие PHP код запущено', [
'DEAL_ID' => $deal_id,
]);
try {
updateDealData($deal_id);
writeProcessLog($this, 'Действие PHP код завершено', [
'DEAL_ID' => $deal_id,
]);
} catch (Throwable $exception) {
writeProcessLog($this, 'Ошибка в действии PHP код', [
'DEAL_ID' => $deal_id,
'ERROR' => $exception->getMessage(),
]);
}
/**
* Обновляет данные сделки.
*/
function updateDealData(int $deal_id): void
{
if ($deal_id <= 0) {
throw new RuntimeException('Не передан ID сделки');
}
$deal = new CCrmDeal(false);
$deal->Update($deal_id, [
'UF_CRM_EXAMPLE_FIELD' => 'Y',
]);
}
/**
* Пишет сообщение в журнал бизнес-процесса.
*/
function writeProcessLog(object $activity, string $message, array $context): void
{
$log_data = [
'MESSAGE' => LOG_PREFIX . ' ' . $message,
'CONTEXT' => $context,
];
$activity->WriteToTrackingService(print_r($log_data, true));
} Если нужно не только увидеть ошибку в журнале, но и остановить бизнес-процесс как ошибочный, после записи лога можно повторно выбросить исключение. Это зависит от того, как должен вести себя конкретный процесс.