← Назад к справке

Бизнес-процессы

PHP-код в бизнес-процессах

Рабочая справка по PHP-коду в блоках бизнес-процессов коробочного Bitrix24: переменные, поля документа, отчёт, try-catch, функции и условия.

Главная особенность: код в действии бизнес-процесса пишется без открывающего и закрывающего PHP-тега. Внутри блока уже есть контекст бизнес-процесса, поэтому переменные, константы, поля документа и отчёт обычно получают через $this.

Оформление

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

Код без PHP-тегов

В блоке бизнес-процесса не нужно писать <?php, <? или ?>. Вставляется только сам код.

Вариант Как писать
В обычном PHP-файле <?php нужен в начале файла.
В PHP-блоке бизнес-процесса PHP-теги не нужны. Пишется только тело кода.
$this->WriteToTrackingService(
    'PHP-блок выполнился',
    0,
    \CBPTrackingType::Report
);

Шаблон с try-catch

Если в PHP-блоке возникает ошибка, бизнес-процесс может прекратить выполнение. При ручном запуске ошибку иногда видно сразу на экране, а при запуске от события её часто приходится искать в F12 → Network.

Поэтому рабочий вариант — оборачивать код в try-catch и писать ошибку в отчёт бизнес-процесса.

/**
 * Записывает обычное сообщение в отчёт бизнес-процесса.
 */
function writeBpReportMessage($context, string $message): void
{
    $context->WriteToTrackingService(
        $message,
        0,
        \CBPTrackingType::Report
    );
}

/**
 * Записывает ошибку в отчёт бизнес-процесса.
 */
function writeBpErrorMessage($context, \Throwable $exception): void
{
    $message = sprintf(
        'Ошибка: %s. Файл: %s. Строка: %s',
        $exception->getMessage(),
        $exception->getFile(),
        $exception->getLine()
    );

    $context->WriteToTrackingService(
        $message,
        0,
        \CBPTrackingType::FaultActivity
    );
}

try {
    writeBpReportMessage($this, 'PHP-блок начал выполнение');

    // Здесь основной код блока

    writeBpReportMessage($this, 'PHP-блок завершил выполнение');
} catch (\Throwable $exception) {
    writeBpErrorMessage($this, $exception);

    // Если процесс должен остановиться, можно пробросить ошибку дальше.
    // throw $exception;
}

Важно: try-catch не обязан останавливать бизнес-процесс. Он позволяет записать ошибку в отчёт. После записи ошибки нужно решить, что делать дальше: прервать выполнение через throw, выйти из блока через return или дать процессу продолжиться.

Если код большой или повторяется в нескольких местах, функции лучше вынести в /local/php_interface/init.php или в свой модуль. В PHP-блоке оставить только вызов.

Почему не вставлять значения напрямую

Не стоит вставлять значения стандартным способом прямо в PHP-код: {=Document:TITLE}, {{Название}} и похожими подстановками. Битрикс подставляет значение до выполнения PHP-кода, и можно получить синтаксическую ошибку.

Например, если поле пустое, код может превратиться в такой вариант:

$title = ;

Если значение содержит кавычки, ошибка тоже возможна:

$title = 'Компания 'Рога и копыта'';

Безопаснее получать значения через методы бизнес-процесса уже внутри PHP-кода.

Данные процесса

Основные данные бизнес-процесса читаются через $this или через корневую активность $this->GetRootActivity().

Переменные и константы процесса

Переменные процесса читаются через GetVariable(), записываются через SetVariable(). Константы процесса можно получить через GetConstant().

/**
 * Получает имя из переменной бизнес-процесса.
 */
function fetchProcessName($context): string
{
    return (string) $context->GetVariable('name');
}

/**
 * Сохраняет имя в переменную бизнес-процесса.
 */
function saveProcessName($context, string $name): void
{
    $context->SetVariable('name', $name);
}

/**
 * Получает значение константы бизнес-процесса.
 */
function fetchProcessConstant($context, string $constant_id)
{
    return $context->GetConstant($constant_id);
}

try {
    $name = fetchProcessName($this);
    $name_prefix = (string) fetchProcessConstant($this, 'name_prefix');

    saveProcessName($this, $name_prefix . $name);
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Вариант через корневую активность тоже рабочий. Его удобно использовать, если код копируется из примеров, где принято сначала получать $root_activity.

$root_activity = $this->GetRootActivity();

$name = (string) $root_activity->GetVariable('name');

$root_activity->SetVariable('name', $name);

Поля документа

Поля документа лучше читать через сервис документа. Так не нужно вставлять {=Document:...} прямо в PHP-код.

/**
 * Получает поля документа бизнес-процесса.
 */
function fetchDocumentFields($context): array
{
    $document_service = $context->workflow->GetService('DocumentService');
    $document_fields = $document_service->getDocument($context->getDocumentId());

    return is_array($document_fields) ? $document_fields : [];
}

/**
 * Получает значение поля документа.
 */
function fetchDocumentFieldValue(array $document_fields, string $field_id)
{
    return $document_fields[$field_id] ?? null;
}

try {
    $document_fields = fetchDocumentFields($this);
    $title = (string) fetchDocumentFieldValue($document_fields, 'TITLE');

    $this->SetVariable('document_title', $title);
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Название поля зависит от документа, для которого запущен бизнес-процесс. Для CRM это могут быть поля сделки, лида, компании, контакта или смарт-процесса.

Изменение документа

Через $this удобно читать данные процесса и документа. Сам документ лучше изменять через API конкретной сущности: CCrmDeal, CCrmLead, ORM смарт-процесса или отдельные действия бизнес-процесса.

Пример ниже — упрощённая заготовка для CRM-документов, где ID сущности находится в конце document id. Для других типов документов сначала выведите $this->getDocumentId() в отчёт и проверьте фактический формат.

\Bitrix\Main\Loader::includeModule('crm');

/**
 * Получает ID CRM-документа из бизнес-процесса.
 */
function fetchCrmDocumentEntityId($context): int
{
    $document_ids = $context->getDocumentId();
    $document_code = is_array($document_ids) ? (string) end($document_ids) : (string) $document_ids;

    preg_match('/(\d+)$/', $document_code, $matches);

    return isset($matches[1]) ? (int) $matches[1] : 0;
}

/**
 * Изменяет название сделки.
 */
function updateDealTitle(int $deal_id, string $title): bool
{
    if ($deal_id <= 0) {
        return false;
    }

    $deal = new \CCrmDeal();

    return (bool) $deal->Update(
        $deal_id,
        [
            'TITLE' => $title,
        ],
        true,
        true,
        [
            'DISABLE_USER_FIELD_CHECK' => true,
        ]
    );
}

try {
    $deal_id = fetchCrmDocumentEntityId($this);
    $title = (string) $this->GetVariable('new_title');

    $is_updated = updateDealTitle($deal_id, $title);

    $this->WriteToTrackingService(
        $is_updated ? 'Сделка обновлена' : 'Сделка не обновлена',
        0,
        \CBPTrackingType::Report
    );
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Для лида, контакта, компании или смарт-процесса нужен другой API. Этот пример только показывает подход: получить ID документа и изменить сущность через её PHP-методы.

Глобальные значения

Глобальные переменные и константы бизнес-процессов читаются не через GetVariable(), а через классы модуля bizproc.

Глобальные константы

Для глобальных констант используется \Bitrix\Bizproc\Workflow\Type\GlobalConst. Через getById() можно получить описание свойства, через getValue() — значение.

\Bitrix\Main\Loader::includeModule('bizproc');

/**
 * Получает свойство глобальной константы.
 */
function fetchGlobalConstantProperty(string $constant_id): array
{
    $constant_property = \Bitrix\Bizproc\Workflow\Type\GlobalConst::getById($constant_id);

    return is_array($constant_property) ? $constant_property : [];
}

/**
 * Получает значение глобальной константы.
 */
function fetchGlobalConstantValue(string $constant_id)
{
    return \Bitrix\Bizproc\Workflow\Type\GlobalConst::getValue($constant_id);
}

try {
    $constant_id = 'Const123456789';
    $constant_property = fetchGlobalConstantProperty($constant_id);
    $constant_value = fetchGlobalConstantValue($constant_id);

    $this->WriteToTrackingService(
        print_r($constant_value, true),
        0,
        \CBPTrackingType::Report
    );
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Глобальные переменные

Для глобальных переменных используется \Bitrix\Bizproc\Workflow\Type\GlobalVar. Подход похож на глобальные константы.

\Bitrix\Main\Loader::includeModule('bizproc');

/**
 * Получает свойство глобальной переменной.
 */
function fetchGlobalVariableProperty(string $variable_id): array
{
    $variable_property = \Bitrix\Bizproc\Workflow\Type\GlobalVar::getById($variable_id);

    return is_array($variable_property) ? $variable_property : [];
}

/**
 * Получает значение глобальной переменной.
 */
function fetchGlobalVariableValue(string $variable_id)
{
    return \Bitrix\Bizproc\Workflow\Type\GlobalVar::getValue($variable_id);
}

try {
    $variable_id = 'Var123456789';
    $variable_property = fetchGlobalVariableProperty($variable_id);
    $variable_value = fetchGlobalVariableValue($variable_id);

    $this->SetVariable('global_variable_value', $variable_value);
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Изменить глобальную переменную

Чтобы изменить глобальную переменную, обычно получают её свойство через getById(), меняют значение Default и сохраняют через upsert().

\Bitrix\Main\Loader::includeModule('bizproc');

/**
 * Изменяет значение глобальной переменной.
 */
function updateGlobalVariableDefault(string $variable_id, $value): bool
{
    $global_variable = \Bitrix\Bizproc\Workflow\Type\GlobalVar::getById($variable_id);

    if (!is_array($global_variable)) {
        return false;
    }

    $global_variable['Default'] = $value;

    \Bitrix\Bizproc\Workflow\Type\GlobalVar::upsert($variable_id, $global_variable);

    return true;
}

try {
    $variable_id = 'Var123456789';
    $is_updated = updateGlobalVariableDefault($variable_id, 'Y');

    $this->WriteToTrackingService(
        $is_updated ? 'Глобальная переменная обновлена' : 'Глобальная переменная не найдена',
        0,
        \CBPTrackingType::Report
    );
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Отчёт и отладка

Отчёт бизнес-процесса — самый удобный способ понять, что произошло внутри PHP-блока.

Запись в отчёт

Для записи в отчёт используется WriteToTrackingService(). Первый параметр должен быть строкой.

$this->WriteToTrackingService('Some message!');

$this->WriteToTrackingService(
    'Some message!',
    0,
    \CBPTrackingType::Report
);
Тип Когда использовать
\CBPTrackingType::Report Обычные сообщения: старт, результат, найденные ID, промежуточные значения.
\CBPTrackingType::Error Ошибки, которые не обязательно ломают весь процесс.
\CBPTrackingType::FaultActivity Критическая ошибка в действии.
\CBPTrackingType::Custom Сообщение произвольного типа.

Массивы и ошибки

Если нужно вывести массив, его нужно преобразовать в строку. Для быстрой отладки удобен print_r($array, true). Для более компактной записи можно использовать json_encode().

/**
 * Записывает массив в отчёт через print_r.
 */
function writeArrayToReport($context, array $items): void
{
    $context->WriteToTrackingService(
        print_r($items, true),
        0,
        \CBPTrackingType::Report
    );
}

/**
 * Записывает массив в отчёт через JSON.
 */
function writeJsonToReport($context, array $items): void
{
    $context->WriteToTrackingService(
        json_encode($items, JSON_UNESCAPED_UNICODE),
        0,
        \CBPTrackingType::Report
    );
}

try {
    $items = [
        'deal_id' => 123,
        'status' => 'success',
    ];

    writeArrayToReport($this, $items);
    writeJsonToReport($this, $items);
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Функции и условия

В PHP-блоках можно использовать свои функции, но важно помнить про область видимости и контекст бизнес-процесса.

Функции в PHP-блоке

Функции можно объявить внутри PHP-блока, но они не должны переопределять уже существующие функции. Если такая же функция уже есть в init.php или была объявлена в другом PHP-блоке этого же процесса, повторное объявление может привести к ошибке.

В циклах функции лучше не объявлять. Сначала объявить функцию, потом использовать её внутри цикла.

/**
 * Считает сумму двух чисел.
 */
function calculateBpSum(int $first_number, int $second_number): int
{
    return $first_number + $second_number;
}

try {
    $numbers = [10, 20, 30];
    $sum = 0;

    foreach ($numbers as $number) {
        $sum = calculateBpSum($sum, (int) $number);
    }

    $this->SetVariable('sum', $sum);
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

Если функция нужна в нескольких процессах, лучше вынести её в init.php или в модуль, а в PHP-блоке только вызывать.

Передача контекста

При вызове своей функции контекст бизнес-процесса сам по себе не передаётся. Если внутри функции нужен доступ к переменным, константам или отчёту, передайте $this отдельным параметром.

/**
 * Считает сумму и сохраняет её в переменную процесса.
 */
function calculateAndSaveBpSum(int $first_number, int $second_number, $context): int
{
    $sum = $first_number + $second_number;

    $context->SetVariable('sum', $sum);

    return $sum;
}

try {
    $sum = calculateAndSaveBpSum(10, 20, $this);

    $this->WriteToTrackingService(
        'Сумма: ' . $sum,
        0,
        \CBPTrackingType::Report
    );
} catch (\Throwable $exception) {
    $this->WriteToTrackingService(
        $exception->getMessage(),
        0,
        \CBPTrackingType::FaultActivity
    );
}

PHP-код в условии

PHP-код в условии отличается от обычного PHP-блока. На практике надёжнее использовать $ownerActivity. Привычный контекст $this может быть недоступен или работать не так, как в обычном действии PHP-кода.

Пример условия: выполнить ветку, если значение переменной id больше или равно 10.

$ownerActivity->GetVariable('id') >= 10

Если нужно условие по полю документа, удобный рабочий вариант — сначала в обычном PHP-блоке или блоке изменения переменных сохранить нужное значение в переменную процесса, а уже в условии читать эту переменную через $ownerActivity.

(int) $ownerActivity->GetVariable('pay_system_id') === 14

Источники