Через CPullStack можно отправить команду из PHP одному пользователю, группе получателей или всем участникам общего канала. Например, сообщить владельцу о готовности личного отчета или уведомить участников об изменении общего списка.
В команду можно включить готовые данные или идентификатор объекта, чтобы страница запросила его состояние. PHP-код выбирает получателей и проверяет их права. JavaScript-обработчик получает команду и обновляет интерфейс.
Формат команды и проверка доступа
Используйте страницу получателя /local/pull-demo/index.php из статьи Обновление страницы по команде сервера. Сервер очередей настройте по статье Настройка модуля и сервера очередей.
В примерах кода используются:
-
идентификатор модуля
example.reports, -
команда
reportReady, -
идентификатор отчета
42.
Эти значения совпадают с настройками обработчика из статьи Обновление страницы по команде сервера. Отчет условный. Код сообщает о готовности, но не формирует файл и не сохраняет данные.
PHP и JavaScript должны использовать одинаковые имена полей и типы данных. Например, параметр params.reportId указывает обработчику, какой отчет обновить. Получателя PHP передает отдельно от данных команды.
Модулю нужны module_id и непустая command. Состав данных в params определяет приложение. Поля reportId и text нужны обработчику учебного примера, а не каждой Pull-команде. Передавайте значения явно и сохраняйте их типы.
|
|| Поле | Тип в примере | Назначение и значение ||
|| module_id | string | Идентификатор отправляющего модуля: example.reports. Совпадает с moduleId JS-подписки ||
|| command | string | Имя команды: reportReady. Совпадает с command JS-подписки ||
|| params | array | Данные для обработчика команды ||
|| params.reportId | int | Идентификатор учебного отчета: 42 ||
|| params.text | string | Текст для страницы: Отчет 42 готов. ||
|#
В примерах передавайте params с полями reportId и text. Для собственной команды без данных явно передайте 'params' => [] и используйте обработчик, которому эти поля не нужны.
Названия reportReady, reportId и text заданы в учебном приложении. Сохраните их в PHP и JavaScript. Если передать reportId строкой вместо числа, строгая проверка в обработчике может отклонить команду.
Проверяйте доступ на сервере до отправки:
-
Проверьте, вправе ли пользователь выполнить операцию. В форме проверьте авторизацию, права на действие и CSRF-токен. Токен связывает отправку формы с сессией посетителя. Добавьте и проверьте его по примеру из статьи Обновление страницы по команде сервера.
-
Получите список адресатов из данных приложения. Не принимайте переданный браузером идентификатор получателя как разрешение на отправку.
-
Проверьте право каждого адресата читать конкретный отчет и передаваемые поля.
-
Сформируйте команду с минимально необходимыми данными.
Проверка сессии не заменяет проверку прав на отчет. Фильтр moduleId, имя команды и условия в JavaScript тоже не защищают данные. Обработчик проверяет их уже после получения команды. Если получателям доступны разные поля, отправляйте каждой группе отдельную команду.
Что передавать в команде
Передавайте в params данные, которые нужны интерфейсу. Небольшое изменение можно показать сразу. Большой отчет или данные с ограниченным доступом лучше запросить отдельно через API приложения.
|
|| Вариант | Когда применять | Что делает клиент | Что учесть ||
|| Передать готовые данные | Небольшое изменение с одинаковыми правами у всех адресатов | Обновляет интерфейс из params | Получатель уже имеет данные, даже если интерфейс их не показал ||
|| Передать идентификатор | Большой отчет, разные права на поля, важна актуальность на момент чтения | Запрашивает состояние отчета по reportId | Сервер повторно проверяет доступ при каждом запросе ||
|| Передать идентификатор и версию | Возможны повторы и конкурирующие изменения | Сопоставляет версию с уже показанной и при необходимости перечитывает данные | Приложение само задает правила версий ||
|#
На странице /local/pull-demo/index.php из статьи Обновление страницы по команде сервера обработчик показывает строку из params.text. Если передать только reportId, измените обработчик так, чтобы он запросил состояние отчета. Запрос должен читать сохраненный результат, а не запускать формирование отчета заново.
Если данные могут измениться несколько раз, добавьте params.revision — неотрицательное целое число с версией отчета. Увеличивайте его при сохранении новой версии и возвращайте вместе с данными при чтении через API.
Сравнивайте версии только одного отчета. Номер версии задает приложение, а не Push and Pull. Чтение и проверку версии настройте по примеру из статьи Обработка команд в JavaScript.
Передавайте params как PHP-массив. В нем можно использовать строки, числа, логические значения, null и вложенные массивы. Объекты слоя работы с базой данных (ORM), файловые ресурсы и двоичные файлы сначала преобразуйте в нужные поля или замените идентификатором для чтения.
Используйте строки в кодировке UTF-8. Неверная кодировка приводит к false при добавлении команды. Если передать params не массивом, модуль заменит его пустым массивом. Поэтому не кодируйте весь params заранее через serialize() или в JSON.
Не включайте в команду весь отчет, если странице нужен только новый статус. В транспортный пакет входят данные команды, адресаты и служебные поля. Ограничение размера пакета нельзя считать допустимым размером params.
Модуль сам формирует пакеты и учитывает ограничения из таблицы. JSON-RPC и Protobuf — форматы обмена с сервером очередей.
|
|| Метод чтения настройки | Что ограничивает | Где применяется ||
|| CPullOptions::GetMaxPayload() | Размер транспортного пакета | JSON-RPC и Protobuf ||
|| CPullOptions::GetMaxMessagesPerRequest() | Количество сообщений в запросе | Protobuf ||
|| CPullOptions::GetCommandPerHit() | Количество каналов за обращение | Классическая отправка ||
|#
Отправка пользователю или списку пользователей
Для одного получателя используйте AddByUser. Чтобы отправить одну команду нескольким пользователям, передайте их идентификаторы в AddByUsers. В обоих случаях список получателей определяет приложение.
Отправить одному пользователю
В форме из статьи Обновление страницы по команде сервера замените вызов отправки следующим фрагментом. Он проверяет, принял ли модуль команду. Сохраните проверки авторизации и CSRF и подключение pull:
$recipientId = (int)$USER->GetID();
$accepted = \CPullStack::AddByUser($recipientId, [
'module_id' => 'example.reports',
'command' => 'reportReady',
'params' => [
'reportId' => 42,
'text' => 'Отчет 42 готов.',
],
]);
if ($accepted !== true)
{
throw new \RuntimeException('Команда не принята к отправке.');
}
Переменная $recipientId содержит идентификатор текущего авторизованного пользователя. Отправитель не берет его из формы. Перед отправкой откройте страницу получателя под той же учетной записью. После отправки проверьте текст и счетчик без перезагрузки страницы.
Ожидаемый результат: текст на странице изменился, счетчик полученных команд увеличился.
Команду получает пользователь, а не отдельная вкладка. Для проверки двух получателей используйте разные учетные записи и независимые профили браузера. Если нужно уведомлять наблюдателей объекта, настройте отправку по статье Подписки по тегам.
Отправить списку пользователей
Метод AddByUsers отправляет одну команду пользователям из массива идентификаторов. При публикации модуль определяет каналы получателей и формирует транспортные пакеты. Поэтому подбирать размер пакета в прикладном цикле не нужно.
Создайте /local/pull-demo/send-users.php на тестовом сайте. Страница отправляет одну команду двум пользователям. Доступ к форме есть только у администратора.
Замените 101 и 102 реальными идентификаторами тестовых пользователей. Под каждой учетной записью откройте страницу получателя /local/pull-demo/index.php. Форму отправителя откройте под администратором:
<?php
use Bitrix\Main\Loader;
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php';
$APPLICATION->SetTitle('Отправка команды группе пользователей');
// Укажите реальные идентификаторы тестовых пользователей
$recipientIds = [101, 102];
$recipientIds = array_values(array_unique($recipientIds));
if (!$USER->IsAdmin())
{
echo '<p>Тестовая отправка доступна администратору.</p>';
}
elseif (!Loader::includeModule('pull'))
{
echo '<p>Не удалось подключить модуль pull.</p>';
}
else
{
if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
if (!check_bitrix_sessid())
{
echo '<p>Обновите страницу и повторите отправку.</p>';
}
elseif ($recipientIds === [])
{
echo '<p>Список получателей пуст. Отправка пропущена.</p>';
}
else
{
$accepted = \CPullStack::AddByUsers($recipientIds, [
'module_id' => 'example.reports',
'command' => 'reportReady',
'params' => [
'reportId' => 42,
'text' => 'Отчет 42 готов.',
],
]);
echo $accepted === true
? '<p>Команда принята. Проверьте обоих получателей.</p>'
: '<p>Модуль не принял команду к отправке.</p>';
}
}
?>
<form method="post" action="/local/pull-demo/send-users.php">
<?= bitrix_sessid_post() ?>
<button type="submit">Отправить команду тестовым пользователям</button>
</form>
<?php
}
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php';
Исключите каталог с тестовыми страницами из кеширования HTML по статье Обновление страницы по команде сервера. После отправки обе страницы получателей должны показать готовность отчета. Форма отправки сама не принимает команды.
У учебного отчета нет отдельных прав доступа. В приложении сначала проверьте право каждого получателя читать отчет, затем сформируйте список. Доступ администратора к форме отправки не дает такого права остальным пользователям.
Если список получателей пуст, не вызывайте метод отправки. Не заменяйте его отправкой через AddShared. Удаление повторяющихся ID из списка не гарантирует доставку ровно один раз. Если получателям нужны разные данные, отправьте отдельные команды.
Отправка команд неавторизованному посетителю
Перед персональной отправкой команды гостю проверьте доступность его канала. Если модуль отклоняет отрицательный гостевой ID, адресная отправка гостю недоступна. Для личного отчета используйте авторизацию и положительный ID активной учетной записи.
Проверка персонального канала не определяет возможность получать общие команды. Гость с настроенным клиентом может получать их через общий канал, даже если персональный канал недоступен. Передавайте в общий канал только общедоступные данные.
Учесть ограничение персонального канала гостя
Если CPullChannel::Get() проверяет активность пользователя через UserTable для любого ненулевого ID, отрицательный ID гостя не проходит проверку. В этом случае метод не создает персональный канал. Присвоение гостю другого уникального номера не устраняет отказ.
Для персональной отправки сначала проверьте, принимает ли установленный модуль отрицательный ID. Отказ этой проверки не запрещает получение команд из общего канала.
Для авторизованного получателя используйте пример из раздела Отправка пользователю или списку пользователей. Следующий пример кода проверяет доступность гостевого канала через CPullChannel::Get(-1).
Создайте на тестовом сайте /local/pull-demo/check-guest.php и откройте под администратором. Страница проверяет, можно ли получить канал для отрицательного ID:
<?php
use Bitrix\Main\Loader;
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php';
if (!$USER->IsAdmin())
{
echo '<p>Проверка доступна администратору.</p>';
}
elseif (!Loader::includeModule('pull'))
{
echo '<p>Модуль pull недоступен.</p>';
}
elseif (!\CPullOptions::GetQueueServerStatus())
{
echo '<p>Сначала включите сервер очередей.</p>';
}
else
{
// Только проба канала: команда на этот адрес не отправляется
$channel = \CPullChannel::Get(-1);
echo is_array($channel)
? '<p>Канал получен. Далее проверьте идентификацию и доставку.</p>'
: '<p>Гостевой канал не получен. Персональную отправку не продолжайте.</p>';
}
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php';
Если модуль отклоняет отрицательный ID, страница покажет сообщение «Гостевой канал не получен». Если модуль поддерживает такой ID, вызов может создать или обновить канал. Поэтому запускайте проверку только на тестовой установке. Число -1 здесь служит для проверки. Не используйте его как общий адрес всех гостей.
Если метод вернул массив, канал получен. Далее нужно проверить, что приложение правильно определяет гостя и доставляет команду. Если метод вернул false на другом окружении, проверьте реализацию и ошибки. Само значение false не указывает причину отказа.
Подготовить идентификацию на совместимой реализации
Если установленный модуль поддерживает отрицательные адреса, присвойте каждому гостю уникальный положительный номер. Передавайте его в pull с минусом. Такой же отрицательный номер задайте в PULL_USER_ID до загрузки клиента.
Реализуйте определение гостя, хранение его ID и подключение клиентской страницы в приложении. Готового гостевого клиента в примере нет.
Подготовьте определение гостя и сохранение его ID между запросами:
-
Присвойте каждому гостю уникальный числовой ID и сохраните связь с проверенной сессией на сервере. Убедитесь, что другие способы определения гостей не выдают совпадающие номера.
-
При следующем запросе восстановите тот же ID по серверным данным. Не принимайте значение из GET или POST без проверки связи с сессией.
-
Задайте
PULL_USER_IDв обработчикеmain:OnPrologдо загрузки клиента. Пролог — начальный этап обработки страницы. Сортировка2позволяет вызвать обработчик раньше pull AJAX-обработчика с сортировкой3. Чем меньше число, тем раньше вызов. -
Восстанавливайте ID и в служебных запросах конфигурации клиента. Гостевая авторизация сравнивает присланный
pull_guest_idс сервернымPULL_USER_ID. Определять константу только на странице получателя недостаточно. -
Перед отправкой проверьте доступ гостя к отчету. Возьмите отрицательный ID из серверных данных и вызовите
AddByUser. Если команду отправляет фоновое задание, сохраните для него связь с получателем отдельно от браузерной сессии.
Модуль поддерживает два способа определить гостя. Автоматический способ использует модуль statistic, ID посетителя и опцию guest. При явном задании ID приложение определяет PULL_USER_ID, и модуль проверяет эту константу раньше. Включение опции guest не устраняет ограничение персонального канала.
Проверить изоляцию и смену авторизации
Если модуль поддерживает персональные каналы гостей, откройте сайт в двух независимых профилях браузера. Сначала отправьте каждому гостю его команду и подтвердите получение. Затем отправьте команду только первому. Второй не должен ее получить. Повторно откройте страницу в той же сессии и проверьте, что приложение восстановило прежний ID.
После входа на сайт используйте положительный ID учетной записи и заново инициализируйте клиент. После выхода восстановите гостевой ID по правилам приложения. Для контрольной проверки полностью перезагрузите страницу. PHP-константу нельзя переопределить в уже выполняющемся запросе. Проверьте также старые вкладки. Они должны перестать читать персональные данные прежнего пользователя.
Использование того же браузера после входа не дает права на гостевой отчет. Если отчет нужно перенести на учетную запись, отдельно проверьте право на такой перенос. При каждом чтении результата снова проверяйте доступ.
Общий канал работает отдельно от персонального. Метод Bitrix\Pull\Config::get() добавляет общий канал для отрицательного адреса, даже если персонального канала нет. Общий канал использует адрес 0, для которого проверка активности пользователя пропускается. Получение общей команды гостем не подтверждает, что ему можно отправить персональную.
Отправка в общий канал
Метод AddShared отправляет команду в общий канал сервера очередей. Список пользователей ему не нужен. Передавайте только данные, доступные всей аудитории канала. Без сервера очередей такая отправка не работает.
Команду из общего канала могут получить и гости с настроенным клиентом. Проверка имени модуля или отчета в JavaScript не защищает содержимое. Для получения нужен подключенный клиент. Отправка в общий канал не означает, что команду увидят все посетители, в том числе с закрытой страницей.
Создайте /local/pull-demo/send-shared.php. Страница отправляет общедоступный статус учебного отчета. Для получения команды откройте /local/pull-demo/index.php под тестовыми пользователями:
<?php
use Bitrix\Main\Loader;
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php';
$APPLICATION->SetTitle('Отправка команды в общий канал');
if (!$USER->IsAdmin())
{
echo '<p>Тестовая отправка доступна администратору.</p>';
}
elseif (!Loader::includeModule('pull'))
{
echo '<p>Не удалось подключить модуль pull.</p>';
}
else
{
if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
if (!check_bitrix_sessid())
{
echo '<p>Обновите страницу и повторите отправку.</p>';
}
else
{
$accepted = \CPullStack::AddShared([
'module_id' => 'example.reports',
'command' => 'reportReady',
'params' => [
'reportId' => 42,
'text' => 'Отчет 42 готов.',
],
]);
echo $accepted === true
? '<p>Команда принята. Проверьте получателей.</p>'
: '<p>Команда не принята. Проверьте параметры и сервер очередей.</p>';
}
}
?>
<form method="post" action="/local/pull-demo/send-shared.php">
<?= bitrix_sessid_post() ?>
<button type="submit">Опубликовать общедоступный статус</button>
</form>
<?php
}
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php';
Откройте страницы получателей под пользователями A, B и C. Отправьте команду под администратором.
Ожидаемый результат: все три подключенных клиента с обработчиком reportReady показали новый текст. Список адресатов для этой отправки не нужен.
Этот пример проверяется на авторизованных страницах получателей. Для получения общих команд гостем отдельно реализуйте его идентификацию и подключение клиента. Готовой гостевой страницы здесь нет. Успешная проверка персонального канала через CPullChannel::Get(-1) для получения общих команд не требуется.
Используйте общий канал для публичного статуса или общедоступного списка. Для личного отчета нужна адресная отправка. Даже идентификатор закрытого объекта может раскрывать сведения, недоступные всей аудитории.
Если обновление нужно только наблюдателям объекта, сначала проверьте их доступ на сервере. Затем настройте получателей по статье Подписки по тегам.
Момент отправки и контексты PHP
Сначала сохраните результат операции, затем отправьте команду о готовности. После вызова API завершите страницу или задачу подходящим способом. Вызов из середины скрипта еще не подтверждает, что команда дошла до клиента.
Согласовать команду с сохранением
Чтобы команда сообщала о сохраненном результате:
-
Проверьте права инициатора и входные данные.
-
Сохраните результат операции. Если используете транзакцию, дождитесь успешной фиксации всей операции, включая внешнюю транзакцию вызывающего кода.
-
Проверьте текущие права получателей и подготовьте данные из сохраненного результата.
-
Вызовите API отправки.
-
Завершите запрос или задачу способом, который подходит для страницы, AJAX, агента или CLI.
Не отправляйте команду «отчет готов» из безусловного блока finally. Этот блок выполнится и после ошибки. При откате транзакции команда об успехе не нужна.
Событие сохранения может произойти до окончательной фиксации всей операции. Выбирайте момент отправки с учетом порядка работы API, которое сохраняет данные.
Процесс может завершиться с ошибкой между сохранением результата и отправкой команды. Если пропуск уведомления недопустим, сохраняйте задание на отправку вместе с результатом операции. Приложение должно повторно обрабатывать неотправленные задания. Модуль pull не заменяет такой учет.
Повторная команда должна обновлять интерфейс, а не запускать операцию заново. Пример с фоновой задачей приведен в статье Обновление компонента после фоновой операции.
Завершить страницу или AJAX-обработчик
Эпилог — завершающий этап обработки страницы Bitrix. Метод Event::add обычно накапливает команды в памяти процесса. Затем через Application::addBackgroundJob регистрирует задачу, которая отправит их при завершении приложения. Это отложенная задача текущего процесса, а не отдельный агент или задание в базе данных.
Метод Application::terminate() вызывает завершающие события, выполняет отложенные задачи и завершает процесс. Метод CMain::FinalActions() передает HTTP-ответ и запускает штатное завершение приложения. Не заменяйте завершение страницы вызовом exit сразу после добавления команды.
|
|| Контекст | Как завершить выполнение | Что нельзя считать подтверждением доставки ||
|| Обычная страница | Подключить /bitrix/footer.php, как в учебных формах | Текст «Команда принята» ||
|| Самостоятельный AJAX-обработчик | Передать подготовленный ответ в CMain::FinalActions($output) | Успешный HTTP-ответ ||
|| Действие контроллера | Вернуть результат вызывающему коду и дать приложению штатно завершиться | Возврат из метода действия ||
|| Отдельный CLI-скрипт | После выполнения работы вызвать Application::getInstance()->terminate($exitCode) | Нулевой код завершения сам по себе ||
|#
Создайте /local/pull-demo/send-ajax.php. Обработчик отправляет команду текущему авторизованному пользователю. Файл prolog_before.php загружает окружение Bitrix без HTML-шаблона страницы:
<?php
use Bitrix\Main\Loader;
use Bitrix\Main\Web\Json;
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
$result = ['accepted' => false];
if ($_SERVER['REQUEST_METHOD'] !== 'POST')
{
http_response_code(405);
$result['error'] = 'Требуется POST-запрос.';
}
elseif (!$USER->IsAuthorized())
{
http_response_code(401);
$result['error'] = 'Требуется авторизация.';
}
elseif (!check_bitrix_sessid())
{
http_response_code(403);
$result['error'] = 'Проверка сессии не пройдена.';
}
elseif (!Loader::includeModule('pull'))
{
http_response_code(503);
$result['error'] = 'Модуль pull недоступен.';
}
else
{
$result['accepted'] = \CPullStack::AddByUser((int)$USER->GetID(), [
'module_id' => 'example.reports',
'command' => 'reportReady',
'params' => [
'reportId' => 42,
'text' => 'Отчет 42 готов.',
],
]) === true;
if (!$result['accepted'])
{
http_response_code(500);
$result['error'] = 'Команда не принята к отправке.';
}
}
header('Content-Type: application/json; charset=UTF-8');
\CMain::FinalActions(Json::encode($result));
На открытой странице получателя /local/pull-demo/index.php выполните в консоли браузера:
fetch('/local/pull-demo/send-ajax.php', {
method: 'POST',
credentials: 'same-origin',
body: new URLSearchParams({ sessid: BX.bitrix_sessid() })
})
.then(response => response.json())
.then(result => console.log(result));
Ожидаемый результат: в ответе появилось accepted: true, на странице изменились текст и счетчик. Ответ подтверждает подготовку команды, а изменение страницы — получение и обработку.
Повторите запрос без sessid. Обработчик должен вернуть отказ, а счетчик — остаться прежним. В приложении дополнительно проверяйте права на объект по правилам раздела Формат команды и проверка доступа.
Не вызывайте FinalActions() внутри действия контроллера. Этот метод завершает приложение целиком. Формирование ответа и завершение запроса оставьте контроллеру.
Отправить из агента
Агент — периодическая PHP-задача Bitrix. Он может выполняться при обращении к сайту или через скрипт, который запускает cron. Cron — системный планировщик для запуска задач по расписанию.
Момент отправки из агента зависит от констант, которые задает ядро. Если определена BX_CHECK_AGENT_START и еще не определена BX_WITH_ON_AFTER_EPILOG, метод Event::addEvent() сразу вызывает Event::send(). В остальных случаях модуль планирует отложенную отправку. Не задавайте эти константы вручную, чтобы ускорить отправку.
Внутри агента сохраните результат, выберите получателей из данных задания и вызовите AddByUser или AddByUsers. Не берите получателя из $USER->GetID(). Агент может выполняться не от имени пользователя, который запустил операцию.
После отправки верните управление планировщику. Вызов FinalActions() или terminate() внутри агента завершит весь запускающий процесс.
Проверьте отправку в том режиме, в котором будет работать агент. После обработки задания открытая страница получателя должна показать команду. Если запускаете агенты собственным CLI-скриптом, завершите весь скрипт по примеру из раздела Отправить из CLI. Регистрацию и расписание агента настройте отдельно.
Отправить из CLI
Для отправки из командной строки загрузите окружение Bitrix и завершите приложение после выполнения работы. В примере ID получателя передается аргументом команды. Идентификатор учебного отчета задан в коде.
Создайте /local/pull-demo/send-cli.php на тестовом сайте. Замените example.test именем этого сайта. Скрипт определяет корень сайта по своему расположению. Запускайте его на сервере, где доступен PHP для командной строки:
<?php
use Bitrix\Main\Application;
use Bitrix\Main\Loader;
if (PHP_SAPI !== 'cli')
{
http_response_code(404);
exit;
}
$recipient = $argv[1] ?? '';
if (!ctype_digit($recipient) || (int)$recipient <= 0)
{
fwrite(STDERR, "Передайте положительный ID тестового пользователя.\n");
exit(1);
}
$_SERVER['DOCUMENT_ROOT'] = dirname(__DIR__, 2);
$_SERVER['SERVER_NAME'] = 'example.test';
define('NOT_CHECK_PERMISSIONS', true);
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
$exitCode = 0;
try
{
if (!Loader::includeModule('pull'))
{
throw new \RuntimeException('Модуль pull недоступен.');
}
if (!\CPullOptions::GetQueueServerStatus())
{
throw new \RuntimeException('Сервер очередей выключен.');
}
$accepted = \CPullStack::AddByUser((int)$recipient, [
'module_id' => 'example.reports',
'command' => 'reportReady',
'params' => [
'reportId' => 42,
'text' => 'Отчет 42 готов.',
],
]);
if ($accepted !== true)
{
throw new \RuntimeException('Команда не принята к отправке.');
}
fwrite(STDOUT, "Команда принята. Проверьте страницу получателя.\n");
}
catch (\Throwable $exception)
{
fwrite(STDERR, $exception->getMessage() . "\n");
$exitCode = 1;
}
Application::getInstance()->terminate($exitCode);
Запустите из корня сайта, заменив 101 реальным ID активного тестового пользователя:
Перед запуском откройте /local/pull-demo/index.php под выбранным пользователем.
Ожидаемый результат. После завершения скрипта текст на странице изменился, счетчик увеличился. При запуске без аргумента скрипт должен показать ошибку и завершиться до загрузки Bitrix.
Проверка PHP_SAPI запрещает запуск через браузер. CLI-скрипт не использует браузерную авторизацию. Константа NOT_CHECK_PERMISSIONS, заданная до подключения пролога, отключает штатную проверку доступа, которая на закрытом сайте может вывести форму входа и завершить скрипт до отправки команды. Ограничьте доступ к запуску скрипта средствами сервера. В рабочей фоновой задаче дополнительно проверяйте права на операцию и доступ получателя к отчету. Переданный аргумент CLI не заменяет эти проверки.
Метод terminate() вызывается после блока try. Поэтому код выхода отражает результат подготовки команды, но не подтверждает ее публикацию. Проверяйте ошибки отправки в журнале PHP и получение команды на странице.
Если процесс должен продолжить работу после отправки, используйте \Bitrix\Pull\Event::send(). Этот метод отправляет все команды, накопленные в процессе к моменту вызова. Вызывайте его после сохранения результата операции. Завершать все приложение внутри цикла для этого не нужно.
Такой способ подходит для обычных команд с готовым params. Он не охватывает отложенное вычисление данных через callback и мобильные push-уведомления.
Значение true от send() не подтверждает доставку. Ошибки транспорта и ограничения этого результата разобраны в разделе Ошибки и проверка результата.
Ошибки и проверка результата
Проверяйте отдельно подготовку команды, отправку на сервер очередей и получение в браузере. Успех одного этапа не подтверждает следующий.
|
|| Наблюдение | Что подтверждает | Чего не подтверждает ||
|| Loader::includeModule('pull') вернул true | Модуль подключен в текущем PHP-контексте | Сервер очередей доступен ||
|| API вернул true для обычной команды | Команда прошла подготовку в модуле | Сообщение опубликовано или доставлено ||
|| Запрос отправителя завершился | Завершен HTTP-запрос | Нужный клиент получил команду ||
|| JS-обработчик получил ожидаемые параметры | Конкретная контрольная команда дошла до этого обработчика | Все другие получатели получили ее, отсутствуют пропуски и повторы ||
|| Страница показала результат | Обработчик применил данные к интерфейсу | Прикладная операция действительно сохранена, если это отдельно не проверено ||
|#
Методы CPullStack::AddByUser и AddByUsers передают аргументы в Bitrix\Pull\Event::add. Для обычной команды значение true означает, что модуль подготовил команду. Значение false означает, что подготовка не удалась. Отправка на сервер очередей может произойти позже. Если сервер выключен, модуль очищает накопленные команды на этапе отправки без доставки.
При нормальном завершении метод send() возвращает true, даже если транспорт зарегистрировал ошибки в TransportResult. О таких ошибках метод сообщает предупреждениями PHP.
Классический транспорт не записывает каждый сетевой отказ в TransportResult. Поэтому отсутствие предупреждения тоже не подтверждает доставку. Повторную отправку, восстановление состояния и учет заданий должно обеспечивать приложение.
|
|| Условие | Результат для примеров | Действие приложения ||
|| Не задан module_id, нет непустой command, неверный UTF-8 | false | Исправить формирование команды. Не повторять запрос с той же ошибкой ||
|| Пустой массив адресатов в AddByUsers | false | Пропустить отправку до вызова API, как в примере ||
|| Нельзя получить общий канал | AddShared возвращает false | Проверить настройки сервера и журнал ошибок ||
|| Команда подготовлена | true | Завершить запрос или задачу. Отдельно проверить получение ||
|| Ошибка транспорта зарегистрирована в TransportResult | Предупреждение PHP. При нормальном завершении send() возвращает true | Проверить журнал и сервер. Для классического транспорта отсутствие предупреждения не исключает сетевую ошибку ||
|#
Таблица относится к командам с готовыми данными, без push, user_params, dictionary и callback. Неверные PHP-типы и исключения вызываемых компонентов также могут прервать выполнение. Проверка на false не заменяет обработку исключений.
Если исключение возникнет при отложенной отправке, блок try вокруг добавления команды его не перехватит.
Модуль может объединить одинаковые команды и списки их получателей, если команды добавлены до одной отправки. Несколько вызовов API в одном запросе не обязательно приведут к такому же числу вызовов JavaScript-обработчика. Для проверки повторов отправляйте команды отдельными запросами.
Проверьте получателей на учебных данных. Записывайте время каждой отправки:
-
Сначала отправьте пользователю B контрольную команду и подтвердите, что его клиент работает. Затем отправьте команду пользователю A. У A должны измениться текст и счетчик, у B счетчик должен остаться прежним.
-
Отправьте команду списку
[A, B]. Проверьте оба интерфейса и третью учетную запись C, которая не входит в список. -
Задайте в тестовой форме пустой массив. Должно появиться сообщение о пропуске отправки, без вызова API.
-
Повторите отправку. Учебный счетчик должен снова увеличиться, но бизнес-операция не должна выполняться повторно.
-
Проверьте общий канал через
/local/pull-demo/send-shared.php. Команду должен получить и подключенный C. Для персонального гостевого сценария сначала выполните проверку совместимости, описанную выше. -
На изолированной тестовой установке сделайте сервер очередей недоступным. Запишите результат вызова и убедитесь, что команда не пришла. После восстановления отправьте новую команду. Ее получение не подтверждает доставку предыдущей.
После изменения прав исключите пользователя из получателей и попробуйте прочитать отчет от его имени. Сервер должен отказать в доступе. Данные, которые уже попали в браузер, нельзя отозвать командой.
Если команда не дошла, пройдите диагностику в статье Обновление страницы по команде сервера. Затем проверьте соединение по статье Настройка модуля и сервера очередей.
Настройте клиентский обработчик, первоначальное чтение и восстановление после разрыва связи по статье Обработка команд в JavaScript. Тестовые страницы удалите, когда они больше не нужны.