vendor/shopware/core/Framework/Script/Execution/ScriptLoader.php line 65

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Script\Execution;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\DevOps\Environment\EnvironmentHelper;
  5. use Shopware\Core\Framework\Adapter\Cache\CacheCompressor;
  6. use Shopware\Core\Framework\App\Lifecycle\Persister\ScriptPersister;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\FetchModeHelper;
  8. use Shopware\Core\Framework\Log\Package;
  9. use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. use Twig\Cache\FilesystemCache;
  12. /**
  13. * @internal only for use by the app-system
  14. */
  15. #[Package('core')]
  16. class ScriptLoader implements EventSubscriberInterface
  17. {
  18. public const CACHE_KEY = 'shopware-app-scripts';
  19. private Connection $connection;
  20. private string $cacheDir;
  21. private ScriptPersister $scriptPersister;
  22. private bool $debug;
  23. private TagAwareAdapterInterface $cache;
  24. public function __construct(Connection $connection, ScriptPersister $scriptPersister, TagAwareAdapterInterface $cache, string $cacheDir, bool $debug)
  25. {
  26. $this->connection = $connection;
  27. $this->cacheDir = $cacheDir . '/twig/scripts';
  28. $this->scriptPersister = $scriptPersister;
  29. $this->debug = $debug;
  30. $this->cache = $cache;
  31. }
  32. public static function getSubscribedEvents(): array
  33. {
  34. return ['script.written' => 'invalidateCache'];
  35. }
  36. /**
  37. * @return Script[]
  38. */
  39. public function get(string $hook): array
  40. {
  41. $cacheItem = $this->cache->getItem(self::CACHE_KEY);
  42. if ($cacheItem->isHit() && $cacheItem->get() && !$this->debug) {
  43. return CacheCompressor::uncompress($cacheItem)[$hook] ?? [];
  44. }
  45. $scripts = $this->load();
  46. $cacheItem = CacheCompressor::compress($cacheItem, $scripts);
  47. $this->cache->save($cacheItem);
  48. return $scripts[$hook] ?? [];
  49. }
  50. public function invalidateCache(): void
  51. {
  52. $this->cache->deleteItem(self::CACHE_KEY);
  53. }
  54. private function load(): array
  55. {
  56. if ($this->debug) {
  57. $this->scriptPersister->refresh();
  58. }
  59. $scripts = $this->connection->fetchAllAssociative('
  60. SELECT LOWER(HEX(`script`.`app_id`)) as `app_id`,
  61. `script`.`name` AS scriptName,
  62. `script`.`script` AS script,
  63. `script`.`hook` AS hook,
  64. IFNULL(`script`.`updated_at`, `script`.`created_at`) AS lastModified,
  65. `app`.`name` AS appName,
  66. LOWER(HEX(`app`.`integration_id`)) AS integrationId,
  67. `app`.`version` AS appVersion,
  68. `script`.`active` AS active
  69. FROM `script`
  70. LEFT JOIN `app` ON `script`.`app_id` = `app`.`id`
  71. WHERE `script`.`hook` != \'include\'
  72. ORDER BY `app`.`created_at`, `app`.`id`, `script`.`name`
  73. ');
  74. $includes = $this->connection->fetchAllAssociative('
  75. SELECT LOWER(HEX(`script`.`app_id`)) as `app_id`,
  76. `script`.`name` AS name,
  77. `script`.`script` AS script,
  78. `app`.`name` AS appName,
  79. LOWER(HEX(`app`.`integration_id`)) AS integrationId,
  80. IFNULL(`script`.`updated_at`, `script`.`created_at`) AS lastModified
  81. FROM `script`
  82. LEFT JOIN `app` ON `script`.`app_id` = `app`.`id`
  83. WHERE `script`.`hook` = \'include\'
  84. ORDER BY `app`.`created_at`, `app`.`id`, `script`.`name`
  85. ');
  86. $allIncludes = FetchModeHelper::group($includes);
  87. $executableScripts = [];
  88. /** @var array $script */
  89. foreach ($scripts as $script) {
  90. $appId = $script['app_id'];
  91. $includes = $allIncludes[$appId] ?? [];
  92. $dates = array_merge([$script['lastModified']], array_column($includes, 'lastModified'));
  93. /** @var \DateTimeInterface $lastModified */
  94. $lastModified = new \DateTimeImmutable(max($dates));
  95. /** @var string $cachePrefix */
  96. $cachePrefix = $script['appName'] ? md5($script['appName'] . $script['appVersion']) : EnvironmentHelper::getVariable('INSTANCE_ID', '');
  97. $includes = array_map(function (array $script) use ($appId) {
  98. $script['app_id'] = $appId;
  99. return new Script(
  100. $script['name'],
  101. $script['script'],
  102. new \DateTimeImmutable($script['lastModified']),
  103. $this->getAppInfo($script)
  104. );
  105. }, $includes);
  106. $options = [];
  107. if (!$this->debug) {
  108. $options['cache'] = new FilesystemCache($this->cacheDir . '/' . $cachePrefix);
  109. } else {
  110. $options['debug'] = true;
  111. }
  112. $executableScripts[$script['hook']][] = new Script(
  113. $script['scriptName'],
  114. $script['script'],
  115. $lastModified,
  116. $this->getAppInfo($script),
  117. $options,
  118. $includes,
  119. (bool) $script['active']
  120. );
  121. }
  122. return $executableScripts;
  123. }
  124. private function getAppInfo(array $script): ?ScriptAppInformation
  125. {
  126. if (!$script['app_id'] || !$script['appName'] || !$script['integrationId']) {
  127. return null;
  128. }
  129. return new ScriptAppInformation(
  130. $script['app_id'],
  131. $script['appName'],
  132. $script['integrationId']
  133. );
  134. }
  135. }