<?php declare(strict_types=1);
namespace Shopware\Storefront\Theme\Subscriber;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\Feature;
use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Event\PluginLifecycleEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPostActivateEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPostDeactivationFailedEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPostUninstallEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPreActivateEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPreDeactivateEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPreUninstallEvent;
use Shopware\Core\Framework\Plugin\Event\PluginPreUpdateEvent;
use Shopware\Storefront\Theme\Exception\InvalidThemeBundleException;
use Shopware\Storefront\Theme\Exception\ThemeCompileException;
use Shopware\Storefront\Theme\StorefrontPluginConfiguration\AbstractStorefrontPluginConfigurationFactory;
use Shopware\Storefront\Theme\StorefrontPluginConfiguration\StorefrontPluginConfiguration;
use Shopware\Storefront\Theme\StorefrontPluginRegistryInterface;
use Shopware\Storefront\Theme\ThemeLifecycleHandler;
use Shopware\Storefront\Theme\ThemeLifecycleService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class PluginLifecycleSubscriber implements EventSubscriberInterface
{
private StorefrontPluginRegistryInterface $storefrontPluginRegistry;
private string $projectDirectory;
private AbstractStorefrontPluginConfigurationFactory $pluginConfigurationFactory;
private ThemeLifecycleHandler $themeLifecycleHandler;
private ThemeLifecycleService $themeLifecycleService;
/**
* @internal
*/
public function __construct(
StorefrontPluginRegistryInterface $storefrontPluginRegistry,
string $projectDirectory,
AbstractStorefrontPluginConfigurationFactory $pluginConfigurationFactory,
ThemeLifecycleHandler $themeLifecycleHandler,
ThemeLifecycleService $themeLifecycleService
) {
$this->storefrontPluginRegistry = $storefrontPluginRegistry;
$this->projectDirectory = $projectDirectory;
$this->pluginConfigurationFactory = $pluginConfigurationFactory;
$this->themeLifecycleHandler = $themeLifecycleHandler;
$this->themeLifecycleService = $themeLifecycleService;
}
/**
* @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
*/
public static function getSubscribedEvents()
{
if (Feature::isActive('v6.5.0.0')) {
return [
PluginPostActivateEvent::class => 'pluginPostActivate',
PluginPreUpdateEvent::class => 'pluginUpdate',
PluginPreDeactivateEvent::class => 'pluginDeactivateAndUninstall',
PluginPostDeactivationFailedEvent::class => 'pluginPostDeactivateFailed',
PluginPreUninstallEvent::class => 'pluginDeactivateAndUninstall',
PluginPostUninstallEvent::class => 'pluginPostUninstall',
];
}
return [
PluginPreActivateEvent::class => 'pluginActivate',
PluginPostActivateEvent::class => 'pluginPostActivate',
PluginPreUpdateEvent::class => 'pluginUpdate',
PluginPreDeactivateEvent::class => 'pluginDeactivateAndUninstall',
PluginPostDeactivationFailedEvent::class => 'pluginPostDeactivateFailed',
PluginPreUninstallEvent::class => 'pluginDeactivateAndUninstall',
PluginPostUninstallEvent::class => 'pluginPostUninstall',
];
}
/**
* @deprecated tag:v6.5.0 - Method will be removed. use pluginPostActivate instead
*/
public function pluginActivate(PluginPreActivateEvent $event): void
{
Feature::triggerDeprecationOrThrow(
'v6.5.0.0',
sprintf('Method pluginActivate of Class %s is deprecated. Use method pluginPostActivate instead', static::class)
);
// do nothing
}
public function pluginPostActivate(PluginPostActivateEvent $event): void
{
$this->doPostActivate($event);
}
public function pluginPostDeactivateFailed(PluginPostDeactivationFailedEvent $event): void
{
$this->doPostActivate($event);
}
public function pluginUpdate(PluginPreUpdateEvent $event): void
{
if ($this->skipCompile($event->getContext()->getContext())) {
return;
}
$pluginName = $event->getPlugin()->getName();
$config = $this->storefrontPluginRegistry->getConfigurations()->getByTechnicalName($pluginName);
if (!$config) {
return;
}
$this->themeLifecycleHandler->handleThemeInstallOrUpdate(
$config,
$this->storefrontPluginRegistry->getConfigurations(),
$event->getContext()->getContext()
);
}
/**
* @param PluginPreDeactivateEvent|PluginPreUninstallEvent $event
*/
public function pluginDeactivateAndUninstall($event): void
{
if ($this->skipCompile($event->getContext()->getContext())) {
return;
}
$pluginName = $event->getPlugin()->getName();
$config = $this->storefrontPluginRegistry->getConfigurations()->getByTechnicalName($pluginName);
if (!$config) {
return;
}
$this->themeLifecycleHandler->handleThemeUninstall($config, $event->getContext()->getContext());
}
public function pluginPostUninstall(PluginPostUninstallEvent $event): void
{
if ($event->getContext()->keepUserData()) {
return;
}
$this->themeLifecycleService->removeTheme($event->getPlugin()->getName(), $event->getContext()->getContext());
}
/**
* @throws ThemeCompileException
* @throws InvalidThemeBundleException
*/
private function createConfigFromClassName(string $pluginPath, string $className): StorefrontPluginConfiguration
{
/** @var Plugin $plugin */
$plugin = new $className(true, $pluginPath, $this->projectDirectory);
if (!$plugin instanceof Plugin) {
throw new \RuntimeException(
sprintf('Plugin class "%s" must extend "%s"', \get_class($plugin), Plugin::class)
);
}
return $this->pluginConfigurationFactory->createFromBundle($plugin);
}
private function doPostActivate(PluginLifecycleEvent $event): void
{
if (!($event instanceof PluginPostActivateEvent) && !($event instanceof PluginPostDeactivationFailedEvent)) {
return;
}
if ($this->skipCompile($event->getContext()->getContext())) {
return;
}
// create instance of the plugin to create a configuration
// (the kernel boot is already finished and the activated plugin is missing)
$storefrontPluginConfig = $this->createConfigFromClassName(
$event->getPlugin()->getPath() ?: '',
$event->getPlugin()->getBaseClass()
);
// add plugin configuration to the list of all active plugin configurations
$configurationCollection = clone $this->storefrontPluginRegistry->getConfigurations();
$configurationCollection->add($storefrontPluginConfig);
$this->themeLifecycleHandler->handleThemeInstallOrUpdate(
$storefrontPluginConfig,
$configurationCollection,
$event->getContext()->getContext()
);
}
private function skipCompile(Context $context): bool
{
return $context->hasState(Plugin\PluginLifecycleService::STATE_SKIP_ASSET_BUILDING);
}
}