vendor/shopware/core/Framework/Plugin/KernelPluginLoader/KernelPluginLoader.php line 94

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Plugin\KernelPluginLoader;
  3. use Composer\Autoload\ClassLoader;
  4. use Composer\Autoload\ClassMapGenerator;
  5. use Shopware\Core\Framework\Log\Package;
  6. use Shopware\Core\Framework\Parameter\AdditionalBundleParameters;
  7. use Shopware\Core\Framework\Plugin;
  8. use Shopware\Core\Framework\Plugin\Exception\KernelPluginLoaderException;
  9. use Shopware\Core\Framework\Plugin\KernelPluginCollection;
  10. use Symfony\Component\DependencyInjection\ContainerBuilder;
  11. use Symfony\Component\DependencyInjection\Definition;
  12. use Symfony\Component\DependencyInjection\Reference;
  13. use Symfony\Component\HttpKernel\Bundle\Bundle;
  14. #[Package('core')]
  15. abstract class KernelPluginLoader extends Bundle
  16. {
  17.     protected array $pluginInfos = [];
  18.     private ClassLoader $classLoader;
  19.     private KernelPluginCollection $pluginInstances;
  20.     private string $pluginDir;
  21.     private bool $initialized false;
  22.     /**
  23.      * @internal
  24.      */
  25.     public function __construct(ClassLoader $classLoader, ?string $pluginDir null)
  26.     {
  27.         $this->classLoader $classLoader;
  28.         $this->pluginDir $pluginDir ?? 'custom/plugins';
  29.         $this->pluginInstances = new KernelPluginCollection();
  30.     }
  31.     final public function getPluginDir(string $projectDir): string
  32.     {
  33.         // absolute path
  34.         if (mb_strpos($this->pluginDir'/') === 0) {
  35.             return $this->pluginDir;
  36.         }
  37.         return $projectDir '/' $this->pluginDir;
  38.     }
  39.     /**
  40.      * Basic information required for instantiating the plugins
  41.      */
  42.     final public function getPluginInfos(): array
  43.     {
  44.         return $this->pluginInfos;
  45.     }
  46.     /**
  47.      * Instances of the plugin bundle classes
  48.      */
  49.     final public function getPluginInstances(): KernelPluginCollection
  50.     {
  51.         return $this->pluginInstances;
  52.     }
  53.     final public function getBundles(array $kernelParameters = [], array $loadedBundles = []): iterable
  54.     {
  55.         if (!$this->initialized) {
  56.             return;
  57.         }
  58.         foreach ($this->pluginInstances->getActives() as $plugin) {
  59.             $copy = new KernelPluginCollection($this->getPluginInstances()->all());
  60.             $additionalBundleParameters = new AdditionalBundleParameters($this->classLoader$copy$kernelParameters);
  61.             $additionalBundles $plugin->getAdditionalBundles($additionalBundleParameters);
  62.             [$preLoaded$postLoaded] = $this->splitBundlesIntoPreAndPost($additionalBundles);
  63.             foreach ([...\array_values($preLoaded), $plugin, ...\array_values($postLoaded)] as $bundle) {
  64.                 if (!\in_array($bundle->getName(), $loadedBundlestrue)) {
  65.                     yield $bundle;
  66.                     $loadedBundles[] = $bundle->getName();
  67.                 }
  68.             }
  69.         }
  70.         if (!\in_array($this->getName(), $loadedBundlestrue)) {
  71.             yield $this;
  72.         }
  73.     }
  74.     /**
  75.      * @throws KernelPluginLoaderException
  76.      */
  77.     final public function initializePlugins(string $projectDir): void
  78.     {
  79.         if ($this->initialized) {
  80.             return;
  81.         }
  82.         $this->loadPluginInfos();
  83.         if (empty($this->pluginInfos)) {
  84.             $this->initialized true;
  85.             return;
  86.         }
  87.         $this->registerPluginNamespaces($projectDir);
  88.         $this->instantiatePlugins($projectDir);
  89.         $this->initialized true;
  90.     }
  91.     final public function build(ContainerBuilder $container): void
  92.     {
  93.         if (!$this->initialized) {
  94.             return;
  95.         }
  96.         parent::build($container);
  97.         /*
  98.          * Register every plugin in the di container, enable autowire and set public
  99.          */
  100.         foreach ($this->pluginInstances->getActives() as $plugin) {
  101.             $class \get_class($plugin);
  102.             $definition = new Definition();
  103.             if ($container->hasDefinition($class)) {
  104.                 $definition $container->getDefinition($class);
  105.             }
  106.             $definition->setFactory([new Reference(self::class), 'getPluginInstance']);
  107.             $definition->addArgument($class);
  108.             $definition->setAutowired(true);
  109.             $definition->setPublic(true);
  110.             $container->setDefinition($class$definition);
  111.         }
  112.     }
  113.     final public function getPluginInstance(string $class): ?Plugin
  114.     {
  115.         $plugin $this->pluginInstances->get($class);
  116.         if (!$plugin || !$plugin->isActive()) {
  117.             return null;
  118.         }
  119.         return $plugin;
  120.     }
  121.     public function getClassLoader(): ClassLoader
  122.     {
  123.         return $this->classLoader;
  124.     }
  125.     abstract protected function loadPluginInfos(): void;
  126.     /**
  127.      * @throws KernelPluginLoaderException
  128.      */
  129.     private function registerPluginNamespaces(string $projectDir): void
  130.     {
  131.         foreach ($this->pluginInfos as $plugin) {
  132.             $pluginName $plugin['name'] ?? $plugin['baseClass'];
  133.             // plugins managed by composer are already in the classMap
  134.             if ($plugin['managedByComposer']) {
  135.                 continue;
  136.             }
  137.             if (!isset($plugin['autoload'])) {
  138.                 $reason sprintf(
  139.                     'Unable to register plugin "%s" in autoload. Required property `autoload` missing.',
  140.                     $plugin['baseClass']
  141.                 );
  142.                 throw new KernelPluginLoaderException($pluginName$reason);
  143.             }
  144.             $psr4 $plugin['autoload']['psr-4'] ?? [];
  145.             $psr0 $plugin['autoload']['psr-0'] ?? [];
  146.             if (empty($psr4) && empty($psr0)) {
  147.                 $reason sprintf(
  148.                     'Unable to register plugin "%s" in autoload. Required property `psr-4` or `psr-0` missing in property autoload.',
  149.                     $plugin['baseClass']
  150.                 );
  151.                 throw new KernelPluginLoaderException($pluginName$reason);
  152.             }
  153.             foreach ($psr4 as $namespace => $paths) {
  154.                 if (\is_string($paths)) {
  155.                     $paths = [$paths];
  156.                 }
  157.                 $mappedPaths $this->mapPsrPaths($pluginName$paths$projectDir$plugin['path']);
  158.                 $this->classLoader->addPsr4($namespace$mappedPaths);
  159.                 if ($this->classLoader->isClassMapAuthoritative()) {
  160.                     foreach ($mappedPaths as $mappedPath) {
  161.                         $this->classLoader->addClassMap(ClassMapGenerator::createMap($mappedPath));
  162.                     }
  163.                 }
  164.             }
  165.             foreach ($psr0 as $namespace => $paths) {
  166.                 if (\is_string($paths)) {
  167.                     $paths = [$paths];
  168.                 }
  169.                 $mappedPaths $this->mapPsrPaths($pluginName$paths$projectDir$plugin['path']);
  170.                 $this->classLoader->add($namespace$mappedPaths);
  171.                 if ($this->classLoader->isClassMapAuthoritative()) {
  172.                     foreach ($mappedPaths as $mappedPath) {
  173.                         $this->classLoader->addClassMap(ClassMapGenerator::createMap($mappedPath));
  174.                     }
  175.                 }
  176.             }
  177.         }
  178.     }
  179.     /**
  180.      * @throws KernelPluginLoaderException
  181.      */
  182.     private function mapPsrPaths(string $plugin, array $psrstring $projectDirstring $pluginRootPath): array
  183.     {
  184.         $mappedPaths = [];
  185.         $absolutePluginRootPath $this->getAbsolutePluginRootPath($projectDir$pluginRootPath);
  186.         if (mb_strpos($absolutePluginRootPath$projectDir) !== 0) {
  187.             throw new KernelPluginLoaderException(
  188.                 $plugin,
  189.                 sprintf('Plugin dir %s needs to be a sub-directory of the project dir %s'$pluginRootPath$projectDir)
  190.             );
  191.         }
  192.         foreach ($psr as $path) {
  193.             $mappedPaths[] = $absolutePluginRootPath '/' $path;
  194.         }
  195.         return $mappedPaths;
  196.     }
  197.     private function getAbsolutePluginRootPath(string $projectDirstring $pluginRootPath): string
  198.     {
  199.         // is relative path
  200.         if (mb_strpos($pluginRootPath'/') !== 0) {
  201.             $pluginRootPath $projectDir '/' $pluginRootPath;
  202.         }
  203.         return $pluginRootPath;
  204.     }
  205.     /**
  206.      * @throws KernelPluginLoaderException
  207.      */
  208.     private function instantiatePlugins(string $projectDir): void
  209.     {
  210.         foreach ($this->pluginInfos as $pluginData) {
  211.             $className $pluginData['baseClass'];
  212.             $pluginClassFilePath $this->classLoader->findFile($className);
  213.             if (!class_exists($className) || !$pluginClassFilePath || !file_exists($pluginClassFilePath)) {
  214.                 continue;
  215.             }
  216.             /** @var Plugin $plugin */
  217.             $plugin = new $className((bool) $pluginData['active'], $pluginData['path'], $projectDir);
  218.             if (!$plugin instanceof Plugin) {
  219.                 $reason sprintf('Plugin class "%s" must extend "%s"'\get_class($plugin), Plugin::class);
  220.                 throw new KernelPluginLoaderException($pluginData['name'], $reason);
  221.             }
  222.             $this->pluginInstances->add($plugin);
  223.         }
  224.     }
  225.     /**
  226.      * @param Bundle[] $bundles
  227.      *
  228.      * @return array<Bundle[]>
  229.      */
  230.     private function splitBundlesIntoPreAndPost(array $bundles): array
  231.     {
  232.         $pre = [];
  233.         $post = [];
  234.         foreach ($bundles as $index => $bundle) {
  235.             if (\is_int($index) && $index 0) {
  236.                 $pre[$index] = $bundle;
  237.             } else {
  238.                 $post[$index] = $bundle;
  239.             }
  240.         }
  241.         \ksort($pre);
  242.         \ksort($post);
  243.         return [$pre$post];
  244.     }
  245. }