3 declare(strict_types
=1);
5 namespace PhpMyAdmin\Command
;
8 use PhpMyAdmin\DatabaseInterface
;
9 use PhpMyAdmin\Routing\Routing
;
10 use PhpMyAdmin\Template
;
11 use PhpMyAdmin\Tests\Stubs\DbiDummy
;
12 use RecursiveDirectoryIterator
;
13 use RecursiveIteratorIterator
;
15 use Symfony\Component\Console\Attribute\AsCommand
;
16 use Symfony\Component\Console\Command\Command
;
17 use Symfony\Component\Console\Input\InputArgument
;
18 use Symfony\Component\Console\Input\InputInterface
;
19 use Symfony\Component\Console\Output\OutputInterface
;
20 use Twig\Cache\CacheInterface
;
22 use function file_put_contents
;
24 use function json_encode
;
26 use function str_contains
;
27 use function str_replace
;
30 use const CONFIG_FILE
;
32 #[AsCommand(name: 'cache:warmup', description: 'Warms up the Twig templates cache.')]
33 final class CacheWarmupCommand
extends Command
35 protected function configure(): void
37 $this->addOption('twig', null, null, 'Warm up twig templates cache.');
38 $this->addOption('routing', null, null, 'Warm up routing cache.');
39 $this->addOption('twig-po', null, null, 'Warm up twig templates and write file mappings.');
43 InputArgument
::OPTIONAL
,
44 'Defines the environment (production or development) for twig warmup',
47 $this->setHelp('The <info>%command.name%</info> command warms up the cache of the Twig templates.');
50 protected function execute(InputInterface
$input, OutputInterface
$output): int
52 /** @var string $env */
53 $env = $input->getOption('env');
55 if ($input->getOption('twig') === true && $input->getOption('routing') === true) {
56 $output->writeln('Please specify --twig or --routing');
58 return Command
::FAILURE
;
61 if ($input->getOption('twig') === true) {
62 return $this->warmUpTwigCache($output, $env, false);
65 if ($input->getOption('twig-po') === true) {
66 return $this->warmUpTwigCache($output, $env, true);
69 if ($input->getOption('routing') === true) {
70 return $this->warmUpRoutingCache($output);
73 $output->writeln('Warming up all caches.', OutputInterface
::VERBOSITY_VERBOSE
);
74 $twigCode = $this->warmUpTwigCache($output, $env, false);
75 if ($twigCode !== 0) {
76 $output->writeln('Twig cache generation had an error.');
81 $routingCode = $this->warmUpRoutingCache($output);
82 if ($routingCode !== 0) {
83 $output->writeln('Routing cache generation had an error.');
88 $output->writeln('Warm up of all caches done.', OutputInterface
::VERBOSITY_VERBOSE
);
90 return Command
::SUCCESS
;
93 private function warmUpRoutingCache(OutputInterface
$output): int
95 $output->writeln('Warming up the routing cache', OutputInterface
::VERBOSITY_VERBOSE
);
96 Routing
::getDispatcher();
98 if (is_file(Routing
::ROUTES_CACHE_FILE
)) {
99 $output->writeln('Warm up done.', OutputInterface
::VERBOSITY_VERBOSE
);
101 return Command
::SUCCESS
;
106 'Warm up did not work, the folder "%s" is probably not writable.',
109 OutputInterface
::VERBOSITY_NORMAL
,
112 return Command
::FAILURE
;
115 private function warmUpTwigCache(
116 OutputInterface
$output,
118 bool $writeReplacements,
120 $output->writeln('Warming up the twig cache', OutputInterface
::VERBOSITY_VERBOSE
);
121 $config = Config
::getInstance();
122 $config->loadAndCheck(CONFIG_FILE
);
123 $config->settings
['environment'] = $environment;
124 $config->set('environment', $config->settings
['environment']);
125 DatabaseInterface
::$instance = new DatabaseInterface(new DbiDummy());
126 $tmpDir = ROOT_PATH
. 'twig-templates';
127 $twig = Template
::getTwigEnvironment($tmpDir, $config->config
->environment
=== 'development');
129 $output->writeln('Searching for files...', OutputInterface
::VERBOSITY_VERY_VERBOSE
);
131 $templates = new RecursiveIteratorIterator(
132 new RecursiveDirectoryIterator(Template
::TEMPLATES_FOLDER
),
133 RecursiveIteratorIterator
::LEAVES_ONLY
,
136 /** @var CacheInterface $twigCache */
137 $twigCache = $twig->getCache(false);
140 'Twig debug is: ' . ($twig->isDebug() ?
'enabled' : 'disabled'),
141 OutputInterface
::VERBOSITY_DEBUG
,
144 $output->writeln('Warming templates', OutputInterface
::VERBOSITY_VERY_VERBOSE
);
145 /** @var SplFileInfo $file */
146 foreach ($templates as $file) {
148 if (str_contains($file->getPathname(), '/tests/')) {
153 if (! $file->isFile() ||
$file->getExtension() !== 'twig') {
157 $name = str_replace(Template
::TEMPLATES_FOLDER
. '/', '', $file->getPathname());
158 $output->writeln('Loading: ' . $name, OutputInterface
::VERBOSITY_DEBUG
);
159 /** @psalm-suppress InternalMethod */
160 $template = $twig->loadTemplate($twig->getTemplateClass($name), $name);
162 if (! $writeReplacements) {
167 /** @psalm-suppress InternalMethod */
168 $cacheFilename = $twigCache->generateKey($name, $twig->getTemplateClass($name));
169 $templateFile = 'resources/templates/' . $name;
170 $cacheFile = str_replace($tmpDir, 'twig-templates', $cacheFilename);
171 /** @psalm-suppress InternalMethod */
172 $replacements[$cacheFile] = [$templateFile, $template->getDebugInfo()];
175 if (! $writeReplacements) {
176 $output->writeln('Warm up done.', OutputInterface
::VERBOSITY_VERBOSE
);
178 return Command
::SUCCESS
;
181 $output->writeln('Writing replacements...', OutputInterface
::VERBOSITY_VERY_VERBOSE
);
183 // Store replacements in JSON
184 if (file_put_contents($tmpDir . '/replace.json', (string) json_encode($replacements)) === false) {
185 return Command
::FAILURE
;
188 $output->writeln('Replacements written done.', OutputInterface
::VERBOSITY_VERBOSE
);
189 $output->writeln('Warm up done.', OutputInterface
::VERBOSITY_VERBOSE
);
191 return Command
::SUCCESS
;