<?php declare(strict_types=1);namespace Shopware\Core\Framework\Script\Execution;use Doctrine\DBAL\Connection;use Shopware\Core\DevOps\Environment\EnvironmentHelper;use Shopware\Core\Framework\Adapter\Cache\CacheCompressor;use Shopware\Core\Framework\App\Lifecycle\Persister\ScriptPersister;use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\FetchModeHelper;use Shopware\Core\Framework\Log\Package;use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;use Symfony\Component\EventDispatcher\EventSubscriberInterface;use Twig\Cache\FilesystemCache;/** * @internal only for use by the app-system */#[Package('core')]class ScriptLoader implements EventSubscriberInterface{ public const CACHE_KEY = 'shopware-app-scripts'; private Connection $connection; private string $cacheDir; private ScriptPersister $scriptPersister; private bool $debug; private TagAwareAdapterInterface $cache; public function __construct(Connection $connection, ScriptPersister $scriptPersister, TagAwareAdapterInterface $cache, string $cacheDir, bool $debug) { $this->connection = $connection; $this->cacheDir = $cacheDir . '/twig/scripts'; $this->scriptPersister = $scriptPersister; $this->debug = $debug; $this->cache = $cache; } public static function getSubscribedEvents(): array { return ['script.written' => 'invalidateCache']; } /** * @return Script[] */ public function get(string $hook): array { $cacheItem = $this->cache->getItem(self::CACHE_KEY); if ($cacheItem->isHit() && $cacheItem->get() && !$this->debug) { return CacheCompressor::uncompress($cacheItem)[$hook] ?? []; } $scripts = $this->load(); $cacheItem = CacheCompressor::compress($cacheItem, $scripts); $this->cache->save($cacheItem); return $scripts[$hook] ?? []; } public function invalidateCache(): void { $this->cache->deleteItem(self::CACHE_KEY); } private function load(): array { if ($this->debug) { $this->scriptPersister->refresh(); } $scripts = $this->connection->fetchAllAssociative(' SELECT LOWER(HEX(`script`.`app_id`)) as `app_id`, `script`.`name` AS scriptName, `script`.`script` AS script, `script`.`hook` AS hook, IFNULL(`script`.`updated_at`, `script`.`created_at`) AS lastModified, `app`.`name` AS appName, LOWER(HEX(`app`.`integration_id`)) AS integrationId, `app`.`version` AS appVersion, `script`.`active` AS active FROM `script` LEFT JOIN `app` ON `script`.`app_id` = `app`.`id` WHERE `script`.`hook` != \'include\' ORDER BY `app`.`created_at`, `app`.`id`, `script`.`name` '); $includes = $this->connection->fetchAllAssociative(' SELECT LOWER(HEX(`script`.`app_id`)) as `app_id`, `script`.`name` AS name, `script`.`script` AS script, `app`.`name` AS appName, LOWER(HEX(`app`.`integration_id`)) AS integrationId, IFNULL(`script`.`updated_at`, `script`.`created_at`) AS lastModified FROM `script` LEFT JOIN `app` ON `script`.`app_id` = `app`.`id` WHERE `script`.`hook` = \'include\' ORDER BY `app`.`created_at`, `app`.`id`, `script`.`name` '); $allIncludes = FetchModeHelper::group($includes); $executableScripts = []; /** @var array $script */ foreach ($scripts as $script) { $appId = $script['app_id']; $includes = $allIncludes[$appId] ?? []; $dates = array_merge([$script['lastModified']], array_column($includes, 'lastModified')); /** @var \DateTimeInterface $lastModified */ $lastModified = new \DateTimeImmutable(max($dates)); /** @var string $cachePrefix */ $cachePrefix = $script['appName'] ? md5($script['appName'] . $script['appVersion']) : EnvironmentHelper::getVariable('INSTANCE_ID', ''); $includes = array_map(function (array $script) use ($appId) { $script['app_id'] = $appId; return new Script( $script['name'], $script['script'], new \DateTimeImmutable($script['lastModified']), $this->getAppInfo($script) ); }, $includes); $options = []; if (!$this->debug) { $options['cache'] = new FilesystemCache($this->cacheDir . '/' . $cachePrefix); } else { $options['debug'] = true; } $executableScripts[$script['hook']][] = new Script( $script['scriptName'], $script['script'], $lastModified, $this->getAppInfo($script), $options, $includes, (bool) $script['active'] ); } return $executableScripts; } private function getAppInfo(array $script): ?ScriptAppInformation { if (!$script['app_id'] || !$script['appName'] || !$script['integrationId']) { return null; } return new ScriptAppInformation( $script['app_id'], $script['appName'], $script['integrationId'] ); }}