293  /  382
Справочник

Зацикливание обработчиков событий

Просмотров: 17265
Дата последнего изменения: 12.08.2020
Роберт Басыров
Сложность урока:
2 уровень - несложные понятия и действия, но не расслабляйтесь.
1
2
3
4
5
Недоступно в лицензиях:
Ограничений нет

Задача: при изменении элемента инфоблока модифицировать другой элемент. Кейс может быть какой угодно - это и логирование, и деактивация основного товара, когда нет активных предложений, и изменение даты активности связанного элемента. Если создать обработчик, использующий метод 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();
   }
}

Курсы разработаны в компании «1С-Битрикс»

Если вы нашли неточность в тексте, непонятное объяснение, пожалуйста, сообщите нам об этом в комментариях.
Развернуть комментарии