Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/eventmanager.php |
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2023 Bitrix */ namespace Bitrix\Main; use Bitrix\Main\Type\Collection; class EventManager { protected const CACHE_ID = 'b_module_to_module'; /** * @var EventManager */ protected static $instance; protected $handlers = []; protected $isHandlersLoaded = false; protected function __construct() { } /** * @static * @return EventManager */ public static function getInstance() { if (!isset(self::$instance)) { $c = __CLASS__; self::$instance = new $c; } return self::$instance; } /** * @static * @param EventManager $instance */ public static function setInstance($instance) { $c = __CLASS__; if ($instance instanceof $c) { self::$instance = $instance; } } protected function addEventHandlerInternal($fromModuleId, $eventType, $callback, $includeFile, $sort, $version) { $arEvent = [ 'FROM_MODULE_ID' => $fromModuleId, 'MESSAGE_ID' => $eventType, 'CALLBACK' => $callback, 'SORT' => (int)$sort, 'FULL_PATH' => $includeFile, 'VERSION' => $version, 'TO_NAME' => $this->formatEventName(['CALLBACK' => $callback]), ]; $fromModuleId = strtoupper($fromModuleId); $eventType = strtoupper($eventType); if (!isset($this->handlers[$fromModuleId]) || !is_array($this->handlers[$fromModuleId])) { $this->handlers[$fromModuleId] = []; } $arEvents = &$this->handlers[$fromModuleId]; if (empty($arEvents[$eventType]) || !is_array($arEvents[$eventType])) { $arEvents[$eventType] = [$arEvent]; $iEventHandlerKey = 0; } else { $newEvents = []; $iEventHandlerKey = max(array_keys($arEvents[$eventType])) + 1; foreach ($arEvents[$eventType] as $key => $value) { if ($value['SORT'] > $arEvent['SORT']) { $newEvents[$iEventHandlerKey] = $arEvent; } $newEvents[$key] = $value; } $newEvents[$iEventHandlerKey] = $arEvent; $arEvents[$eventType] = $newEvents; } return $iEventHandlerKey; } public function addEventHandler($fromModuleId, $eventType, $callback, $includeFile = false, $sort = 100) { return $this->addEventHandlerInternal($fromModuleId, $eventType, $callback, $includeFile, $sort, 2); } /** * @param $fromModuleId * @param $eventType * @param $callback * @param bool $includeFile * @param int $sort * @return int */ public function addEventHandlerCompatible($fromModuleId, $eventType, $callback, $includeFile = false, $sort = 100) { return $this->addEventHandlerInternal($fromModuleId, $eventType, $callback, $includeFile, $sort, 1); } public function removeEventHandler($fromModuleId, $eventType, $iEventHandlerKey) { $fromModuleId = strtoupper($fromModuleId); $eventType = strtoupper($eventType); if (isset($this->handlers[$fromModuleId][$eventType][$iEventHandlerKey])) { unset($this->handlers[$fromModuleId][$eventType][$iEventHandlerKey]); return true; } return false; } public function unRegisterEventHandler($fromModuleId, $eventType, $toModuleId, $toClass = '', $toMethod = '', $toPath = '', $toMethodArg = []) { $toMethodArg = (!is_array($toMethodArg) || empty($toMethodArg) ? '' : serialize($toMethodArg)); $con = Application::getConnection(); $sqlHelper = $con->getSqlHelper(); $strSql = "DELETE FROM b_module_to_module " . "WHERE FROM_MODULE_ID='" . $sqlHelper->forSql($fromModuleId) . "'" . " AND MESSAGE_ID='" . $sqlHelper->forSql($eventType) . "' " . " AND TO_MODULE_ID='" . $sqlHelper->forSql($toModuleId) . "' " . (($toClass != '') ? " AND TO_CLASS='" . $sqlHelper->forSql($toClass) . "' " : " AND (TO_CLASS='' OR TO_CLASS IS NULL) ") . (($toMethod != '') ? " AND TO_METHOD='" . $sqlHelper->forSql($toMethod) . "'" : " AND (TO_METHOD='' OR TO_METHOD IS NULL) ") . (($toPath != '' && $toPath !== 1/*controller disconnect correction*/) ? " AND TO_PATH='" . $sqlHelper->forSql($toPath) . "'" : " AND (TO_PATH='' OR TO_PATH IS NULL) ") . (($toMethodArg != '') ? " AND TO_METHOD_ARG='" . $sqlHelper->forSql($toMethodArg) . "'" : " AND (TO_METHOD_ARG='' OR TO_METHOD_ARG IS NULL) "); $con->queryExecute($strSql); $this->clearLoadedHandlers(); } public function registerEventHandler($fromModuleId, $eventType, $toModuleId, $toClass = '', $toMethod = '', $sort = 100, $toPath = '', $toMethodArg = []) { $this->registerEventHandlerInternal($fromModuleId, $eventType, $toModuleId, $toClass, $toMethod, $sort, $toPath, $toMethodArg, 2); } public function registerEventHandlerCompatible($fromModuleId, $eventType, $toModuleId, $toClass = '', $toMethod = '', $sort = 100, $toPath = '', $toMethodArg = []) { $this->registerEventHandlerInternal($fromModuleId, $eventType, $toModuleId, $toClass, $toMethod, $sort, $toPath, $toMethodArg, 1); } protected function registerEventHandlerInternal($fromModuleId, $eventType, $toModuleId, $toClass, $toMethod, $sort, $toPath, $toMethodArg, $version) { $toMethodArg = (!is_array($toMethodArg) || empty($toMethodArg) ? '' : serialize($toMethodArg)); $sort = intval($sort); $version = intval($version); $uniqueID = md5(mb_strtolower($fromModuleId . '.' . $eventType . '.' . $toModuleId . '.' . $toPath . '.' . $toClass . '.' . $toMethod . '.' . $toMethodArg . '.' . $version)); $connection = Application::getConnection(); $sqlHelper = $connection->getSqlHelper(); $fromModuleId = $sqlHelper->forSql($fromModuleId); $eventType = $sqlHelper->forSql($eventType); $toModuleId = $sqlHelper->forSql($toModuleId); $toClass = $sqlHelper->forSql($toClass); $toMethod = $sqlHelper->forSql($toMethod); $toPath = $sqlHelper->forSql($toPath); $toMethodArg = $sqlHelper->forSql($toMethodArg); $fields = '(SORT, FROM_MODULE_ID, MESSAGE_ID, TO_MODULE_ID, TO_CLASS, TO_METHOD, TO_PATH, TO_METHOD_ARG, VERSION, UNIQUE_ID)'; $values = "(" . $sort . ", '" . $fromModuleId . "', '" . $eventType . "', '" . $toModuleId . "', " . " '" . $toClass . "', '" . $toMethod . "', '" . $toPath . "', '" . $toMethodArg . "', " . $version . ", '" . $uniqueID . "')"; $sql = $sqlHelper->getInsertIgnore('b_module_to_module', $fields, 'VALUES ' . $values); $connection->queryExecute($sql); $this->clearLoadedHandlers(); } protected function formatEventName($arEvent) { $strName = ''; if (isset($arEvent['CALLBACK'])) { if (is_array($arEvent['CALLBACK'])) { $strName .= (is_object($arEvent['CALLBACK'][0]) ? get_class($arEvent['CALLBACK'][0]) : $arEvent['CALLBACK'][0]) . '::' . $arEvent['CALLBACK'][1]; } elseif (is_callable($arEvent['CALLBACK'])) { $strName .= 'callable'; } else { $strName .= $arEvent['CALLBACK']; } } else { $strName .= $arEvent['TO_CLASS'] . '::' . $arEvent['TO_METHOD']; } if (!empty($arEvent['TO_MODULE_ID'])) { $strName .= ' (' . $arEvent['TO_MODULE_ID'] . ')'; } return $strName; } protected function loadEventHandlers() { $cache = Application::getInstance()->getManagedCache(); if ($cache->read(3600, self::CACHE_ID, self::CACHE_ID)) { $rawEvents = $cache->get(self::CACHE_ID); if (!is_array($rawEvents)) { $rawEvents = []; } } else { $con = Application::getConnection(); $rs = $con->query(" SELECT FROM_MODULE_ID, MESSAGE_ID, SORT, TO_MODULE_ID, TO_PATH, TO_CLASS, TO_METHOD, TO_METHOD_ARG, VERSION FROM b_module_to_module m2m INNER JOIN b_module m ON (m2m.TO_MODULE_ID = m.ID) ORDER BY SORT "); $rawEvents = $rs->fetchAll(); $cache->set(self::CACHE_ID, $rawEvents); } $handlers = $this->handlers; $hasHandlers = !empty($this->handlers); foreach ($rawEvents as $ar) { $ar['TO_NAME'] = $this->formatEventName([ 'TO_MODULE_ID' => $ar['TO_MODULE_ID'], 'TO_CLASS' => $ar['TO_CLASS'], 'TO_METHOD' => $ar['TO_METHOD'], ]); $ar['FROM_MODULE_ID'] = strtoupper($ar['FROM_MODULE_ID']); $ar['MESSAGE_ID'] = strtoupper($ar['MESSAGE_ID']); if ($ar['TO_METHOD_ARG'] != '') { $ar['TO_METHOD_ARG'] = unserialize($ar['TO_METHOD_ARG'], ['allowed_classes' => false]); } else { $ar['TO_METHOD_ARG'] = []; } $this->handlers[$ar['FROM_MODULE_ID']][$ar['MESSAGE_ID']][] = [ 'SORT' => (int)$ar['SORT'], 'TO_MODULE_ID' => $ar['TO_MODULE_ID'], 'TO_PATH' => $ar['TO_PATH'], 'TO_CLASS' => $ar['TO_CLASS'], 'TO_METHOD' => $ar['TO_METHOD'], 'TO_METHOD_ARG' => $ar['TO_METHOD_ARG'], 'VERSION' => $ar['VERSION'], 'TO_NAME' => $ar['TO_NAME'], 'FROM_DB' => true, ]; } if ($hasHandlers) { // need to re-sort because of AddEventHandler() calls (before loadEventHandlers) foreach (array_keys($handlers) as $moduleId) { foreach (array_keys($handlers[$moduleId]) as $event) { Collection::sortByColumn( $this->handlers[$moduleId][$event], ['SORT' => SORT_ASC], '', null, true ); } } } $this->isHandlersLoaded = true; } public function clearLoadedHandlers() { $managedCache = Application::getInstance()->getManagedCache(); $managedCache->clean(self::CACHE_ID, self::CACHE_ID); foreach ($this->handlers as $module => $types) { foreach ($types as $type => $events) { foreach ($events as $i => $event) { if (isset($event['FROM_DB']) && $event['FROM_DB']) { unset($this->handlers[$module][$type][$i]); } } } } $this->isHandlersLoaded = false; } public function findEventHandlers($eventModuleId, $eventType, array $filter = null) { if (!$this->isHandlersLoaded) { $this->loadEventHandlers(); } $eventModuleId = strtoupper($eventModuleId); $eventType = strtoupper($eventType); if (!isset($this->handlers[$eventModuleId]) || !isset($this->handlers[$eventModuleId][$eventType])) { return []; } $handlers = $this->handlers[$eventModuleId][$eventType]; if (!is_array($handlers)) { return []; } if (is_array($filter) && !empty($filter)) { $handlersTmp = $handlers; $handlers = []; foreach ($handlersTmp as $handler) { if (isset($handler['TO_MODULE_ID']) && in_array($handler['TO_MODULE_ID'], $filter)) { $handlers[] = $handler; } } } return $handlers; } public function send(Event $event) { $handlers = $this->findEventHandlers($event->getModuleId(), $event->getEventType(), $event->getFilter()); foreach ($handlers as $handler) { $this->sendToEventHandler($handler, $event); } } protected function sendToEventHandler(array $handler, Event $event) { try { $result = true; $includeResult = true; $event->addDebugInfo($handler); if (!empty($handler['TO_MODULE_ID']) && ($handler['TO_MODULE_ID'] != 'main')) { $result = Loader::includeModule($handler['TO_MODULE_ID']); } elseif (!empty($handler['TO_PATH'])) { $path = ltrim($handler['TO_PATH'], '/'); if (($path = Loader::getLocal($path)) !== false) { $includeResult = include_once($path); } } elseif (!empty($handler['FULL_PATH']) && IO\File::isFileExists($handler['FULL_PATH'])) { $includeResult = include_once($handler['FULL_PATH']); } $event->addDebugInfo($result); if ($result) { if (!empty($handler['TO_METHOD_ARG']) && is_array($handler['TO_METHOD_ARG'])) { $args = $handler['TO_METHOD_ARG']; } else { $args = []; } if ($handler['VERSION'] > 1) { $args[] = $event; } else { $args = array_merge($args, array_values($event->getParameters())); } $callback = null; if (isset($handler['CALLBACK'])) { $callback = $handler['CALLBACK']; } elseif (!empty($handler['TO_CLASS']) && !empty($handler['TO_METHOD']) && class_exists($handler['TO_CLASS'])) { $callback = [$handler['TO_CLASS'], $handler['TO_METHOD']]; } if ($callback != null) { $result = call_user_func_array($callback, $args); } else { $result = $includeResult; } if (($result != null) && !($result instanceof EventResult)) { $result = new EventResult(EventResult::UNDEFINED, $result, $handler['TO_MODULE_ID'] ?? null); } $event->addDebugInfo($result); if ($result != null) { $event->addResult($result); } } } catch (\Exception $ex) { if ($event->isDebugOn()) { $event->addException($ex); } else { throw $ex; } } } }