injectableFactory = $injectableFactory; $this->fileManager = $fileManager; $this->metadata = $metadata; $this->config = $config; $this->dataCache = $dataCache; $this->log = $log; $this->pathProvider = $pathProvider; } public function process( string $scope, string $hookName, $injection = null, array $options = [], array $hookData = [] ): void { if ($this->isDisabled) { return; } if (!isset($this->data)) { $this->loadHooks(); } $hookList = $this->getHookList($scope, $hookName); if (empty($hookList)) { return; } foreach ($hookList as $className) { if (empty($this->hooks[$className])) { $this->hooks[$className] = $this->createHookByClassName($className); } $hook = $this->hooks[$className]; $hook->$hookName($injection, $options, $hookData); } } /** * Disable hook processing. */ public function disable(): void { $this->isDisabled = true; } /** * Enable hook processing. */ public function enable(): void { $this->isDisabled = false; } private function loadHooks(): void { if ($this->config->get('useCache') && $this->dataCache->has($this->cacheKey)) { $this->data = $this->dataCache->get($this->cacheKey); return; } $metadata = $this->metadata; $data = $this->readHookData($this->pathProvider->getCustom() . 'Hooks'); foreach ($metadata->getModuleList() as $moduleName) { $modulePath = $this->pathProvider->getModule($moduleName) . 'Hooks'; $data = $this->readHookData($modulePath, $data); } $data = $this->readHookData($this->pathProvider->getCore() . 'Hooks', $data); $this->data = $this->sortHooks($data); if ($this->config->get('useCache')) { $this->dataCache->store($this->cacheKey, $this->data); } } private function createHookByClassName(string $className): object { if (!class_exists($className)) { $this->log->error("Hook class '{$className}' does not exist."); } $obj = $this->injectableFactory->create($className); return $obj; } private function readHookData(string $hookDir, array $hookData = []): array { if (!$this->fileManager->exists($hookDir)) { return $hookData; } $fileList = $this->fileManager->getFileList($hookDir, 1, '\.php$', true); foreach ($fileList as $scopeName => $hookFiles) { $hookScopeDirPath = Util::concatPath($hookDir, $scopeName); $normalizedScopeName = Util::normilizeScopeName($scopeName); foreach ($hookFiles as $hookFile) { $hookFilePath = Util::concatPath($hookScopeDirPath, $hookFile); $className = Util::getClassName($hookFilePath); $classMethods = get_class_methods($className); $hookMethods = array_diff($classMethods, $this->ignoredMethodList); $hookMethods = array_filter($hookMethods, function ($item) { if (strpos($item, 'set') === 0) { return false; } return true; }); foreach ($hookMethods as $hookType) { $entityHookData = $hookData[$normalizedScopeName][$hookType] ?? []; if ($this->hookExists($className, $entityHookData)) { continue; } $hookData[$normalizedScopeName][$hookType][] = [ 'className' => $className, 'order' => $className::$order ?? self::DEFAULT_ORDER, ]; } } } return $hookData; } /** * Sort hooks by the order parameter. */ private function sortHooks(array $hooks): array { foreach ($hooks as &$scopeHooks) { foreach ($scopeHooks as &$hookList) { usort($hookList, [$this, 'cmpHooks']); } } return $hooks; } /** * Get sorted hook list. */ private function getHookList(string $scope, string $hookName): array { $key = $scope . '_' . $hookName; if (!isset($this->hookListHash[$key])) { $hookList = []; if (isset($this->data['Common'][$hookName])) { $hookList = $this->data['Common'][$hookName]; } if (isset($this->data[$scope][$hookName])) { $hookList = array_merge($hookList, $this->data[$scope][$hookName]); usort($hookList, array($this, 'cmpHooks')); } $normalizedList = []; foreach ($hookList as $hookData) { $normalizedList[] = $hookData['className']; } $this->hookListHash[$key] = $normalizedList; } return $this->hookListHash[$key]; } /** * Check if hook exists in the list. */ private function hookExists(string $className, array $hookData): bool { $class = preg_replace('/^.*\\\(.*)$/', '$1', $className); foreach ($hookData as $hookData) { if (preg_match('/\\\\'.$class.'$/', $hookData['className'])) { return true; } } return false; } private function cmpHooks($a, $b): int { if ($a['order'] == $b['order']) { return 0; } return ($a['order'] < $b['order']) ? -1 : 1; } }