Skip to content

Commit

Permalink
Merge branch 'next-39782/improve-application-startup' into 'trunk'
Browse files Browse the repository at this point in the history
NEXT-39782 - Improve application startup

See merge request shopware/6/product/platform!15388
  • Loading branch information
shyim committed Nov 26, 2024
2 parents 3d1a332 + e88d805 commit 18db357
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 130 deletions.
14 changes: 7 additions & 7 deletions src/Core/Content/DependencyInjection/flow.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@

<service id="Shopware\Core\Content\Flow\Dispatching\FlowDispatcher" decorates="event_dispatcher" decoration-priority="1000">
<argument type="service" id="Shopware\Core\Content\Flow\Dispatching\FlowDispatcher.inner"/>
<argument type="service" id="logger"/>
<argument type="service" id="Shopware\Core\Content\Flow\Dispatching\FlowFactory"/>
<argument type="service" id="Doctrine\DBAL\Connection"/>

<call method="setContainer">
<argument type="service" id="service_container"/>
</call>
<argument type="service_locator">
<argument type="service" key="logger" id="logger"/>
<argument type="service" key="Doctrine\DBAL\Connection" id="Doctrine\DBAL\Connection"/>
<argument type="service" key="Shopware\Core\Content\Flow\Dispatching\FlowFactory" id="Shopware\Core\Content\Flow\Dispatching\FlowFactory"/>
<argument type="service" key="Shopware\Core\Content\Flow\Dispatching\FlowExecutor" id="Shopware\Core\Content\Flow\Dispatching\FlowExecutor"/>
<argument type="service" key="Shopware\Core\Content\Flow\Dispatching\FlowLoader" id="Shopware\Core\Content\Flow\Dispatching\FlowLoader"/>
</argument>
</service>

<service id="Shopware\Core\Content\Flow\Rule\FlowRuleScopeBuilder">
Expand Down
49 changes: 22 additions & 27 deletions src/Core/Content/Flow/Dispatching/FlowDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,29 @@
namespace Shopware\Core\Content\Flow\Dispatching;

use Doctrine\DBAL\Connection;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\StoppableEventInterface;
use Psr\Log\LoggerInterface;
use Shopware\Core\Content\Flow\Exception\ExecuteSequenceException;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\Event\FlowEventAware;
use Shopware\Core\Framework\Event\FlowLogEvent;
use Shopware\Core\Framework\Log\Package;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

/**
* @internal not intended for decoration or replacement
*/
#[Package('services-settings')]
class FlowDispatcher implements EventDispatcherInterface
class FlowDispatcher implements EventDispatcherInterface, ServiceSubscriberInterface
{
private ContainerInterface $container;

public function __construct(
private readonly EventDispatcherInterface $dispatcher,
private readonly LoggerInterface $logger,
private readonly FlowFactory $flowFactory,
private readonly Connection $connection,
private readonly ContainerInterface $container,
) {
}

public function setContainer(ContainerInterface $container): void
{
$this->container = $container;
}

/**
* @template TEvent of object
*
Expand All @@ -62,7 +52,7 @@ public function dispatch(object $event, ?string $eventName = null): object
return $event;
}

$storableFlow = $this->flowFactory->create($event);
$storableFlow = $this->container->get(FlowFactory::class)->create($event);
$this->callFlowExecutor($storableFlow);

return $event;
Expand Down Expand Up @@ -109,6 +99,20 @@ public function hasListeners(?string $eventName = null): bool
return $this->dispatcher->hasListeners($eventName);
}

/**
* {@inheritDoc}
*/
public static function getSubscribedServices(): array
{
return [
'logger',
Connection::class,
FlowFactory::class,
FlowExecutor::class,
FlowLoader::class,
];
}

private function callFlowExecutor(StorableFlow $event): void
{
$flows = $this->getFlows($event->getName());
Expand All @@ -119,16 +123,12 @@ private function callFlowExecutor(StorableFlow $event): void

$flowExecutor = $this->container->get(FlowExecutor::class);

if ($flowExecutor === null) {
throw new ServiceNotFoundException(FlowExecutor::class);
}

foreach ($flows as $flow) {
try {
$payload = $flow['payload'];
$flowExecutor->execute($payload, $event);
} catch (ExecuteSequenceException $e) {
$this->logger->warning(
$this->container->get('logger')->warning(
"Could not execute flow with error message:\n"
. 'Flow name: ' . $flow['name'] . "\n"
. 'Flow id: ' . $flow['id'] . "\n"
Expand All @@ -148,7 +148,7 @@ private function callFlowExecutor(StorableFlow $event): void
throw $e->getPrevious();
}
} catch (\Throwable $e) {
$this->logger->error(
$this->container->get('logger')->error(
"Could not execute flow with error message:\n"
. 'Flow name: ' . $flow['name'] . "\n"
. 'Flow id: ' . $flow['id'] . "\n"
Expand All @@ -166,11 +166,6 @@ private function callFlowExecutor(StorableFlow $event): void
private function getFlows(string $eventName): array
{
$flowLoader = $this->container->get(FlowLoader::class);

if ($flowLoader === null) {
throw new ServiceNotFoundException(FlowExecutor::class);
}

$flows = $flowLoader->load();

$result = [];
Expand All @@ -183,6 +178,6 @@ private function getFlows(string $eventName): array

private function isInNestedTransaction(): bool
{
return $this->connection->getTransactionNestingLevel() !== 1 && !$this->connection->getNestTransactionsWithSavepoints();
return $this->container->get(Connection::class)->getTransactionNestingLevel() !== 1 && !$this->container->get(Connection::class)->getNestTransactionsWithSavepoints();
}
}
2 changes: 1 addition & 1 deletion src/Core/Framework/DependencyInjection/cache.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<tag name="kernel.event_subscriber"/>
</service>

<service id="Shopware\Core\Framework\Adapter\Cache\CacheTracer">
<service id="Shopware\Core\Framework\Adapter\Cache\CacheTracer" lazy="true">
<argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService"/>
<argument type="service" id="Shopware\Core\Framework\Adapter\Translation\Translator"/>
<argument type="service" id="Shopware\Core\Framework\Adapter\Cache\CacheTagCollection"/>
Expand Down
5 changes: 5 additions & 0 deletions src/Core/Framework/Webhook/Hookable/HookableEventFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public function __construct(
) {
}

public static function isHookable(object $event): bool
{
return $event instanceof Hookable || $event instanceof FlowEventAware || $event instanceof EntityWrittenContainerEvent;
}

/**
* @return Hookable[]
*/
Expand Down
3 changes: 2 additions & 1 deletion src/Core/Framework/Webhook/WebhookDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Shopware\Core\DevOps\Environment\EnvironmentHelper;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\Framework\Webhook\Hookable\HookableEventFactory;
use Shopware\Core\Framework\Webhook\Service\WebhookManager;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
Expand All @@ -27,7 +28,7 @@ public function dispatch(object $event, ?string $eventName = null): object
{
$event = $this->dispatcher->dispatch($event, $eventName);

if (EnvironmentHelper::getVariable('DISABLE_EXTENSIONS', false)) {
if (EnvironmentHelper::getVariable('DISABLE_EXTENSIONS', false) || !HookableEventFactory::isHookable($event)) {
return $event;
}

Expand Down
6 changes: 4 additions & 2 deletions src/Core/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public function registerBundles(): iterable
$bundles = require $this->getProjectDir() . '/config/bundles.php';
$instanciatedBundleNames = [];

$kernelParameters = $this->getKernelParameters();

foreach ($bundles as $class => $envs) {
if (isset($envs['all']) || isset($envs[$this->environment])) {
/** @var ShopwareBundle|Bundle $bundle */
Expand All @@ -121,7 +123,7 @@ public function registerBundles(): iterable
}

$classLoader = new ClassLoader();
$parameters = new AdditionalBundleParameters($classLoader, new KernelPluginCollection(), $this->getKernelParameters());
$parameters = new AdditionalBundleParameters($classLoader, new KernelPluginCollection(), $kernelParameters);
foreach ($bundle->getAdditionalBundles($parameters) as $additionalBundle) {
if ($this->isBundleRegistered($additionalBundle, $instanciatedBundleNames)) {
continue;
Expand All @@ -138,7 +140,7 @@ public function registerBundles(): iterable
yield new Service();
}

yield from $this->pluginLoader->getBundles($this->getKernelParameters(), $instanciatedBundleNames);
yield from $this->pluginLoader->getBundles($kernelParameters, $instanciatedBundleNames);
}

public function getProjectDir(): string
Expand Down
2 changes: 1 addition & 1 deletion src/Storefront/DependencyInjection/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@
<argument type="service" id="assets.packages"/>
</service>

<service id="Shopware\Storefront\Framework\Cache\CacheTracer" decorates="Shopware\Core\Framework\Adapter\Cache\CacheTracer">
<service id="Shopware\Storefront\Framework\Cache\CacheTracer" decorates="Shopware\Core\Framework\Adapter\Cache\CacheTracer" lazy="true">
<argument type="service" id="Shopware\Storefront\Framework\Cache\CacheTracer.inner"/>
<argument type="service" id="Shopware\Storefront\Theme\ThemeConfigValueAccessor"/>
</service>
Expand Down
102 changes: 15 additions & 87 deletions tests/unit/Core/Content/Flow/Dispatching/FlowDispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@
use Shopware\Core\Framework\Event\FlowLogEvent;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\Test\Stub\Framework\IdsCollection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
Expand All @@ -37,9 +36,7 @@
#[CoversClass(FlowDispatcher::class)]
class FlowDispatcherTest extends TestCase
{
private IdsCollection $ids;

private MockObject&ContainerInterface $container;
private ContainerInterface $container;

private MockObject&EventDispatcherInterface $dispatcher;

Expand All @@ -53,15 +50,17 @@ class FlowDispatcherTest extends TestCase

protected function setUp(): void
{
$this->ids = new IdsCollection();
$this->container = $this->createMock(ContainerInterface::class);
$this->container = new ContainerBuilder();
$this->dispatcher = $this->createMock(EventDispatcherInterface::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->flowFactory = $this->createMock(FlowFactory::class);
$this->connection = $this->createMock(Connection::class);

$this->flowDispatcher = new FlowDispatcher($this->dispatcher, $this->logger, $this->flowFactory, $this->connection);
$this->flowDispatcher->setContainer($this->container);
$this->container->set('logger', $this->logger);
$this->container->set(FlowFactory::class, $this->flowFactory);
$this->container->set(Connection::class, $this->connection);

$this->flowDispatcher = new FlowDispatcher($this->dispatcher, $this->container);
}

public function testDispatchWithNotFlowEventAware(): void
Expand Down Expand Up @@ -96,34 +95,6 @@ public function testDispatchSkipTrigger(): void
$this->flowDispatcher->dispatch($event);
}

public function testDispatchWithoutFlowLoader(): void
{
$context = Context::createDefaultContext();
$order = new OrderEntity();
$event = new CheckoutOrderPlacedEvent(
$context,
$order,
Defaults::SALES_CHANNEL_TYPE_STOREFRONT
);

$flowLogEvent = new FlowLogEvent(FlowLogEvent::NAME, $event);
$this->dispatcher->expects(static::exactly(2))
->method('dispatch')
->willReturnOnConsecutiveCalls($event, $flowLogEvent);

$flow = new StorableFlow('name', $context, [], []);
$this->flowFactory->expects(static::once())
->method('create')
->willReturn($flow);

$this->container->expects(static::once())
->method('get')
->willReturnOnConsecutiveCalls(null);

$this->expectException(ServiceNotFoundException::class);
$this->flowDispatcher->dispatch($event);
}

public function testDispatchWithoutFlows(): void
{
$context = Context::createDefaultContext();
Expand All @@ -145,55 +116,11 @@ public function testDispatchWithoutFlows(): void
->willReturn($flow);

$flowLoader = $this->createMock(FlowLoader::class);
$this->container->set(FlowLoader::class, $flowLoader);
$flowLoader->expects(static::once())
->method('load')
->willReturn([]);

$this->container->expects(static::once())
->method('get')
->willReturnOnConsecutiveCalls($flowLoader);

$this->flowDispatcher->dispatch($event);
}

public function testDispatchWithoutFlowExecutor(): void
{
$context = Context::createDefaultContext();
$order = new OrderEntity();
$event = new CheckoutOrderPlacedEvent(
$context,
$order,
Defaults::SALES_CHANNEL_TYPE_STOREFRONT
);

$flowLogEvent = new FlowLogEvent(FlowLogEvent::NAME, $event);
$this->dispatcher->expects(static::exactly(2))
->method('dispatch')
->willReturnOnConsecutiveCalls($event, $flowLogEvent);

$flow = new StorableFlow('state_enter.order.state.in_progress', $context, [], []);
$this->flowFactory->expects(static::once())
->method('create')
->willReturn($flow);

$flowLoader = $this->createMock(FlowLoader::class);
$flowLoader->expects(static::once())
->method('load')
->willReturn([
'state_enter.order.state.in_progress' => [
[
'id' => $this->ids->get('order'),
'name' => 'Order enters status in progress',
'payload' => [],
],
],
]);

$this->container->expects(static::exactly(2))
->method('get')
->willReturnOnConsecutiveCalls($flowLoader, null);

$this->expectException(ServiceNotFoundException::class);
$this->flowDispatcher->dispatch($event);
}

Expand Down Expand Up @@ -230,9 +157,8 @@ public function testDispatch(array $flows): void
$flowExecutor->expects(static::exactly(is_countable($flows['state_enter.order.state.in_progress']) ? \count($flows['state_enter.order.state.in_progress']) : 0))
->method('execute');

$this->container->expects(static::exactly(2))
->method('get')
->willReturnOnConsecutiveCalls($flowLoader, $flowExecutor);
$this->container->set(FlowLoader::class, $flowLoader);
$this->container->set(FlowExecutor::class, $flowExecutor);

$this->flowDispatcher->dispatch($event);
}
Expand Down Expand Up @@ -282,7 +208,8 @@ public function testNestedTransactionExceptionsAreRethrownWhenSavePointsAreNotEn
$internalException
));

$this->container->method('get')->willReturnOnConsecutiveCalls($flowLoader, $flowExecutor);
$this->container->set(FlowLoader::class, $flowLoader);
$this->container->set(FlowExecutor::class, $flowExecutor);

$this->expectException(FlowException::class);
$this->expectExceptionMessage('Flow action transaction could not be committed and was rolled back. Exception: An exception occurred in the driver: Table not found');
Expand Down Expand Up @@ -344,7 +271,8 @@ public function testExceptionsAreLoggedAndExecutionContinuesWhenNestedTransactio
$internalException
));

$this->container->method('get')->willReturnOnConsecutiveCalls($flowLoader, $flowExecutor);
$this->container->set(FlowLoader::class, $flowLoader);
$this->container->set(FlowExecutor::class, $flowExecutor);

$this->connection->method('getTransactionNestingLevel')->willReturn(1);
$this->connection->method('getNestTransactionsWithSavepoints')->willReturn(true);
Expand Down
Loading

0 comments on commit 18db357

Please sign in to comment.