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

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

Просмотров: 32577
Дата последнего изменения: 13.11.2023
Роберт Басыров
Сложность урока:
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();
	}
}

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

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