vendor/shopware/storefront/Theme/Subscriber/PluginLifecycleSubscriber.php line 58

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Theme\Subscriber;
  3. use Shopware\Core\Framework\Context;
  4. use Shopware\Core\Framework\Log\Package;
  5. use Shopware\Core\Framework\Plugin;
  6. use Shopware\Core\Framework\Plugin\Event\PluginLifecycleEvent;
  7. use Shopware\Core\Framework\Plugin\Event\PluginPostActivateEvent;
  8. use Shopware\Core\Framework\Plugin\Event\PluginPostDeactivationFailedEvent;
  9. use Shopware\Core\Framework\Plugin\Event\PluginPostUninstallEvent;
  10. use Shopware\Core\Framework\Plugin\Event\PluginPreDeactivateEvent;
  11. use Shopware\Core\Framework\Plugin\Event\PluginPreUninstallEvent;
  12. use Shopware\Core\Framework\Plugin\Event\PluginPreUpdateEvent;
  13. use Shopware\Core\Framework\Plugin\PluginLifecycleService;
  14. use Shopware\Storefront\Theme\Exception\InvalidThemeBundleException;
  15. use Shopware\Storefront\Theme\Exception\ThemeCompileException;
  16. use Shopware\Storefront\Theme\StorefrontPluginConfiguration\AbstractStorefrontPluginConfigurationFactory;
  17. use Shopware\Storefront\Theme\StorefrontPluginConfiguration\StorefrontPluginConfiguration;
  18. use Shopware\Storefront\Theme\StorefrontPluginRegistryInterface;
  19. use Shopware\Storefront\Theme\ThemeLifecycleHandler;
  20. use Shopware\Storefront\Theme\ThemeLifecycleService;
  21. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  22. /**
  23.  * @internal
  24.  */
  25. #[Package('storefront')]
  26. class PluginLifecycleSubscriber implements EventSubscriberInterface
  27. {
  28.     /**
  29.      * @internal
  30.      */
  31.     public function __construct(
  32.         private readonly StorefrontPluginRegistryInterface $storefrontPluginRegistry,
  33.         private readonly string $projectDirectory,
  34.         private readonly AbstractStorefrontPluginConfigurationFactory $pluginConfigurationFactory,
  35.         private readonly ThemeLifecycleHandler $themeLifecycleHandler,
  36.         private readonly ThemeLifecycleService $themeLifecycleService
  37.     ) {
  38.     }
  39.     /**
  40.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  41.      */
  42.     public static function getSubscribedEvents(): array
  43.     {
  44.         return [
  45.             PluginPostActivateEvent::class => 'pluginPostActivate',
  46.             PluginPreUpdateEvent::class => 'pluginUpdate',
  47.             PluginPreDeactivateEvent::class => 'pluginDeactivateAndUninstall',
  48.             PluginPostDeactivationFailedEvent::class => 'pluginPostDeactivateFailed',
  49.             PluginPreUninstallEvent::class => 'pluginDeactivateAndUninstall',
  50.             PluginPostUninstallEvent::class => 'pluginPostUninstall',
  51.         ];
  52.     }
  53.     public function pluginPostActivate(PluginPostActivateEvent $event): void
  54.     {
  55.         $this->doPostActivate($event);
  56.     }
  57.     public function pluginPostDeactivateFailed(PluginPostDeactivationFailedEvent $event): void
  58.     {
  59.         $this->doPostActivate($event);
  60.     }
  61.     public function pluginUpdate(PluginPreUpdateEvent $event): void
  62.     {
  63.         if ($this->skipCompile($event->getContext()->getContext())) {
  64.             return;
  65.         }
  66.         $pluginName $event->getPlugin()->getName();
  67.         $config $this->storefrontPluginRegistry->getConfigurations()->getByTechnicalName($pluginName);
  68.         if (!$config) {
  69.             return;
  70.         }
  71.         $this->themeLifecycleHandler->handleThemeInstallOrUpdate(
  72.             $config,
  73.             $this->storefrontPluginRegistry->getConfigurations(),
  74.             $event->getContext()->getContext()
  75.         );
  76.     }
  77.     public function pluginDeactivateAndUninstall(PluginPreDeactivateEvent|PluginPreUninstallEvent $event): void
  78.     {
  79.         if ($this->skipCompile($event->getContext()->getContext())) {
  80.             return;
  81.         }
  82.         $pluginName $event->getPlugin()->getName();
  83.         $config $this->storefrontPluginRegistry->getConfigurations()->getByTechnicalName($pluginName);
  84.         if (!$config) {
  85.             return;
  86.         }
  87.         $this->themeLifecycleHandler->handleThemeUninstall($config$event->getContext()->getContext());
  88.     }
  89.     public function pluginPostUninstall(PluginPostUninstallEvent $event): void
  90.     {
  91.         if ($event->getContext()->keepUserData()) {
  92.             return;
  93.         }
  94.         $this->themeLifecycleService->removeTheme($event->getPlugin()->getName(), $event->getContext()->getContext());
  95.     }
  96.     /**
  97.      * @throws ThemeCompileException
  98.      * @throws InvalidThemeBundleException
  99.      */
  100.     private function createConfigFromClassName(string $pluginPathstring $className): StorefrontPluginConfiguration
  101.     {
  102.         /** @var Plugin $plugin */
  103.         $plugin = new $className(true$pluginPath$this->projectDirectory);
  104.         if (!$plugin instanceof Plugin) {
  105.             throw new \RuntimeException(
  106.                 sprintf('Plugin class "%s" must extend "%s"'$plugin::class, Plugin::class)
  107.             );
  108.         }
  109.         return $this->pluginConfigurationFactory->createFromBundle($plugin);
  110.     }
  111.     private function doPostActivate(PluginLifecycleEvent $event): void
  112.     {
  113.         if (!($event instanceof PluginPostActivateEvent) && !($event instanceof PluginPostDeactivationFailedEvent)) {
  114.             return;
  115.         }
  116.         if ($this->skipCompile($event->getContext()->getContext())) {
  117.             return;
  118.         }
  119.         // create instance of the plugin to create a configuration
  120.         // (the kernel boot is already finished and the activated plugin is missing)
  121.         $storefrontPluginConfig $this->createConfigFromClassName(
  122.             $event->getPlugin()->getPath() ?: '',
  123.             $event->getPlugin()->getBaseClass()
  124.         );
  125.         // ensure plugin configuration is in the list of all active plugin configurations
  126.         $configurationCollection = clone $this->storefrontPluginRegistry->getConfigurations();
  127.         $configurationCollection->add($storefrontPluginConfig);
  128.         $this->themeLifecycleHandler->handleThemeInstallOrUpdate(
  129.             $storefrontPluginConfig,
  130.             $configurationCollection,
  131.             $event->getContext()->getContext()
  132.         );
  133.     }
  134.     private function skipCompile(Context $context): bool
  135.     {
  136.         return $context->hasState(PluginLifecycleService::STATE_SKIP_ASSET_BUILDING);
  137.     }
  138. }