← Назад к заметкам

Заметки

Как отлаживать 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-карточки. Чем меньше лишних данных попадает в журнал, тем проще сопровождать процесс и безопаснее разбирать ошибки.