Заметки
Как отлаживать PHP-код внутри бизнес-процесса
Как безопасно отлаживать PHP-активити в бизнес-процессах Bitrix24: журнал БП, print_r, запись в файл, переменные и ограничения на проде.
Самый безопасный первый шаг при отладке PHP-активити — писать короткие сообщения
в журнал бизнес-процесса через WriteToTrackingService. Запись в файл лучше
оставлять для больших массивов и временной диагностики, а на проде обязательно ограничивать
объём логов и не выводить токены, пароли и персональные данные.
Задача
В коробочном Bitrix24 в бизнес-процесс можно добавить действие «PHP-код». Оно удобно, когда стандартных действий не хватает: нужно обработать массив, найти связанные элементы, вызвать D7-класс или выполнить нестандартную проверку.
Проблема в том, что PHP-активити не всегда понятно падает. Иногда бизнес-процесс просто идёт дальше не так, как ожидалось, переменная остаётся пустой, условие не срабатывает, а в интерфейсе нет очевидной причины.
Поэтому код в БП лучше писать так, чтобы он сам оставлял следы: где запустился, какие данные получил, какую ветку выбрал и чем завершился.
Журнал бизнес-процесса
Для короткой отладки удобнее всего использовать журнал бизнес-процесса. Сообщение появится там же, где видно ход выполнения действий.
Минимальный пример:
$this->WriteToTrackingService('PHP-активити запустилась'); Такой вывод помогает быстро понять, дошёл ли процесс до нужного PHP-действия. Это полезно даже до отладки переменных: сначала нужно убедиться, что код вообще запускается.
Если нужно вывести значение переменной БП, можно получить корневую активити и прочитать переменную по имени:
$root_activity = $this->GetRootActivity();
$deal_id = $root_activity->GetVariable('DealId');
$this->WriteToTrackingService('DealId: ' . $deal_id); Журнал не стоит превращать в постоянное хранилище больших данных. Для обычной диагностики достаточно коротких сообщений: ID элемента, выбранная ветка, итоговый статус, текст ошибки.
Массивы и переменные
В БП часто ломается не сам PHP-код, а ожидание формата данных. Переменная может быть строкой, числом, массивом, пустой строкой или вообще не тем значением, которое кажется по дизайнеру процесса.
Для быстрой проверки массива можно использовать print_r со вторым параметром
true. Тогда результат вернётся строкой, и его можно отправить в журнал.
$root_activity = $this->GetRootActivity();
$user_ids = $root_activity->GetVariable('UserIds');
$this->WriteToTrackingService(
'UserIds: ' . print_r($user_ids, true)
);
Для более компактного вывода часто удобнее собрать диагностический массив вручную
и вывести его через json_encode:
$debug_data = [
'deal_id' => $deal_id,
'user_ids' => $user_ids,
'stage_id' => $stage_id,
];
$this->WriteToTrackingService(
'Debug data: ' . json_encode($debug_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
); Так проще читать лог: вместо огромного дампа всего документа в журнал попадают только те значения, которые нужны для конкретной проверки.
Если результат отладки нужен дальше в самом процессе, лучше записать его в отдельную
переменную БП через SetVariable. Например, можно хранить статус выполнения
или последнее сообщение об ошибке.
Ошибки и try/catch
Любой PHP-код в БП лучше оборачивать в try/catch, особенно если внутри есть
загрузка модулей, работа с CRM, файлами, задачами или внешними сервисами.
Пример ниже пишет ошибку в журнал и одновременно сохраняет её в переменную процесса:
try {
$root_activity = $this->GetRootActivity();
$deal_id = (int)$root_activity->GetVariable('DealId');
if ($deal_id <= 0) {
throw new RuntimeException('DealId не заполнен');
}
$this->WriteToTrackingService('Обработка сделки: ' . $deal_id);
// Здесь основной код активити.
} catch (Throwable $exception) {
$this->WriteToTrackingService(
'Ошибка PHP-активити: ' . $exception->getMessage()
);
$this->SetVariable('PhpDebugError', $exception->getMessage());
}
Такой подход удобен тем, что ошибка не пропадает. Её можно увидеть в журнале,
а переменную PhpDebugError использовать дальше: отправить администратору,
показать в задаче или записать в поле служебного смарт-процесса.
Если нужно просто отметить итог выполнения, можно хранить отдельный статус:
try {
// Основной код активити.
$this->SetVariable('PhpDebugStatus', 'success');
$this->SetVariable('PhpDebugError', '');
} catch (Throwable $exception) {
$this->SetVariable('PhpDebugStatus', 'error');
$this->SetVariable('PhpDebugError', $exception->getMessage());
$this->WriteToTrackingService(
'Ошибка PHP-активити: ' . $exception->getMessage()
);
} Для постоянной логики лучше не оставлять переменные с названием «debug» в пользовательском интерфейсе. Их удобно использовать на время настройки, а потом убрать или заменить на понятные служебные поля.
Запись в файл
Файл нужен, когда журнал БП становится неудобным: данных много, процесс запускается часто или нужно сравнить несколько запусков подряд.
Пример временного файлового лога:
function writeBizprocDebugLog(string $message, array $context = []): void
{
$log_dir = $_SERVER['DOCUMENT_ROOT'] . '/upload/bp-debug';
if (!is_dir($log_dir)) {
mkdir($log_dir, 0755, true);
}
$log_file = $log_dir . '/debug.log';
$log_row = [
'date' => date('Y-m-d H:i:s'),
'message' => $message,
'context' => $context,
];
file_put_contents(
$log_file,
json_encode($log_row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL,
FILE_APPEND | LOCK_EX
);
}
writeBizprocDebugLog('PHP-активити запустилась', [
'workflow_id' => $this->GetWorkflowInstanceId(),
]); Такой лог удобен для диагностики, но его нельзя оставлять без контроля. Если писать в файл на каждом запуске БП, он быстро разрастётся. Лучше добавлять отладку временно, писать только нужные поля и после решения проблемы удалять код или выключать его флагом.
Если есть возможность, храните технические логи вне публичной директории сайта.
Если лог временно лежит внутри /upload, ограничьте доступ к папке на уровне
веб-сервера и не пишите туда чувствительные данные.
Безопасная отладка на проде
На рабочем портале отладка должна быть управляемой. Нельзя просто оставить подробный дамп всех данных сделки, контакта или пользователя в журнале и забыть о нём.
Лучше завести отдельную переменную БП вроде IsDebugEnabled и писать
диагностику только когда она включена:
$root_activity = $this->GetRootActivity();
$is_debug_enabled = $root_activity->GetVariable('IsDebugEnabled') === 'Y';
if ($is_debug_enabled) {
$this->WriteToTrackingService('Отладка включена');
} В логах не должно быть паролей, токенов, URL входящих вебхуков, паспортных данных, лишних телефонов, email и другой информации, которая не нужна для диагностики. Если нужно понять, что значение пришло, часто достаточно вывести ID, тип, длину строки или факт заполнения.
Хорошее правило для прода: лог должен отвечать на вопрос «где сломалось и почему», но не должен становиться копией CRM-карточки. Чем меньше лишних данных попадает в журнал, тем проще сопровождать процесс и безопаснее разбирать ошибки.