src/Storefront/Theme/Subscriber/PluginLifecycleSubscriber.php line 63

  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(private readonly StorefrontPluginRegistryInterface $storefrontPluginRegistry, private readonly string $projectDirectory, private readonly AbstractStorefrontPluginConfigurationFactory $pluginConfigurationFactory, private readonly ThemeLifecycleHandler $themeLifecycleHandler, private readonly ThemeLifecycleService $themeLifecycleService)
  32.     {
  33.     }
  34.     /**
  35.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  36.      */
  37.     public static function getSubscribedEvents(): array
  38.     {
  39.         return [
  40.             PluginPostActivateEvent::class => 'pluginPostActivate',
  41.             PluginPreUpdateEvent::class => 'pluginUpdate',
  42.             PluginPreDeactivateEvent::class => 'pluginDeactivateAndUninstall',
  43.             PluginPostDeactivationFailedEvent::class => 'pluginPostDeactivateFailed',
  44.             PluginPreUninstallEvent::class => 'pluginDeactivateAndUninstall',
  45.             PluginPostUninstallEvent::class => 'pluginPostUninstall',
  46.         ];
  47.     }
  48.     public function pluginPostActivate(PluginPostActivateEvent $event): void
  49.     {
  50.         $this->doPostActivate($event);
  51.     }
  52.     public function pluginPostDeactivateFailed(PluginPostDeactivationFailedEvent $event): void
  53.     {
  54.         $this->doPostActivate($event);
  55.     }
  56.     public function pluginUpdate(PluginPreUpdateEvent $event): void
  57.     {
  58.         if ($this->skipCompile($event->getContext()->getContext())) {
  59.             return;
  60.         }
  61.         $pluginName $event->getPlugin()->getName();
  62.         $config $this->storefrontPluginRegistry->getConfigurations()->getByTechnicalName($pluginName);
  63.         if (!$config) {
  64.             return;
  65.         }
  66.         $this->themeLifecycleHandler->handleThemeInstallOrUpdate(
  67.             $config,
  68.             $this->storefrontPluginRegistry->getConfigurations(),
  69.             $event->getContext()->getContext()
  70.         );
  71.     }
  72.     public function pluginDeactivateAndUninstall(PluginPreDeactivateEvent|PluginPreUninstallEvent $event): void
  73.     {
  74.         if ($this->skipCompile($event->getContext()->getContext())) {
  75.             return;
  76.         }
  77.         $pluginName $event->getPlugin()->getName();
  78.         $config $this->storefrontPluginRegistry->getConfigurations()->getByTechnicalName($pluginName);
  79.         if (!$config) {
  80.             return;
  81.         }
  82.         $this->themeLifecycleHandler->handleThemeUninstall($config$event->getContext()->getContext());
  83.     }
  84.     public function pluginPostUninstall(PluginPostUninstallEvent $event): void
  85.     {
  86.         if ($event->getContext()->keepUserData()) {
  87.             return;
  88.         }
  89.         $this->themeLifecycleService->removeTheme($event->getPlugin()->getName(), $event->getContext()->getContext());
  90.     }
  91.     /**
  92.      * @throws ThemeCompileException
  93.      * @throws InvalidThemeBundleException
  94.      */
  95.     private function createConfigFromClassName(string $pluginPathstring $className): StorefrontPluginConfiguration
  96.     {
  97.         /** @var Plugin $plugin */
  98.         $plugin = new $className(true$pluginPath$this->projectDirectory);
  99.         if (!$plugin instanceof Plugin) {
  100.             throw new \RuntimeException(
  101.                 sprintf('Plugin class "%s" must extend "%s"'$plugin::class, Plugin::class)
  102.             );
  103.         }
  104.         return $this->pluginConfigurationFactory->createFromBundle($plugin);
  105.     }
  106.     private function doPostActivate(PluginLifecycleEvent $event): void
  107.     {
  108.         if (!($event instanceof PluginPostActivateEvent) && !($event instanceof PluginPostDeactivationFailedEvent)) {
  109.             return;
  110.         }
  111.         if ($this->skipCompile($event->getContext()->getContext())) {
  112.             return;
  113.         }
  114.         // create instance of the plugin to create a configuration
  115.         // (the kernel boot is already finished and the activated plugin is missing)
  116.         $storefrontPluginConfig $this->createConfigFromClassName(
  117.             $event->getPlugin()->getPath() ?: '',
  118.             $event->getPlugin()->getBaseClass()
  119.         );
  120.         // ensure plugin configuration is in the list of all active plugin configurations
  121.         $configurationCollection = clone $this->storefrontPluginRegistry->getConfigurations();
  122.         $configurationCollection->add($storefrontPluginConfig);
  123.         $this->themeLifecycleHandler->handleThemeInstallOrUpdate(
  124.             $storefrontPluginConfig,
  125.             $configurationCollection,
  126.             $event->getContext()->getContext()
  127.         );
  128.     }
  129.     private function skipCompile(Context $context): bool
  130.     {
  131.         return $context->hasState(PluginLifecycleService::STATE_SKIP_ASSET_BUILDING);
  132.     }
  133. }