serializer = $serializer ?: $objectManager->get(Serialize::class); parent::__construct($reader, $configScope, $cache, $cacheId, $this->serializer); $this->_omConfig = $omConfig; $this->_relations = $relations; $this->_definitions = $definitions; $this->_classDefinitions = $classDefinitions; $this->_scopePriorityScheme = $scopePriorityScheme; $this->_objectManager = $objectManager; } /** * Collect parent types configuration for requested type * * @param string $type * @return array * @throws \InvalidArgumentException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _inheritPlugins($type) { $type = ltrim($type, '\\'); if (!array_key_exists($type, $this->_inherited)) { $realType = $this->_omConfig->getOriginalInstanceType($type); if ($realType !== $type) { $plugins = $this->_inheritPlugins($realType); } elseif ($this->_relations->has($type)) { $relations = $this->_relations->getParents($type); $plugins = []; foreach ($relations as $relation) { if ($relation) { $relationPlugins = $this->_inheritPlugins($relation); if ($relationPlugins) { $plugins = array_replace_recursive($plugins, $relationPlugins); } } } } else { $plugins = []; } if (isset($this->_data[$type])) { if (!$plugins) { $plugins = $this->_data[$type]; } else { $plugins = array_replace_recursive($plugins, $this->_data[$type]); } } $this->_inherited[$type] = null; if (is_array($plugins) && count($plugins)) { $this->filterPlugins($plugins); uasort($plugins, [$this, '_sort']); $this->trimInstanceStartingBackslash($plugins); $this->_inherited[$type] = $plugins; $lastPerMethod = []; foreach ($plugins as $key => $plugin) { // skip disabled plugins if (isset($plugin['disabled']) && $plugin['disabled']) { unset($plugins[$key]); continue; } $pluginType = $this->_omConfig->getOriginalInstanceType($plugin['instance']); if (!class_exists($pluginType)) { throw new \InvalidArgumentException('Plugin class ' . $pluginType . ' doesn\'t exist'); } foreach ($this->_definitions->getMethodList($pluginType) as $pluginMethod => $methodTypes) { $current = isset($lastPerMethod[$pluginMethod]) ? $lastPerMethod[$pluginMethod] : '__self'; $currentKey = $type . '_' . $pluginMethod . '_' . $current; if ($methodTypes & DefinitionInterface::LISTENER_AROUND) { $this->_processed[$currentKey][DefinitionInterface::LISTENER_AROUND] = $key; $lastPerMethod[$pluginMethod] = $key; } if ($methodTypes & DefinitionInterface::LISTENER_BEFORE) { $this->_processed[$currentKey][DefinitionInterface::LISTENER_BEFORE][] = $key; } if ($methodTypes & DefinitionInterface::LISTENER_AFTER) { $this->_processed[$currentKey][DefinitionInterface::LISTENER_AFTER][] = $key; } } } } return $plugins; } return $this->_inherited[$type]; } /** * Trims starting backslash from plugin instance name * * @param array $plugins * @return void */ private function trimInstanceStartingBackslash(&$plugins) { foreach ($plugins as &$plugin) { $plugin['instance'] = ltrim($plugin['instance'], '\\'); } } /** * Sort items * * @param array $itemA * @param array $itemB * @return int */ protected function _sort($itemA, $itemB) { if (isset($itemA['sortOrder'])) { if (isset($itemB['sortOrder'])) { return $itemA['sortOrder'] - $itemB['sortOrder']; } return $itemA['sortOrder']; } elseif (isset($itemB['sortOrder'])) { return (0 - (int)$itemB['sortOrder']); } else { return 0; } } /** * Retrieve plugin Instance * * @param string $type * @param string $code * @return mixed */ public function getPlugin($type, $code) { if (!isset($this->_pluginInstances[$type][$code])) { $this->_pluginInstances[$type][$code] = $this->_objectManager->get( $this->_inherited[$type][$code]['instance'] ); } return $this->_pluginInstances[$type][$code]; } /** * Retrieve next plugins in chain * * @param string $type * @param string $method * @param string $code * @return array */ public function getNext($type, $method, $code = '__self') { $this->_loadScopedData(); if (!isset($this->_inherited[$type]) && !array_key_exists($type, $this->_inherited)) { $this->_inheritPlugins($type); } $key = $type . '_' . lcfirst($method) . '_' . $code; return $this->_processed[$key] ?? null; } /** * Load configuration for current scope * * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function _loadScopedData() { $scope = $this->_configScope->getCurrentScope(); if (false == isset($this->_loadedScopes[$scope])) { if (false == in_array($scope, $this->_scopePriorityScheme)) { $this->_scopePriorityScheme[] = $scope; } $cacheId = implode('|', $this->_scopePriorityScheme) . "|" . $this->_cacheId; $data = $this->_cache->load($cacheId); if ($data) { list($this->_data, $this->_inherited, $this->_processed) = $this->serializer->unserialize($data); foreach ($this->_scopePriorityScheme as $scopeCode) { $this->_loadedScopes[$scopeCode] = true; } } else { $virtualTypes = []; foreach ($this->_scopePriorityScheme as $scopeCode) { if (false == isset($this->_loadedScopes[$scopeCode])) { $data = $this->_reader->read($scopeCode) ?: []; unset($data['preferences']); if (count($data) > 0) { $this->_inherited = []; $this->_processed = []; $this->merge($data); foreach ($data as $class => $config) { if (isset($config['type'])) { $virtualTypes[] = $class; } } } $this->_loadedScopes[$scopeCode] = true; } if ($this->isCurrentScope($scopeCode)) { break; } } foreach ($virtualTypes as $class) { $this->_inheritPlugins($class); } foreach ($this->getClassDefinitions() as $class) { $this->_inheritPlugins($class); } $this->_cache->save( $this->serializer->serialize([$this->_data, $this->_inherited, $this->_processed]), $cacheId ); } $this->_pluginInstances = []; } } /** * Whether scope code is current scope code * * @param string $scopeCode * @return bool */ protected function isCurrentScope($scopeCode) { return $this->_configScope->getCurrentScope() == $scopeCode; } /** * Returns class definitions * * @return array */ protected function getClassDefinitions() { return $this->_classDefinitions->getClasses(); } /** * Merge configuration * * @param array $config * @return void */ public function merge(array $config) { foreach ($config as $type => $typeConfig) { if (isset($typeConfig['plugins'])) { $type = ltrim($type, '\\'); if (isset($this->_data[$type])) { $this->_data[$type] = array_replace_recursive($this->_data[$type], $typeConfig['plugins']); } else { $this->_data[$type] = $typeConfig['plugins']; } } } } /** * Remove from list not existing plugins * * @param array $plugins * @return void */ private function filterPlugins(array &$plugins) { foreach ($plugins as $name => $plugin) { if (empty($plugin['instance'])) { unset($plugins[$name]); $this->getLogger()->info("Reference to undeclared plugin with name '{$name}'."); } } } /** * Get logger * * @return \Psr\Log\LoggerInterface * @deprecated 101.0.0 */ private function getLogger() { if ($this->logger === null) { $this->logger = $this->_objectManager->get(\Psr\Log\LoggerInterface::class); } return $this->logger; } }