CacheWarmupCommand.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Command;
  4. use PhpMyAdmin\Config;
  5. use PhpMyAdmin\DatabaseInterface;
  6. use PhpMyAdmin\Routing;
  7. use PhpMyAdmin\Tests\Stubs\DbiDummy;
  8. use PhpMyAdmin\Twig\CoreExtension;
  9. use PhpMyAdmin\Twig\I18nExtension;
  10. use PhpMyAdmin\Twig\MessageExtension;
  11. use PhpMyAdmin\Twig\PluginsExtension;
  12. use PhpMyAdmin\Twig\RelationExtension;
  13. use PhpMyAdmin\Twig\SanitizeExtension;
  14. use PhpMyAdmin\Twig\TableExtension;
  15. use PhpMyAdmin\Twig\TrackerExtension;
  16. use PhpMyAdmin\Twig\TransformationsExtension;
  17. use PhpMyAdmin\Twig\UrlExtension;
  18. use PhpMyAdmin\Twig\UtilExtension;
  19. use RecursiveDirectoryIterator;
  20. use RecursiveIteratorIterator;
  21. use Symfony\Component\Console\Command\Command;
  22. use Symfony\Component\Console\Input\InputInterface;
  23. use Symfony\Component\Console\Output\OutputInterface;
  24. use Twig\Cache\CacheInterface;
  25. use Twig\Environment;
  26. use Twig\Loader\FilesystemLoader;
  27. use function fclose;
  28. use function fopen;
  29. use function fwrite;
  30. use function json_encode;
  31. use function str_replace;
  32. use function strpos;
  33. use function is_file;
  34. use function sprintf;
  35. final class CacheWarmupCommand extends Command
  36. {
  37. /** @var string */
  38. protected static $defaultName = 'cache:warmup';
  39. protected function configure(): void
  40. {
  41. $this->setDescription('Warms up the Twig templates cache');
  42. $this->addOption('twig', null, null, 'Warm up twig templates cache.');
  43. $this->addOption('routing', null, null, 'Warm up routing cache.');
  44. $this->setHelp('The <info>%command.name%</info> command warms up the cache of the Twig templates.');
  45. }
  46. protected function execute(InputInterface $input, OutputInterface $output): int
  47. {
  48. if ($input->getOption('twig') === true && $input->getOption('routing') === true) {
  49. $output->writeln('Please specify --twig or --routing');
  50. return 1;
  51. }
  52. if ($input->getOption('twig') === true) {
  53. return $this->warmUpTwigCache($output);
  54. }
  55. if ($input->getOption('routing') === true) {
  56. return $this->warmUpRoutingCache($output);
  57. }
  58. $output->writeln('Warming up all caches.', OutputInterface::VERBOSITY_VERBOSE);
  59. $twigCode = $this->warmUptwigCache($output);
  60. if ($twigCode !== 0) {
  61. $output->writeln('Twig cache generation had an error.');
  62. return $twigCode;
  63. }
  64. $routingCode = $this->warmUpTwigCache($output);
  65. if ($routingCode !== 0) {
  66. $output->writeln('Routing cache generation had an error.');
  67. return $twigCode;
  68. }
  69. $output->writeln('Warm up of all caches done.', OutputInterface::VERBOSITY_VERBOSE);
  70. return 0;
  71. }
  72. private function warmUpRoutingCache(OutputInterface $output): int
  73. {
  74. $output->writeln('Warming up the routing cache', OutputInterface::VERBOSITY_VERBOSE);
  75. Routing::getDispatcher();
  76. if (is_file(Routing::ROUTES_CACHE_FILE)) {
  77. $output->writeln('Warm up done.', OutputInterface::VERBOSITY_VERBOSE);
  78. return 0;
  79. }
  80. $output->writeln(
  81. sprintf(
  82. 'Warm up did not work, the folder "%s" is probably not writable.',
  83. CACHE_DIR
  84. ),
  85. OutputInterface::VERBOSITY_NORMAL
  86. );
  87. return 1;
  88. }
  89. private function warmUpTwigCache(OutputInterface $output): int
  90. {
  91. global $cfg, $PMA_Config, $dbi;
  92. $output->writeln('Warming up the twig cache', OutputInterface::VERBOSITY_VERBOSE);
  93. $cfg['environment'] = 'production';
  94. $PMA_Config = new Config(CONFIG_FILE);
  95. $PMA_Config->set('environment', $cfg['environment']);
  96. $dbi = new DatabaseInterface(new DbiDummy());
  97. $tplDir = ROOT_PATH . 'templates';
  98. $tmpDir = ROOT_PATH . 'twig-templates';
  99. $loader = new FilesystemLoader($tplDir);
  100. $twig = new Environment($loader, [
  101. 'auto_reload' => true,
  102. 'cache' => $tmpDir,
  103. ]);
  104. $twig->setExtensions([
  105. new CoreExtension(),
  106. new I18nExtension(),
  107. new MessageExtension(),
  108. new PluginsExtension(),
  109. new RelationExtension(),
  110. new SanitizeExtension(),
  111. new TableExtension(),
  112. new TrackerExtension(),
  113. new TransformationsExtension(),
  114. new UrlExtension(),
  115. new UtilExtension(),
  116. ]);
  117. /** @var CacheInterface $twigCache */
  118. $twigCache = $twig->getCache(false);
  119. $output->writeln('Searching for files...', OutputInterface::VERBOSITY_VERY_VERBOSE);
  120. $replacements = [];
  121. $templates = new RecursiveIteratorIterator(
  122. new RecursiveDirectoryIterator($tplDir),
  123. RecursiveIteratorIterator::LEAVES_ONLY
  124. );
  125. $output->writeln('Warming templates', OutputInterface::VERBOSITY_VERY_VERBOSE);
  126. foreach ($templates as $file) {
  127. // Skip test files
  128. if (strpos($file->getPathname(), '/test/') !== false) {
  129. continue;
  130. }
  131. // force compilation
  132. if (! $file->isFile() || $file->getExtension() !== 'twig') {
  133. continue;
  134. }
  135. $name = str_replace($tplDir . '/', '', $file->getPathname());
  136. $output->writeln('Loading: ' . $name, OutputInterface::VERBOSITY_DEBUG);
  137. if (Environment::MAJOR_VERSION === 3) {
  138. $template = $twig->loadTemplate($twig->getTemplateClass($name), $name);
  139. } else {// @phpstan-ignore-line Twig 2
  140. $template = $twig->loadTemplate($name);// @phpstan-ignore-line Twig 2
  141. }
  142. // Generate line map
  143. $cacheFilename = $twigCache->generateKey($name, $twig->getTemplateClass($name));
  144. $template_file = 'templates/' . $name;
  145. $cache_file = str_replace($tmpDir, 'twig-templates', $cacheFilename);
  146. $replacements[$cache_file] = [$template_file, $template->getDebugInfo()];
  147. }
  148. $output->writeln('Writing replacements...', OutputInterface::VERBOSITY_VERY_VERBOSE);
  149. // Store replacements in JSON
  150. $handle = fopen($tmpDir . '/replace.json', 'w');
  151. if ($handle === false) {
  152. return 1;
  153. }
  154. fwrite($handle, (string) json_encode($replacements));
  155. fclose($handle);
  156. $output->writeln('Warm up done.', OutputInterface::VERBOSITY_VERBOSE);
  157. return 0;
  158. }
  159. }