Completed
Push — ezp25946-migrate_files_to_othe... ( 73b918 )
by
unknown
16:11
created

MigrateFilesCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 2
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the MigrateFilesCommand class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Bundle\EzPublishIOBundle\Command;
10
11
use eZ\Bundle\EzPublishIOBundle\Migration\MigrationHandlerInterface;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Helper\ProgressBar;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Console\Question\ConfirmationQuestion;
18
19
class MigrateFilesCommand extends Command
20
{
21
    private $configuredMetadataHandlers;
22
23
    private $configuredBinarydataHandlers;
24
25
    public function __construct($configuredMetadataHandlers, $configuredBinarydataHandlers)
26
    {
27
        $this->configuredMetadataHandlers = $configuredMetadataHandlers;
28
        $this->configuredBinarydataHandlers = $configuredBinarydataHandlers;
29
        $this->configuredMetadataHandlers['default'] = [];
30
        $this->configuredBinarydataHandlers['default'] = [];
31
32
        parent::__construct();
33
    }
34
35
    protected function configure()
36
    {
37
        $this
38
            ->setName('ezplatform:io:migrate-files')
39
            ->setDescription('Migrates files from one IO repository to another')
40
            ->addOption('from', null, InputOption::VALUE_REQUIRED, 'Migrate from <from_metadata_handler>,<from_binarydata_handler>')
41
            ->addOption('to', null, InputOption::VALUE_REQUIRED, 'Migrate to <to_metadata_handler>,<to_binarydata_handler>')
42
            ->addOption('list-io-configs', null, InputOption::VALUE_NONE, 'List available IO configurations')
43
            ->addOption('skip-binary-files', null, InputOption::VALUE_NONE, 'Skip copying binary files')
44
            ->addOption('skip-media-files', null, InputOption::VALUE_NONE, 'Skip copying media files')
45
            ->addOption('skip-image-files', null, InputOption::VALUE_NONE, 'Skip copying image files')
46
            ->addOption('remove-files', null, InputOption::VALUE_NONE, 'Remove source files after copying')
47
            ->addOption('bulk-count', null, InputOption::VALUE_REQUIRED, 'Number of files processed at once', 100)
48
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute a dry run')
49
            ->setHelp(
50
                <<<EOT
51
The command <info>%command.name%</info> migrates files from one IO repository
52
to another.
53
54
It can for example be used to migrate local files from the default IO
55
configuration to a new IO configuration, like a clustered setup.
56
57
The <info>--from</info> and <info>--to</info> values must be specified as <info><metadata_handler>,<binarydata_handler></info>.
58
If <info>--from</info> is omitted, the default IO configuration will be used.
59
If <info>--to</info> is omitted, the first non-default IO configuration will be used.
60
61
<fg=red>During the script execution the files should not be modified. To avoid
62
surprises you are advised to create a backup and/or execute a dry run before
63
proceeding with actual update.</>
64
65
Since this script can potentially run for a very long time, to avoid memory
66
exhaustion run it in production environment using <info>--env=prod</info> switch.
67
68
EOT
69
            );
70
    }
71
72
    protected function execute(InputInterface $input, OutputInterface $output)
73
    {
74
        if ($input->getOption('list-io-configs')) {
75
            $this->outputConfiguredHandlers($output);
76
77
            return;
78
        }
79
80
        $output->writeln($this->getProcessedHelp());
81
82
        $fromHandlers = $input->getOption('from') ? explode(',', $input->getOption('from')) : null;
83
        $toHandlers = $input->getOption('to') ? explode(',', $input->getOption('to')) : null;
84
        if (!$this->areHandlerOptionsValid($fromHandlers, $toHandlers, $output)) {
85
            return;
86
        }
87
88
        if (!$fromHandlers) {
89
            $fromHandlers = ['default', 'default'];
90
        }
91
        if (!$toHandlers) {
92
            $toHandlers = [
93
                array_keys($this->configuredMetadataHandlers)[0],
94
                array_keys($this->configuredBinarydataHandlers)[0]
95
            ];
96
        }
97
98
        $output->writeln([
99
            "Migrating from '$fromHandlers[0],$fromHandlers[1]' to '$toHandlers[0],$toHandlers[1]'",
100
            '',
101
        ]);
102
103
        $binaryFilesCount = $mediaFilesCount = $imageFilesCount = 0;
104
        if (!$input->getOption('skip-binary-files')) {
105
            //TODO
106
            $binaryFilesCount = $this->migrationHandlerRegistry
0 ignored issues
show
Bug introduced by
The property migrationHandlerRegistry does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
107
                ->getItem('ezpublish.core.io.migration_handler.binary_file')
108
                ->countFiles($fromHandlers[0], $fromHandlers[1]);
109
        }
110
        if (!$input->getOption('skip-media-files')) {
111
            //TODO
112
            $mediaFilesCount = $this->migrationHandlerRegistry
113
                ->getItem('ezpublish.core.io.migration_handler.media_file')
114
                ->countFiles($fromHandlers[0], $fromHandlers[1]);
115
        }
116
        if (!$input->getOption('skip-image-files')) {
117
            //TODO
118
            $imageFilesCount = $this->migrationHandlerRegistry
119
                ->getItem('ezpublish.core.io.migration_handler.image_file')
120
                ->countFiles($fromHandlers[0], $fromHandlers[1]);
121
        }
122
123
        $totalCount = $binaryFilesCount + $mediaFilesCount + $imageFilesCount;
124
        $output->writeln([
125
            "Found total files to update: $totalCount",
126
            "(binary files: $binaryFilesCount, media files: $mediaFilesCount, image files: $imageFilesCount)",
127
            '',
128
        ]);
129
130
        if ($totalCount == 0) {
131
            $output->writeln('Nothing to process.');
132
133
            return;
134
        }
135
136
        $helper = $this->getHelper('question');
137
        $question = new ConfirmationQuestion(
138
            '<question>Are you sure you want to proceed?</question> ',
139
            false
140
        );
141
142
        if (!$helper->ask($input, $output, $question)) {
143
            $output->writeln('Aborting.');
144
145
            return;
146
        }
147
148
        $bulkCount = $input->getOption('bulk-count');
149
        $dryRun = $input->getOption('dry-run');
150
151
        if (!$input->getOption('skip-binary-files')) {
152
            $output->writeln('Migrating binary files');
153
            //TODO
154
            $this->migrateFiles(
155
                $this->migrationHandlerRegistry->getItem('ezpublish.core.io.migration_handler.binary_file'),
156
                $binaryFilesCount,
157
                $bulkCount,
158
                $dryRun,
159
                $fromHandlers,
160
                $toHandlers,
161
                $output
162
            );
163
        }
164
        if (!$input->getOption('skip-media-files')) {
165
            $output->writeln('Migrating media files');
166
            //TODO
167
            $this->migrateFiles(
168
                $this->migrationHandlerRegistry->getItem('ezpublish.core.io.migration_handler.media_file'),
169
                $mediaFilesCount,
170
                $bulkCount,
171
                $dryRun,
172
                $fromHandlers,
173
                $toHandlers,
174
                $output
175
            );
176
        }
177
        if (!$input->getOption('skip-image-files')) {
178
            $output->writeln('Migrating image files');
179
            //TODO
180
            $this->migrateFiles(
181
                $this->migrationHandlerRegistry->getItem('ezpublish.core.io.migration_handler.image_file'),
182
                $imageFilesCount,
183
                $bulkCount,
184
                $dryRun,
185
                $fromHandlers,
186
                $toHandlers,
187
                $output
188
            );
189
        }
190
    }
191
192
    /**
193
     * Output the configured meta/binary data handlers.
194
     *
195
     * @param OutputInterface $output
196
     */
197
    protected function outputConfiguredHandlers(OutputInterface $output)
198
    {
199
        $output->writeln(
200
            'Configured meta data handlers: ' . implode(', ', array_keys($this->configuredMetadataHandlers))
201
        );
202
        $output->writeln(
203
            'Configured binary data handlers: ' . implode(', ', array_keys($this->configuredBinarydataHandlers))
204
        );
205
    }
206
207
    /**
208
     * Verify that the handler options have been set to meaningful values.
209
     *
210
     * @param mixed $fromHandlers
211
     * @param mixed $toHandlers
212
     * @param OutputInterface $output
213
     * @return bool
214
     */
215
    protected function areHandlerOptionsValid(
216
        $fromHandlers,
217
        $toHandlers,
218
        OutputInterface $output
219
    ) {
220 View Code Duplication
        if ($fromHandlers) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
            if (count($fromHandlers) !== 2) {
222
                $output->writeln("Enter two comma separated values for the --from option: <from_metadata_handler>,<from_binarydata_handler>");
223
224
                return false;
225
            }
226
227
            if (!in_array($fromHandlers[0], array_keys($this->configuredMetadataHandlers))) {
228
                $output->writeln("From meta data handler '$fromHandlers[0]' is not configured.");
229
                $this->outputConfiguredHandlers($output);
230
231
                return false;
232
            }
233
234
            if (!in_array($fromHandlers[1], array_keys($this->configuredBinarydataHandlers))) {
235
                $output->writeln("From binary data handler '$fromHandlers[1]' is not configured.");
236
                $this->outputConfiguredHandlers($output);
237
238
                return false;
239
            }
240
        }
241
242 View Code Duplication
        if ($toHandlers) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
243
            if (count($toHandlers) !== 2) {
244
                $output->writeln("Enter two comma separated values for the --to option: <to_metadata_handler>,<to_binarydata_handler>");
245
246
                return false;
247
            }
248
249
            if (!in_array($toHandlers[0], array_keys($this->configuredMetadataHandlers))) {
250
                $output->writeln("To meta data handler '$toHandlers[0]' is not configured.");
251
                $this->outputConfiguredHandlers($output);
252
253
                return false;
254
            }
255
256
            if (!in_array($toHandlers[1], array_keys($this->configuredBinarydataHandlers))) {
257
                $output->writeln("To binary data handler '$toHandlers[1]' is not configured.");
258
                $this->outputConfiguredHandlers($output);
259
260
                return false;
261
            }
262
        }
263
264
        if ($fromHandlers && $toHandlers && $fromHandlers === $toHandlers) {
265
            $output->writeln("From and to handlers are the same. Nothing to do.");
266
267
            return false;
268
        }
269
270
        return true;
271
    }
272
273
    /**
274
     * Migrate files.
275
     *
276
     * @param MigrationHandlerInterface $migrationHandler
277
     * @param int $fileCount
278
     * @param int $bulkCount
279
     * @param bool $dryRun
280
     * @param mixed $fromHandlers,
0 ignored issues
show
Documentation introduced by
There is no parameter named $fromHandlers,. Did you maybe mean $fromHandlers?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
281
     * @param mixed $toHandlers,
0 ignored issues
show
Documentation introduced by
There is no parameter named $toHandlers,. Did you maybe mean $toHandlers?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
282
     * @param OutputInterface $output
283
     */
284
    protected function migrateFiles(
285
        MigrationHandlerInterface $migrationHandler,
286
        $fileCount,
287
        $bulkCount,
288
        $dryRun,
289
        $fromHandlers,
290
        $toHandlers,
291
        OutputInterface $output
292
    ) {
293
        $passCount = ceil($fileCount / $bulkCount);
294
295
        $progress = new ProgressBar($output, $fileCount);
296
        $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%');
297
298
        $output->writeln('');
299
        $progress->start();
300
301
        for ($pass = 0; $pass <= $passCount; ++$pass) {
302
            $fileDataPass = $migrationHandler->loadFileData($bulkCount, $pass);
303
304
            foreach ($fileDataPass as $fileData) {
305
                if (!$dryRun) {
306
                    $migrationHandler->migrateFile(
307
                        $fileData,
308
                        $fromHandlers[0],
309
                        $fromHandlers[1],
310
                        $toHandlers[0],
311
                        $toHandlers[1]
312
                    );
313
                }
314
315
                $progress->clear();
316
317
                $output->write("\r");
318
                $output->writeln('Updated file ' . $fileData->name);
319
                $output->write("\r");
320
321
                $progress->display();
322
323
                $progress->advance();
324
            }
325
        }
326
327
        $progress->finish();
328
329
        $output->writeln('');
330
    }
331
}
332