MigrateFileTask::run()   F
last analyzed

Complexity

Conditions 16
Paths 1536

Size

Total Lines 155
Code Lines 97

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 0 Features 0
Metric Value
cc 16
eloc 97
c 9
b 0
f 0
nc 1536
nop 1
dl 0
loc 155
rs 1.1854

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\Dev\Tasks;
4
5
use Bramus\Monolog\Formatter\ColoredLineFormatter;
6
use Monolog\Handler\FilterHandler;
7
use Monolog\Handler\StreamHandler;
8
use Monolog\Logger;
9
use Psr\Log\LoggerInterface;
10
use SilverStripe\AssetAdmin\Helper\ImageThumbnailHelper;
0 ignored issues
show
Bug introduced by
The type SilverStripe\AssetAdmin\...er\ImageThumbnailHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use SilverStripe\Assets\Dev\Tasks\FileMigrationHelper;
12
use SilverStripe\Assets\Dev\Tasks\LegacyThumbnailMigrationHelper;
13
use SilverStripe\Assets\Dev\Tasks\SecureAssetsMigrationHelper;
14
use SilverStripe\Assets\Storage\AssetStore;
15
use SilverStripe\Assets\Storage\FileHashingService;
16
use SilverStripe\Core\Environment;
17
use SilverStripe\Core\Injector\Injector;
18
use SilverStripe\Dev\BuildTask;
19
20
/**
21
 * Migrates all 3.x file dataobjects to use the new DBFile field.
22
 */
23
class MigrateFileTask extends BuildTask
24
{
25
    private static $segment = 'MigrateFileTask';
0 ignored issues
show
introduced by
The private property $segment is not used, and could be removed.
Loading history...
26
27
    protected $title = 'Migrate File dataobjects from 3.x and successive iterations in 4.x';
28
29
    protected $defaultSubtasks = [
30
        'move-files',
31
        'move-thumbnails',
32
        'generate-cms-thumbnails',
33
        'fix-folder-permissions',
34
        'fix-secureassets',
35
    ];
36
37
    private static $dependencies = [
0 ignored issues
show
introduced by
The private property $dependencies is not used, and could be removed.
Loading history...
38
        'logger' => '%$' . LoggerInterface::class,
39
    ];
40
41
    /** @var Logger */
42
    private $logger;
43
44
    public function run($request)
45
    {
46
        $this->addLogHandlers();
47
48
        $args = $request->getVars();
49
        $this->validateArgs($args);
50
51
        Injector::inst()->get(FileHashingService::class)->enableCache();
52
53
        // Set max time and memory limit
54
        Environment::increaseTimeLimitTo();
55
        Environment::setMemoryLimitMax(-1);
56
        Environment::increaseMemoryLimitTo(-1);
57
58
        $this->extend('preFileMigration');
59
60
        $this->logger->warn(
61
            'Please read https://docs.silverstripe.org/en/4/developer_guides/files/file_migration/ ' .
62
            'before running this task.'
63
        );
64
65
        $subtasks = !empty($args['only']) ? explode(',', $args['only']) : $this->defaultSubtasks;
66
67
        $subtask = 'move-files';
68
        if (in_array($subtask, $subtasks)) {
69
            if (!class_exists(FileMigrationHelper::class)) {
70
                $this->logger->error("No file migration helper detected");
71
            } else {
72
                $this->extend('preFileMigrationSubtask', $subtask);
73
74
                $this->logger->notice("######################################################");
75
                $this->logger->notice("Migrating filesystem and database records ({$subtask})");
76
                $this->logger->notice("######################################################");
77
78
                FileMigrationHelper::singleton()
79
                    ->setLogger($this->logger)
80
                    ->run();
81
82
                // TODO Split file migration helper into two tasks,
83
                // and report back on their process counts consistently here
84
                // if ($count) {
85
                //     $this->logger->info("{$count} File objects upgraded");
86
                // } else {
87
                //     $this->logger->info("No File objects needed upgrading");
88
                // }
89
90
                $this->extend('postFileMigrationSubtask', $subtask);
91
            }
92
        }
93
94
        $subtask = 'move-thumbnails';
95
        if (in_array($subtask, $subtasks)) {
96
            if (!class_exists(LegacyThumbnailMigrationHelper::class)) {
97
                $this->logger->error("LegacyThumbnailMigrationHelper not found");
98
            } else {
99
                $this->extend('preFileMigrationSubtask', $subtask);
100
101
                $this->logger->notice("#############################################################");
102
                $this->logger->notice("Migrating existing thumbnails to new file format ({$subtask})");
103
                $this->logger->notice("#############################################################");
104
105
                $paths = LegacyThumbnailMigrationHelper::singleton()
106
                    ->setLogger($this->logger)
107
                    ->run($this->getStore());
108
109
                if ($paths) {
110
                    $this->logger->info(sprintf("%d thumbnails moved", count($paths)));
111
                } else {
112
                    $this->logger->info("No thumbnails needed to be moved");
113
                }
114
115
                $this->extend('postFileMigrationSubtask', $subtask);
116
            }
117
        }
118
119
        $subtask = 'generate-cms-thumbnails';
120
        if (in_array($subtask, $subtasks)) {
121
            if (!class_exists(ImageThumbnailHelper::class)) {
122
                $this->logger->error("ImageThumbnailHelper not found");
123
            } else {
124
                $this->extend('preFileMigrationSubtask', $subtask);
125
126
                $this->logger->notice("#############################################");
127
                $this->logger->notice("Generating new CMS UI thumbnails ({$subtask})");
128
                $this->logger->notice("#############################################");
129
130
                $count = ImageThumbnailHelper::singleton()
131
                    ->setLogger($this->logger)
132
                    ->run();
133
134
                if ($count > 0) {
135
                    $this->logger->info("Created {$count} CMS UI thumbnails");
136
                } else {
137
                    $this->logger->info("No CMS UI thumbnails needed to be created");
138
                }
139
140
                $this->extend('postFileMigrationSubtask', $subtask);
141
            }
142
        }
143
144
        $subtask = 'fix-folder-permissions';
145
        if (in_array($subtask, $subtasks)) {
146
            if (!class_exists(FixFolderPermissionsHelper::class)) {
147
                $this->logger->error("FixFolderPermissionsHelper not found");
148
            } else {
149
                $this->extend('preFileMigrationSubtask', $subtask);
150
151
                $this->logger->notice("####################################################");
152
                $this->logger->notice("Fixing secure-assets folder permissions ({$subtask})");
153
                $this->logger->notice("####################################################");
154
                $this->logger->debug('Only required if the 3.x project included silverstripe/secure-assets');
155
156
                $count = FixFolderPermissionsHelper::singleton()
157
                    ->setLogger($this->logger)
158
                    ->run();
159
160
                if ($count > 0) {
161
                    $this->logger->info("Repaired {$count} folders with broken CanViewType settings");
162
                } else {
163
                    $this->logger->info("No folders required fixes");
164
                }
165
166
                $this->extend('postFileMigrationSubtask', $subtask);
167
            }
168
        }
169
170
        $subtask = 'fix-secureassets';
171
        if (in_array($subtask, $subtasks)) {
172
            if (!class_exists(SecureAssetsMigrationHelper::class)) {
173
                $this->logger->error("SecureAssetsMigrationHelper not found");
174
            } else {
175
                $this->extend('preFileMigrationSubtask', $subtask);
176
177
                $this->logger->notice("#####################################################");
178
                $this->logger->notice("Fixing secure-assets folder restrictions ({$subtask})");
179
                $this->logger->notice("#####################################################");
180
                $this->logger->debug('Only required if the 3.x project included silverstripe/secure-assets');
181
182
                $paths = SecureAssetsMigrationHelper::singleton()
183
                    ->setLogger($this->logger)
184
                    ->run($this->getStore());
185
186
                if (count($paths) > 0) {
187
                    $this->logger->info(sprintf("Repaired %d folders broken folder restrictions", count($paths)));
188
                } else {
189
                    $this->logger->info("No folders required fixes");
190
                }
191
192
                $this->extend('postFileMigrationSubtask', $subtask);
193
            }
194
        }
195
196
        $this->extend('postFileMigration');
197
198
        $this->logger->info("Done!");
199
    }
200
201
    public function getDescription()
202
    {
203
        return <<<TXT
204
Imports all files referenced by File dataobjects into the new Asset Persistence Layer introduced in 4.0.
205
Moves existing thumbnails, and generates new thumbnail sizes for the CMS UI. Fixes file permissions.
206
If the task fails or times out, run it again and if possible the tasks will start where they left off.
207
You need to flush your cache after running this task via CLI.
208
See https://docs.silverstripe.org/en/4/developer_guides/files/file_migration/.
209
TXT;
210
    }
211
212
    /**
213
     * @param LoggerInterface $logger
214
     */
215
    public function setLogger(LoggerInterface $logger)
216
    {
217
        $this->logger = $logger;
0 ignored issues
show
Documentation Bug introduced by
$logger is of type Psr\Log\LoggerInterface, but the property $logger was declared to be of type Monolog\Logger. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
218
219
        return $this;
220
    }
221
222
    /**
223
     * @return AssetStore
224
     */
225
    protected function getStore()
226
    {
227
        return singleton(AssetStore::class);
228
    }
229
230
    /**
231
     * @param array $args
232
     * @throws \InvalidArgumentException
233
     */
234
    protected function validateArgs($args)
235
    {
236
        if (!empty($args['only'])) {
237
            if (array_diff(explode(',', $args['only']), $this->defaultSubtasks)) {
238
                throw new \InvalidArgumentException('Invalid subtasks detected: ' . $args['only']);
239
            }
240
        }
241
    }
242
243
    /**
244
     * TODO Refactor this whole mess into Symfony Console on a TaskRunner level,
245
     * with a thin wrapper to show coloured console output via a browser:
246
     * https://github.com/silverstripe/silverstripe-framework/issues/5542
247
     * @throws \Exception
248
     */
249
    protected function addLogHandlers()
250
    {
251
        // Using a global service here so other systems can control and redirect log output,
252
        // for example when this task is run as part of a queuedjob
253
        $logger = Injector::inst()->get(LoggerInterface::class)->withName('log');
254
255
        $formatter = new ColoredLineFormatter();
256
        $formatter->ignoreEmptyContextAndExtra();
257
258
        $errorHandler = new StreamHandler('php://stderr', Logger::ERROR);
259
        $errorHandler->setFormatter($formatter);
260
261
        $standardHandler = new StreamHandler('php://stdout');
262
        $standardHandler->setFormatter($formatter);
263
264
        // Avoid double logging of errors
265
        $standardFilterHandler = new FilterHandler(
266
            $standardHandler,
267
            Logger::DEBUG,
268
            Logger::WARNING
269
        );
270
271
        $logger->pushHandler($standardFilterHandler);
272
        $logger->pushHandler($errorHandler);
273
274
        $this->logger = $logger;
275
    }
276
}
277