Дата последнего изменения: 13.11.2023
Задача: при изменении элемента инфоблока модифицировать другой элемент. Кейс может быть какой угодно - это и логирование, и деактивация основного товара, когда нет активных предложений, и изменение даты активности связанного элемента. Если создать обработчик, использующий метод CIBlockElement::Update, и повесить его на события OnBeforeIBlockElementUpdate / OnAfterIBlockElementUpdate, то:
вызов обработчика OnBeforeIBlockElementUpdate/OnAfterIBlockElementUpdate .... CIBlockElement::Update вызов обработчика OnBeforeIBlockElementUpdate/OnAfterIBlockElementUpdate .... CIBlockElement::Update ... итог - 500 (Internal Server Error)
Причина в том, что происходит рекурсивный вызов обработчика. Ниже приведен код, который избавляет от подобных проблем. В качестве примера взят обработчик OnAfterIBlockElementUpdate.
class myClass
{
protected static $handlerDisallow = false;
public static function iblockElementUpdateHandler(&$fields)
{
/* проверяем, что обработчик уже запущен */
if (self::$handlerDisallow)
return;
/* взводим флаг запуска */
self::$handlerDisallow = true;
/* наш код, приводящий к вызову CIBlockElement::Update */
...
CIBlockElement :: Update (..., ...);
/* вновь разрешаем запускать обработчик */
self::$handlerDisallow = false;
}
}
Пояснение. За основу взят класс, так как подобные решения в основном используются в собственных модулях. В классе имеется статическая булевая переменная - $handlerDisallow. По умолчанию она имеет значение false - нет запрета. В самом начале обработчика необходимо проверять ее значение. Если обработчик уже запущен, она будет равна true и выполнение необходимо прервать. Если же выполнять обработчик можно, необходимо присвоить этой переменной true на время выполнения всего обработчика. В конце необходимо флаг сбросить ($handlerDisallow), иначе до конца хита ваш обработчик не выполнится больше ни разу.
Если используется в качестве обработчика обычная функция, а не класс, то создайте статическую переменную внутри функции.
Можно дополнить класс возможностью блокировать работу обработчика "снаружи". Для этого измените тип переменной и добавьте три метода:
class myClass
{
protected static $handlerDisallow = 0;
public static function disableHandler()
{
self::$handlerDisallow--;
}
public static function enableHandler()
{
self::$handlerDisallow++;
}
public static function isEnabledHandler()
{
return (self::$handlerDisallow >= 0);
}
public static function iblockElementUpdateHandler(&$fields)
{
/* проверяем, что обработчик уже запущен */
if (!self::isEnabledHandler())
return;
/* взводим флаг запуска */
self::disableHandler();
/* наш код, приводящий к вызову CIBlockElement::Update */
...
CIBlockElement :: Update (..., ...);
/* вновь разрешаем запускать обработчик */
self::enableHandler();
}
}