CleanUp::runInner()   F
last analyzed

Complexity

Conditions 35
Paths 4010

Size

Total Lines 139
Code Lines 87

Duplication

Lines 0
Ratio 0 %

Importance

Changes 12
Bugs 0 Features 0
Metric Value
eloc 87
c 12
b 0
f 0
dl 0
loc 139
rs 0
cc 35
nc 4010
nop 0

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 Sunnysideup\DatabaseShareCleanUp;
4
5
use SilverStripe\Control\HTTPRequest;
6
use SilverStripe\Dev\BuildTask;
7
use Sunnysideup\DatabaseShareCleanUp\Api\Anonymiser;
8
use Sunnysideup\DatabaseShareCleanUp\Api\DatabaseActions;
9
use Sunnysideup\Flush\FlushNow;
10
use Sunnysideup\Flush\FlushNowImplementor;
11
12
class CleanUp extends BuildTask
13
{
14
    /**
15
     * @var bool if set to FALSE, keep it from showing in the list
16
     *           and from being executable through URL or CLI
17
     */
18
    protected $enabled = true;
19
20
    /**
21
     * @var string Shown in the overview on the {@link TaskRunner}
22
     *             HTML or CLI interface. Should be short and concise, no HTML allowed.
23
     */
24
    protected $title = 'Cleanup and anonymise database - CAREFUL! Data will be deleted.';
25
26
    /**
27
     * @var string Describe the implications the task has,
28
     *             and the changes it makes. Accepts HTML formatting.
29
     */
30
    protected $description = 'Goes through database and deletes data that may expose personal information and bloat database.';
31
32
    protected $forReal = false;
33
34
    protected $anonymise = false;
35
36
    protected $removeObsolete = false;
37
38
    protected $removeOldVersions = false;
39
40
    protected $debug = false;
41
42
    protected $emptyFields = false;
43
44
    protected $removeRows = false;
45
46
    protected $selectedTables = false;
47
48
    protected $selectedTableList = [];
49
50
    protected $data = [];
51
52
    private static $tables_to_delete_forever = [];
53
54
    private static $tables_to_be_cleaned = [];
55
56
    private static $fields_to_be_cleaned = [];
57
58
    private static $field_table_comboes_to_be_cleaned = [];
59
60
    private static $tables_to_keep = [];
61
62
    private static $fields_to_keep = [
63
        'ClassName',
64
        'Created',
65
        'LastEdited',
66
    ];
67
68
    private static $field_table_combos_to_keep = [];
69
70
    private static $max_table_size_in_mb = 20;
71
72
    private static $max_column_size_in_mb = 2;
73
74
    private static $dependencies = [
75
        'anonymiser' => '%$' . Anonymiser::class,
76
        'database' => '%$' . DatabaseActions::class,
77
    ];
78
79
    private $anonymiser;
80
81
    private $database;
82
83
    /**
84
     * Set a custom url segment (to follow dev/tasks/).
85
     *
86
     * @config
87
     *
88
     * @var string
89
     */
90
    private static $segment = 'database-share-clean-up';
91
92
    public function setAnonymiser($anonymiser)
93
    {
94
        $this->anonymiser = $anonymiser;
95
96
        return $this;
97
    }
98
99
    public function setDatabase($database)
100
    {
101
        $this->database = $database;
102
103
        return $this;
104
    }
105
106
    /**
107
     * Implement this method in the task subclass to
108
     * execute via the TaskRunner.
109
     *
110
     * @param HTTPRequest $request
111
     */
112
    public function run($request)
113
    {
114
        $this->anonymise = (bool) $request->getVar('anonymise');
115
        $this->removeObsolete = (bool) $request->getVar('removeobsolete');
116
        $this->removeOldVersions = (bool) $request->getVar('removeoldversions');
117
        $this->removeRows = (bool) $request->getVar('removerows');
118
        $this->emptyFields = (bool) $request->getVar('emptyfields');
119
120
        $this->selectedTables = (bool) $request->getVar('selectedtables');
121
        $this->debug = (bool) $request->getVar('debug');
122
        $this->forReal = (bool) $request->getVar('forreal');
123
        if ($this->forReal) {
124
            $this->debug = true;
125
        }
126
        $this->selectedTableList = $request->getVar('selectedtablelist') ?? [];
127
128
        $this->anonymiser->setDatabaseActions($this->database);
129
        $this->database->setForReal($this->forReal);
130
        $this->database->setDebug($this->debug);
131
132
        if ($this->forReal) {
133
            FlushNowImplementor::do_flush('<h3>Running in FOR REAL mode</h3>', 'bad');
134
        } else {
135
            FlushNowImplementor::do_flush('<h3>Not runing FOR REAL</h3>', 'good');
136
        }
137
        if ($this->anonymise) {
138
            $this->anonymiser->AnonymisePresets();
139
        }
140
141
        $this->createForm();
142
        $this->runInner();
143
        $this->createTable();
144
        echo "MEMORY USED:" . $this->formatBytes(memory_get_peak_usage());
145
    }
146
147
    protected function runInner()
148
    {
149
        $maxTableSize = $this->Config()->get('max_table_size_in_mb');
150
        $maxColumnSize = $this->Config()->get('max_column_size_in_mb');
151
152
        $tablesToDeleteForever = $this->Config()->get('tables_to_delete_forever');
153
154
        $tablesToKeep = $this->Config()->get('tables_to_keep');
155
        $fieldsToKeep = $this->Config()->get('fields_to_keep');
156
        $fieldTableCombosToKeep = $this->Config()->get('field_table_combos_to_keep');
157
158
        $tablesToBeCleaned = $this->Config()->get('tables_to_be_cleaned');
159
        $fieldsToBeCleaned = $this->Config()->get('fields_to_be_cleaned');
160
        $tableFieldCombosToBeCleaned = $this->Config()->get('field_table_comboes_to_be_cleaned');
161
162
        $tables = $this->database->getAllTables();
163
        foreach ($tables as $tableName) {
164
            if (!$this->database->tableExists($tableName)) {
165
                continue;
166
            }
167
            $this->data[$tableName] = [
168
                'TableName' => $tableName,
169
                'SizeAfter' => 0,
170
                'SizeBefore' => 0,
171
                'Actions' => [],
172
            ];
173
174
            if (in_array($tableName, $tablesToKeep, true)) {
175
                if ($this->debug) {
176
                    $this->data[$tableName]['Actions'][] = 'Skipped because it is in list of tables to keep.';
177
                }
178
179
                continue;
180
            }
181
182
            if ($this->removeObsolete) {
183
                if (in_array($tableName, $tablesToDeleteForever, true)) {
184
                    $this->database->deleteTable($tableName);
185
                    $this->data[$tableName]['Actions'][] = 'DELETING FOREVER.';
186
187
                    continue;
188
                }
189
                $outcome = $this->database->deleteObsoleteTables($tableName);
190
                if ($outcome) {
191
                    $this->data[$tableName]['Actions'][] = 'Deleted because it is obsolete.';
192
                }
193
            }
194
195
            if ($this->database->isEmptyTable($tableName)) {
196
                if ($this->debug) {
197
                    $this->data[$tableName]['Actions'][] = 'Skipped because table is empty.';
198
                }
199
200
                continue;
201
            }
202
            $this->data[$tableName]['SizeBefore'] = $this->database->getTableSizeInMegaBytes($tableName);
203
            if ($this->selectedTables && !in_array($tableName, $this->selectedTableList, true)) {
204
                $this->data[$tableName]['Actions'][] = 'Skipped because it is not a selected table.';
205
206
                continue;
207
            }
208
209
            if ($this->removeOldVersions) {
210
                $outcome = $this->database->emptyVersionedTable($tableName);
211
                if ($outcome) {
212
                    $this->data[$tableName]['Actions'][] = 'Remove all versions.';
213
                }
214
            }
215
216
            if ($this->anonymise) {
217
                $outcome = $this->anonymiser->AnonymiseTable($tableName);
218
                if ($outcome) {
219
                    $this->data[$tableName]['Actions'][] = 'Anonymised Table.';
220
                }
221
            }
222
            //get fields
223
            $fields = $this->database->getAllFieldsForOneTable($tableName);
224
225
            foreach ($fields as $fieldName) {
226
                if ('ID' === substr((string) $fieldName, -2)) {
227
                    if ($this->debug) {
228
                        $this->data[$tableName]['Actions'][] = ' ... ' . $fieldName . ': skipping!';
229
                    }
230
231
                    continue;
232
                }
233
                if (in_array($fieldName, $fieldsToKeep, true)) {
234
                    if ($this->debug) {
235
                        $this->data[$tableName]['Actions'][] = ' ... ' . $fieldName . ': skipping (field is marked as KEEP)!';
236
                    }
237
238
                    continue;
239
                }
240
241
                $combo = $tableName . '.' . $fieldName;
242
                if (in_array($combo, $fieldTableCombosToKeep, true)) {
243
                    if ($this->debug) {
244
                        $this->data[$tableName]['Actions'][] = ' ... ' . $fieldName . ': skipping (table.field is marked as KEEP).';
245
                    }
246
247
                    continue;
248
                }
249
                if ($this->anonymise) {
250
                    $outcome = $this->anonymiser->AnonymiseTableField($tableName, $fieldName);
251
                    if ($outcome) {
252
                        $this->data[$tableName]['Actions'][] = ' ... ' . $fieldName . ': anonymised.';
253
                    }
254
                }
255
                if ($this->emptyFields) {
256
                    $columnSize = $this->database->getColumnSizeInMegabytes($tableName, $fieldName);
257
                    $test1 = $columnSize > $maxColumnSize;
258
                    $test2 = in_array($fieldName, $fieldsToBeCleaned, true);
259
                    $test3 = in_array($combo, $tableFieldCombosToBeCleaned, true);
260
                    if ($test1 || $test2 || $test3) {
261
                        $percentageToKeep = $test2 || $test3 ? 0 : $maxColumnSize / $columnSize;
262
                        $outcome = $this->database->removeOldColumnsFromTable($tableName, $fieldName, $percentageToKeep);
263
                        if ($outcome) {
264
                            $this->data[$tableName]['Actions'][] = ' ... ' . $fieldName . ': Removed most rows.';
265
                        }
266
                    }
267
                }
268
            }
269
270
            // clean table
271
            if ($this->removeRows) {
272
                $removeAllRows = in_array($tableName, $tablesToBeCleaned, true);
273
                if ($removeAllRows) {
274
                    $this->database->removeOldRowsFromTable($tableName, 0.01);
275
                    $this->data[$tableName]['Actions'][] = 'Removed most rows.';
276
                } else {
277
                    $tableSize = $this->database->getTableSizeInMegaBytes($tableName);
278
                    if ($tableSize > $maxTableSize) {
279
                        $percentageToKeep = $maxTableSize / $tableSize;
280
                        $this->database->removeOldRowsFromTable($tableName, $percentageToKeep);
281
                        $this->data[$tableName]['Actions'][] = 'Removed old rows.';
282
                    }
283
                }
284
            }
285
            $this->data[$tableName]['SizeAfter'] = $this->database->getTableSizeInMegaBytes($tableName);
286
        }
287
    }
288
289
    protected function createForm()
290
    {
291
        $anonymise = $this->anonymise ? 'checked="checked"' : '';
292
        $removeObsolete = $this->removeObsolete ? 'checked="checked"' : '';
293
        $removeOldVersions = $this->removeOldVersions ? 'checked="checked"' : '';
294
        $removeRows = $this->removeRows ? 'checked="checked"' : '';
295
        $emptyFields = $this->emptyFields ? 'checked="checked"' : '';
296
        $selectedTables = $this->selectedTables ? 'checked="checked"' : '';
297
        $forReal = $this->forReal ? 'checked="checked"' : '';
298
        $debug = $this->debug ? 'checked="checked"' : '';
299
        echo <<<html
300
        <h3>All sizes in Megabytes</h3>
301
        <form method="get">
302
            <div style="
303
                background-color: pink;
304
                width: 300px;
305
                padding: 1vw;
306
                position: fixed;
307
                right: 0;
308
                top: 0;
309
                bottom: 0;
310
                border: 1px solid red;
311
            ">
312
                <h4>What actions to take?</h4>
313
                <div class="field" style="padding: 10px;">
314
                    <input type="checkbox" name="anonymise" {$anonymise} />
315
                    <label>anonymise</label>
316
                </div>
317
                <div class="field" style="padding: 10px;">
318
                    <input type="checkbox" name="removeoldversions" {$removeOldVersions} />
319
                    <label>remove versioned table entries</label>
320
                </div>
321
                <div class="field" style="padding: 10px;">
322
                    <input type="checkbox" name="removeobsolete" {$removeObsolete} />
323
                    <label>remove obsolete tables</label>
324
                </div>
325
326
                <div class="field" style="padding: 10px;">
327
                    <input type="checkbox" name="emptyfields" {$emptyFields} />
328
                    <label>empty large fields</label>
329
                </div>
330
331
                <div class="field" style="padding: 10px;">
332
                    <input type="checkbox" name="removerows" {$removeRows} />
333
                    <label>remove old rows if there are too many (not recommended)</label>
334
                </div>
335
336
                <hr />
337
                <h4>How to apply?</h4>
338
                <div class="field" style="padding: 10px;">
339
                    <input type="checkbox" name="selectedtables" {$selectedTables} />
340
                    <label>apply to selected tables only?</label>
341
                </div>
342
                <div class="field" style="padding: 10px;">
343
                    <input type="checkbox" name="forreal" {$forReal} />
344
                    <label>do it for real?</label>
345
                </div>
346
                <hr />
347
                <h4>See more info?</h4>
348
                <div class="field" style="padding: 10px;">
349
                    <input type="checkbox" name="debug" {$debug} />
350
                    <label>debug</label>
351
                </div>
352
                <hr />
353
                <div class="field" style="padding: 10px;">
354
                    <input type="submit" value="let's do it!" />
355
                </div>
356
            </div>
357
358
359
html;
360
    }
361
362
    protected function createTable()
363
    {
364
        $tbody = '';
365
        $totalSizeBefore = 0;
366
        $totalSizeAfter = 0;
367
        usort(
368
            $this->data,
369
            function ($a, $b) {
370
                return $b['SizeBefore'] <=> $a['SizeBefore'];
371
            }
372
        );
373
        foreach ($this->data as $data) {
374
            $totalSizeBefore += $data['SizeBefore'];
375
            $totalSizeAfter += $data['SizeAfter'];
376
            $actions = '';
377
            if (count($data['Actions'])) {
378
                $actions = '
379
                        <ul>
380
                            <li>
381
                            ' . implode('</li><li>', $data['Actions']) . '
382
                            </li>
383
                        </ul>';
384
            }
385
            $tableList = empty($this->selectedTableList[$data['TableName']]) ? '' : 'checked="checked"';
386
            $tbody .= '
387
                <tr>
388
                    <td>
389
                        <input type="checkbox" name="selectedtablelist[]" value="' . $data['TableName'] . '" ' . $tableList . ' />
390
                    </td>
391
                    <td>
392
                        ' . $data['TableName'] . '
393
                        ' . $actions . '
394
                    </td>
395
                    <td style="text-align: center;">
396
                        ' . $data['SizeBefore'] . '
397
                    </td>
398
                    <td style="text-align: center;">
399
                        ' . $data['SizeAfter'] . '
400
                    </td>
401
                </tr>';
402
        }
403
        $tfoot = '
404
                <tr>
405
                    <th>
406
                        &nbsp;
407
                    </th>
408
                    <th>
409
                        TOTAL
410
                    </th>
411
                    <th>
412
                        ' . $totalSizeBefore . '
413
                    </th>
414
                    <th>
415
                        ' . $totalSizeAfter . '
416
                    </th>
417
                </tr>
418
        ';
419
        echo '
420
        <table border="1" style="width: calc(100% - 380px);">
421
            <thead>
422
                <tr>
423
                    <th width="5%">
424
                        Select
425
                    </th>
426
                    <th width="65%">
427
                        Table Name
428
                    </th>
429
                    <th style="text-align: center;" width="15%">
430
                        Before
431
                    </th>
432
                    <th style="text-align: center;" width="15%">
433
                        After
434
                    </th>
435
                </tr>
436
            </thead>
437
            <tfoot>' . $tfoot . '</tfoot>
438
            <tbody>' . $tbody . '</tbody>
439
        </table>
440
    </form>';
441
    }
442
443
    private function formatBytes($size, $precision = 2)
444
    {
445
        $base = log($size, 1024);
446
        $suffixes = array('', 'K', 'M', 'G', 'T');
447
448
        return round(pow(1024, $base - floor($base)), $precision) . ' ' . $suffixes[floor($base)];
449
    }
450
}
451