Issues (1880)

src/Command/CacheWarmupCommand.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\Command;
6
7
use PhpMyAdmin\Config;
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;
14
use SplFileInfo;
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;
21
22
use function file_put_contents;
23
use function is_file;
24
use function json_encode;
25
use function sprintf;
26
use function str_contains;
27
use function str_replace;
28
29
use const CACHE_DIR;
30
use const CONFIG_FILE;
31
32
#[AsCommand(name: 'cache:warmup', description: 'Warms up the Twig templates cache.')]
33
final class CacheWarmupCommand extends Command
34
{
35
    protected function configure(): void
36
    {
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.');
40
        $this->addOption(
41
            'env',
42
            null,
43
            InputArgument::OPTIONAL,
44
            'Defines the environment (production or development) for twig warmup',
45
            'production',
46
        );
47
        $this->setHelp('The <info>%command.name%</info> command warms up the cache of the Twig templates.');
48
    }
49
50
    protected function execute(InputInterface $input, OutputInterface $output): int
51
    {
52
        /** @var string $env */
53
        $env = $input->getOption('env');
54
55
        if ($input->getOption('twig') === true && $input->getOption('routing') === true) {
56
            $output->writeln('Please specify --twig or --routing');
57
58
            return Command::FAILURE;
59
        }
60
61
        if ($input->getOption('twig') === true) {
62
            return $this->warmUpTwigCache($output, $env, false);
63
        }
64
65
        if ($input->getOption('twig-po') === true) {
66
            return $this->warmUpTwigCache($output, $env, true);
67
        }
68
69
        if ($input->getOption('routing') === true) {
70
            return $this->warmUpRoutingCache($output);
71
        }
72
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.');
77
78
            return $twigCode;
79
        }
80
81
        $routingCode = $this->warmUpRoutingCache($output);
82
        if ($routingCode !== 0) {
83
            $output->writeln('Routing cache generation had an error.');
84
85
            return $twigCode;
86
        }
87
88
        $output->writeln('Warm up of all caches done.', OutputInterface::VERBOSITY_VERBOSE);
89
90
        return Command::SUCCESS;
91
    }
92
93
    private function warmUpRoutingCache(OutputInterface $output): int
94
    {
95
        $output->writeln('Warming up the routing cache', OutputInterface::VERBOSITY_VERBOSE);
96
        Routing::getDispatcher();
97
98
        if (is_file(Routing::ROUTES_CACHE_FILE)) {
99
            $output->writeln('Warm up done.', OutputInterface::VERBOSITY_VERBOSE);
100
101
            return Command::SUCCESS;
102
        }
103
104
        $output->writeln(
105
            sprintf(
106
                'Warm up did not work, the folder "%s" is probably not writable.',
107
                CACHE_DIR,
108
            ),
109
            OutputInterface::VERBOSITY_NORMAL,
110
        );
111
112
        return Command::FAILURE;
113
    }
114
115
    private function warmUpTwigCache(
116
        OutputInterface $output,
117
        string $environment,
118
        bool $writeReplacements,
119
    ): int {
120
        $output->writeln('Warming up the twig cache', OutputInterface::VERBOSITY_VERBOSE);
121
        $config = Config::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Config::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

121
        $config = /** @scrutinizer ignore-deprecated */ Config::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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');
128
129
        $output->writeln('Searching for files...', OutputInterface::VERBOSITY_VERY_VERBOSE);
130
131
        $templates = new RecursiveIteratorIterator(
132
            new RecursiveDirectoryIterator(Template::TEMPLATES_FOLDER),
133
            RecursiveIteratorIterator::LEAVES_ONLY,
134
        );
135
136
        /** @var CacheInterface $twigCache */
137
        $twigCache = $twig->getCache(false);
138
        $replacements = [];
139
        $output->writeln(
140
            'Twig debug is: ' . ($twig->isDebug() ? 'enabled' : 'disabled'),
141
            OutputInterface::VERBOSITY_DEBUG,
142
        );
143
144
        $output->writeln('Warming templates', OutputInterface::VERBOSITY_VERY_VERBOSE);
145
        /** @var SplFileInfo $file */
146
        foreach ($templates as $file) {
147
            // Skip test files
148
            if (str_contains($file->getPathname(), '/tests/')) {
149
                continue;
150
            }
151
152
            // force compilation
153
            if (! $file->isFile() || $file->getExtension() !== 'twig') {
154
                continue;
155
            }
156
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);
161
162
            if (! $writeReplacements) {
163
                continue;
164
            }
165
166
            // Generate line map
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()];
173
        }
174
175
        if (! $writeReplacements) {
176
            $output->writeln('Warm up done.', OutputInterface::VERBOSITY_VERBOSE);
177
178
            return Command::SUCCESS;
179
        }
180
181
        $output->writeln('Writing replacements...', OutputInterface::VERBOSITY_VERY_VERBOSE);
182
183
        // Store replacements in JSON
184
        if (file_put_contents($tmpDir . '/replace.json', (string) json_encode($replacements)) === false) {
185
            return Command::FAILURE;
186
        }
187
188
        $output->writeln('Replacements written done.', OutputInterface::VERBOSITY_VERBOSE);
189
        $output->writeln('Warm up done.', OutputInterface::VERBOSITY_VERBOSE);
190
191
        return Command::SUCCESS;
192
    }
193
}
194