Exponential 5.x Kernel 7.5.40 Exponential New Stack (Symfony-based) Kernel — PHP Class Reference

MigrateFilesCommand.php
Go to the documentation of this file.
1 <?php
2
7 namespace eZ\Bundle\EzPublishIOBundle\Command;
8
9 use eZ\Bundle\EzPublishIOBundle\Migration\FileListerRegistry;
10 use eZ\Bundle\EzPublishIOBundle\Migration\FileMigratorInterface;
11 use Symfony\Component\Console\Command\Command;
12 use Symfony\Component\Console\Input\InputInterface;
13 use Symfony\Component\Console\Input\InputOption;
14 use Symfony\Component\Console\Helper\ProgressBar;
15 use Symfony\Component\Console\Output\OutputInterface;
16 use Symfony\Component\Console\Question\ConfirmationQuestion;
17
18 final class MigrateFilesCommand extends Command
19 {
21 private $configuredMetadataHandlers;
22
24 private $configuredBinarydataHandlers;
25
27 private $fileListerRegistry;
28
30 private $fileListers;
31
33 private $fileMigrator;
34
35 public function __construct(
36 array $configuredMetadataHandlers,
37 array $configuredBinarydataHandlers,
38 FileListerRegistry $fileListerRegistry,
39 FileMigratorInterface $fileMigrator
40 ) {
41 $this->configuredMetadataHandlers = $configuredMetadataHandlers;
42 $this->configuredBinarydataHandlers = $configuredBinarydataHandlers;
43 if (!array_key_exists('default', $this->configuredMetadataHandlers)) {
44 $this->configuredMetadataHandlers['default'] = [];
45 }
46 if (!array_key_exists('default', $this->configuredBinarydataHandlers)) {
47 $this->configuredBinarydataHandlers['default'] = [];
48 }
49
50 $this->fileListerRegistry = $fileListerRegistry;
51 $this->fileMigrator = $fileMigrator;
52
53 foreach ($this->fileListerRegistry->getIdentifiers() as $fileListerIdentifier) {
54 $this->fileListers[] = $this->fileListerRegistry->getItem($fileListerIdentifier);
55 }
56
57 parent::__construct();
58 }
59
60 protected function configure()
61 {
62 $this
63 ->setName('ezplatform:io:migrate-files')
64 ->setDescription('Migrates files from one IO repository to another')
65 ->addOption('from', null, InputOption::VALUE_REQUIRED, 'Migrate from <from_metadata_handler>,<from_binarydata_handler>')
66 ->addOption('to', null, InputOption::VALUE_REQUIRED, 'Migrate to <to_metadata_handler>,<to_binarydata_handler>')
67 ->addOption('list-io-handlers', null, InputOption::VALUE_NONE, 'List available IO handlers')
68 ->addOption('bulk-count', null, InputOption::VALUE_REQUIRED, 'Number of files processed at once', 100)
69 ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute a dry run')
70 ->setHelp(
71 <<<EOT
72 The command <info>%command.name%</info> migrates files from one IO repository
73 to another. It can for example be used to migrate local files from the default
74 IO configuration to a new IO configuration, like a clustered setup.
75
76 <fg=red>NB: This command is experimental. Use with caution!</>
77
78 The <info>--from</info> and <info>--to</info> values must be specified as <info><metadata_handler>,<binarydata_handler></info>.
79 If <info>--from</info> is omitted, the default IO configuration will be used.
80 If <info>--to</info> is omitted, the first non-default IO configuration will be used.
81
82 <fg=red>During the script execution the files should not be modified. To avoid
83 surprises you are advised to create a backup and/or execute a dry run before
84 proceeding with actual update.</>
85
86 Since this script can potentially run for a very long time, to avoid memory
87 exhaustion run it in production environment using <info>--env=prod</info> switch.
88
89 EOT
90 );
91 }
92
93 protected function execute(InputInterface $input, OutputInterface $output)
94 {
95 if ($input->getOption('list-io-handlers')) {
96 $this->outputConfiguredHandlers($output);
97
98 return;
99 }
100
101 $bulkCount = (int)$input->getOption('bulk-count');
102 if ($bulkCount < 1) {
103 $output->writeln('The value for --bulk-count must be a positive integer.');
104
105 return;
106 }
107
108 $output->writeln($this->getProcessedHelp());
109
110 $fromHandlers = $input->getOption('from') ? explode(',', $input->getOption('from')) : null;
111 $toHandlers = $input->getOption('to') ? explode(',', $input->getOption('to')) : null;
112
113 if (!$fromHandlers) {
114 $fromHandlers = ['default', 'default'];
115 }
116 if (!$toHandlers) {
117 $toHandlers = [
118 array_keys($this->configuredMetadataHandlers)[0],
119 array_keys($this->configuredBinarydataHandlers)[0],
120 ];
121 }
122
123 if (!$this->validateHandlerOptions($fromHandlers, $toHandlers, $output)) {
124 return;
125 }
126
127 $output->writeln([
128 "Migrating from '$fromHandlers[0],$fromHandlers[1]' to '$toHandlers[0],$toHandlers[1]'",
129 '',
130 ]);
131
132 $totalCount = 0;
133 foreach ($this->fileListers as $fileLister) {
134 $fileLister->setIODataHandlersByIdentifiers(
135 $fromHandlers[0],
136 $fromHandlers[1],
137 $toHandlers[0],
138 $toHandlers[1]
139 );
140
141 $totalCount += $fileLister->countFiles();
142 }
143 $this->fileMigrator->setIODataHandlersByIdentifiers(
144 $fromHandlers[0],
145 $fromHandlers[1],
146 $toHandlers[0],
147 $toHandlers[1]
148 );
149
150 $output->writeln([
151 'Total number of files to migrate: ' . ($totalCount === null ? 'unknown' : $totalCount),
152 'This number does not include image aliases, but they will also be migrated.',
153 '',
154 ]);
155
156 if ($totalCount === 0) {
157 $output->writeln('Nothing to process.');
158
159 return;
160 }
161
162 if (!$input->getOption('no-interaction')) {
163 $helper = $this->getHelper('question');
164 $question = new ConfirmationQuestion(
165 '<question>Are you sure you want to proceed?</question> ',
166 false
167 );
168
169 if (!$helper->ask($input, $output, $question)) {
170 $output->writeln('Aborting.');
171
172 return;
173 }
174 }
175
176 $this->migrateFiles(
177 $totalCount,
178 $bulkCount,
179 $input->getOption('dry-run'),
180 $output
181 );
182 }
183
189 protected function outputConfiguredHandlers(OutputInterface $output)
190 {
191 $output->writeln(
192 'Configured meta data handlers: ' . implode(', ', array_keys($this->configuredMetadataHandlers))
193 );
194 $output->writeln(
195 'Configured binary data handlers: ' . implode(', ', array_keys($this->configuredBinarydataHandlers))
196 );
197 }
198
208 protected function validateHandlerOptions(
209 $fromHandlers,
210 $toHandlers,
211 OutputInterface $output
212 ) {
213 foreach (['From' => $fromHandlers, 'To' => $toHandlers] as $direction => $handlers) {
214 $lowerDirection = strtolower($direction);
215 if (count($handlers) !== 2) {
216 $output->writeln(
217 "Enter two comma separated values for the --$lowerDirection option: " .
218 "<{$lowerDirection}_metadata_handler>,<{$lowerDirection}_binarydata_handler>"
219 );
220
221 return false;
222 }
223
224 foreach (['meta' => $handlers[0], 'binary' => $handlers[1]] as $fileDataType => $handler) {
225 if (!in_array($handler, array_keys(
226 $fileDataType === 'meta' ? $this->configuredMetadataHandlers : $this->configuredBinarydataHandlers
227 ))) {
228 $output->writeln("$direction $fileDataType data handler '$handler' is not configured.");
229 $this->outputConfiguredHandlers($output);
230
231 return false;
232 }
233 }
234 }
235
236 if ($fromHandlers === $toHandlers) {
237 $output->writeln('From and to handlers are the same. Nothing to do.');
238
239 return false;
240 }
241
242 return true;
243 }
244
253 protected function migrateFiles(
254 $totalFileCount = null,
255 $bulkCount,
256 $dryRun,
257 OutputInterface $output
258 ) {
259 $progress = new ProgressBar($output, $totalFileCount);
260 if ($totalFileCount) {
261 $progress->setFormat("%message%\n %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%");
262 } else {
263 $progress->setFormat("%message%\n %current% [%bar%] %elapsed:6s% %memory:6s%");
264 }
265
266 $output->writeln('');
267 $progress->setMessage('Starting migration...');
268 $progress->start();
269
270 $elapsedFileCount = 0;
271 $timestamp = microtime(true);
272 $updateFrequency = 1;
273
274 foreach ($this->fileListers as $fileLister) {
275 $pass = 0;
276 do {
277 $metadataList = $fileLister->loadMetadataList($bulkCount, $pass * $bulkCount);
278
279 foreach ($metadataList as $metadata) {
280 if (!$dryRun) {
281 $this->fileMigrator->migrateFile($metadata);
282 }
283
284 $progress->setMessage('Updated file ' . $metadata->id);
285 $progress->advance();
286 ++$elapsedFileCount;
287
288 // Magic that ensures the progressbar is updated ca. once per second
289 if (($elapsedFileCount % $updateFrequency) === 0) {
290 $newTimestamp = microtime(true);
291 if ($newTimestamp - $timestamp > 0.5 && $updateFrequency > 1) {
292 $updateFrequency = (int)($updateFrequency / 2);
293 $progress->setRedrawFrequency($updateFrequency);
294 } elseif ($newTimestamp - $timestamp < 0.1 && $updateFrequency < 10000) {
295 $updateFrequency = $updateFrequency * 2;
296 $progress->setRedrawFrequency($updateFrequency);
297 }
298 $timestamp = $newTimestamp;
299 }
300 }
301
302 ++$pass;
303 } while (count($metadataList) > 0);
304 }
305
306 $progress->setMessage('');
307 $progress->finish();
308
309 $output->writeln("\n\nFinished processing $elapsedFileCount files.");
310 if ($totalFileCount && $totalFileCount > $elapsedFileCount) {
311 $output->writeln([
312 'Files that could not be migrated: ' . ($totalFileCount - $elapsedFileCount),
313 '',
314 ]);
315 }
316 }
317 }
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand
Definition: MigrateFilesCommand.php:19
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\$fileMigrator
$fileMigrator
Definition: MigrateFilesCommand.php:33
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\$fileListers
$fileListers
Definition: MigrateFilesCommand.php:30
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\execute
execute(InputInterface $input, OutputInterface $output)
Definition: MigrateFilesCommand.php:93
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\migrateFiles
migrateFiles( $totalFileCount=null, $bulkCount, $dryRun, OutputInterface $output)
Migrate files.
Definition: MigrateFilesCommand.php:253
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\validateHandlerOptions
validateHandlerOptions( $fromHandlers, $toHandlers, OutputInterface $output)
Verify that the handler options have been set to meaningful values.
Definition: MigrateFilesCommand.php:208
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\__construct
__construct(array $configuredMetadataHandlers, array $configuredBinarydataHandlers, FileListerRegistry $fileListerRegistry, FileMigratorInterface $fileMigrator)
Definition: MigrateFilesCommand.php:35
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\outputConfiguredHandlers
outputConfiguredHandlers(OutputInterface $output)
Output the configured meta/binary data handlers.
Definition: MigrateFilesCommand.php:189
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\$configuredBinarydataHandlers
$configuredBinarydataHandlers
Definition: MigrateFilesCommand.php:24
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\configure
configure()
Definition: MigrateFilesCommand.php:60
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\$fileListerRegistry
$fileListerRegistry
Definition: MigrateFilesCommand.php:27
eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand\$configuredMetadataHandlers
$configuredMetadataHandlers
Definition: MigrateFilesCommand.php:21
eZ\Bundle\EzPublishIOBundle\Migration\FileListerRegistry
A registry of FileListerInterfaces.
Definition: FileListerRegistry.php:13
eZ\Bundle\EzPublishIOBundle\Migration\FileMigratorInterface
Interface for file migrators, mandates the migrateFile method.
Definition: FileMigratorInterface.php:15
eZ\Bundle\EzPublishIOBundle\Command
Definition: MigrateFilesCommand.php:7
eZ\Bundle\EzPublishIOBundle\Migration\FileListerRegistry
Definition: ConfigurableRegistry.php:7
Copyright © 1998 – 2026 7x. All rights reserved. Licensed under the GNU GPLv2+.