Completed
Push — develop ( 1785d9...6aa29b )
by Armando
02:47
created

CleanupCommand::getQueries()   C

Complexity

Conditions 10
Paths 129

Size

Total Lines 195

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 195
ccs 0
cts 178
cp 0
rs 5.94
c 0
b 0
f 0
cc 10
nc 129
nop 1
crap 110

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
 * This file is part of the TelegramBot package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Longman\TelegramBot\Commands\AdminCommands;
12
13
use Longman\TelegramBot\Commands\AdminCommand;
14
use Longman\TelegramBot\DB;
15
use Longman\TelegramBot\Exception\TelegramException;
16
use Longman\TelegramBot\Request;
17
use Longman\TelegramBot\TelegramLog;
18
use PDOException;
19
20
/**
21
 * User "/cleanup" command
22
 *
23
 * Configuration options:
24
 *
25
 * $telegram->setCommandConfig('cleanup', [
26
 *     // Define which tables should be cleaned.
27
 *     'tables_to_clean' => [
28
 *         'message',
29
 *         'edited_message',
30
 *     ],
31
 *     // Define how old cleaned entries should be.
32
 *     'clean_older_than' => [
33
 *         'message'        => '7 days',
34
 *         'edited_message' => '30 days',
35
 *     ]
36
 * );
37
 */
38
class CleanupCommand extends AdminCommand
39
{
40
    /**
41
     * @var string
42
     */
43
    protected $name = 'cleanup';
44
45
    /**
46
     * @var string
47
     */
48
    protected $description = 'Clean up the database from old records';
49
50
    /**
51
     * @var string
52
     */
53
    protected $usage = '/cleanup <days> or /cleanup <count> <unit> (e.g. 3 weeks)';
54
55
    /**
56
     * @var string
57
     */
58
    protected $version = '1.0.0';
59
60
    /**
61
     * @var bool
62
     */
63
    protected $need_mysql = true;
64
65
    /**
66
     * Default tables to clean, cleaning 'chat', 'user' and 'user_chat' by default is bad practice!
67
     *
68
     * @var array
69
     */
70
    protected static $default_tables_to_clean = [
71
        'callback_query',
72
        'chosen_inline_result',
73
        'conversation',
74
        'edited_message',
75
        'inline_query',
76
        'message',
77
        'request_limiter',
78
        'telegram_update',
79
    ];
80
81
    /**
82
     * By default, remove records older than X days/hours/anything from these tables.
83
     *
84
     * @var array
85
     */
86
    protected static $default_clean_older_than = [
87
        'chat'                 => '365 days',
88
        'callback_query'       => '30 days',
89
        'chosen_inline_result' => '30 days',
90
        'conversation'         => '30 days',
91
        'edited_message'       => '30 days',
92
        'inline_query'         => '30 days',
93
        'message'              => '30 days',
94
        'request_limiter'      => '1 minute',
95
        'telegram_update'      => '30 days',
96
        'user'                 => '365 days',
97
        'user_chat'            => '365 days',
98
    ];
99
100
    /**
101
     * Set command config
102
     *
103
     * @param string $custom_time
104
     *
105
     * @return array
106
     */
107
    private function getSettings($custom_time = '')
108
    {
109
        $tables_to_clean      = self::$default_tables_to_clean;
110
        $user_tables_to_clean = $this->getConfig('tables_to_clean');
111
        if (is_array($user_tables_to_clean)) {
112
            $tables_to_clean = $user_tables_to_clean;
113
        }
114
115
        $clean_older_than      = self::$default_clean_older_than;
116
        $user_clean_older_than = $this->getConfig('clean_older_than');
117
        if (is_array($user_clean_older_than)) {
118
            $clean_older_than = array_merge($clean_older_than, $user_clean_older_than);
119
        }
120
121
        // Convert numeric-only values to days.
122
        array_walk($clean_older_than, function (&$time) use ($custom_time) {
123
            if (!empty($custom_time)) {
124
                $time = $custom_time;
125
            }
126
            if (is_numeric($time)) {
127
                $time .= ' days';
128
            }
129
        });
130
131
        return compact('tables_to_clean', 'clean_older_than');
132
    }
133
134
    /**
135
     * Get SQL queries array based on settings provided
136
     *
137
     * @param $settings
138
     *
139
     * @return array
140
     * @throws TelegramException
141
     */
142
    private function getQueries($settings)
143
    {
144
        if (empty($settings) || !is_array($settings)) {
145
            throw new TelegramException('Settings variable is not an array or is empty!');
146
        }
147
148
        // Convert all clean_older_than times to correct format.
149
        $clean_older_than = $settings['clean_older_than'];
150
        foreach ($clean_older_than as $table => $time) {
151
            $clean_older_than[$table] = date('Y-m-d H:i:s', strtotime('-' . $time));
152
        }
153
        $tables_to_clean = $settings['tables_to_clean'];
154
155
        $queries = [];
156
157
        if (in_array('telegram_update', $tables_to_clean, true)) {
158
            $queries[] = sprintf(
159
                'DELETE FROM `%3$s`
160
                WHERE `id` != \'%1$s\'
161
                  AND `chat_id` NOT IN (
162
                    SELECT `id`
163
                    FROM `%4$s`
164
                    WHERE `chat_id` = `%4$s`.`id`
165
                    AND `updated_at` < \'%1$s\'
166
                  )
167
                  AND (
168
                    `message_id` IS NOT NULL
169
                    AND `message_id` IN (
170
                      SELECT f.id
171
                      FROM `%5$s` f
172
                      WHERE `date` < \'%2$s\'
173
                    )
174
                  )
175
                  OR (
176
                    `edited_message_id` IS NOT NULL
177
                    AND `edited_message_id` IN (
178
                      SELECT f.id
179
                      FROM `%6$s` f
180
                      WHERE `edit_date` < \'%2$s\'
181
                    )
182
                  )
183
                  OR (
184
                    `inline_query_id` IS NOT NULL
185
                    AND `inline_query_id` IN (
186
                      SELECT f.id
187
                      FROM `%7$s` f
188
                      WHERE `created_at` < \'%2$s\'
189
                    )
190
                  )
191
                  OR (
192
                    `chosen_inline_result_id` IS NOT NULL
193
                    AND `chosen_inline_result_id` IN (
194
                      SELECT f.id
195
                      FROM `%8$s` f
196
                      WHERE `created_at` < \'%2$s\'
197
                    )
198
                  )
199
                  OR (
200
                    `callback_query_id` IS NOT NULL
201
                    AND `callback_query_id` IN (
202
                      SELECT f.id
203
                      FROM `%9$s` f
204
                      WHERE `created_at` < \'%2$s\'
205
                    )
206
                  )
207
            ',
208
                $this->getUpdate()->getUpdateId(),
209
                $clean_older_than['telegram_update'],
210
                TB_TELEGRAM_UPDATE,
211
                TB_CHAT,
212
                TB_MESSAGE,
213
                TB_EDITED_MESSAGE,
214
                TB_INLINE_QUERY,
215
                TB_CHOSEN_INLINE_RESULT,
216
                TB_CALLBACK_QUERY
217
            );
218
        }
219
220
        if (in_array('user_chat', $tables_to_clean, true)) {
221
            $queries[] = sprintf(
222
                'DELETE FROM `%1$s`
223
                WHERE `user_id` IN (
224
                  SELECT f.id
225
                  FROM `%2$s` f
226
                  WHERE `updated_at` < \'%3$s\'
227
                )
228
            ',
229
                TB_USER_CHAT,
230
                TB_USER,
231
                $clean_older_than['chat']
232
            );
233
        }
234
235
        // Simple.
236
        $simple_tables = [
237
            'user'            => ['table' => TB_USER, 'field' => 'updated_at'],
238
            'chat'            => ['table' => TB_CHAT, 'field' => 'updated_at'],
239
            'conversation'    => ['table' => TB_CONVERSATION, 'field' => 'updated_at'],
240
            'request_limiter' => ['table' => TB_REQUEST_LIMITER, 'field' => 'created_at'],
241
        ];
242
243
        foreach (array_intersect(array_keys($simple_tables), $tables_to_clean) as $table_to_clean) {
244
            $queries[] = sprintf(
245
                'DELETE FROM `%1$s`
246
                WHERE `%2$s` < \'%3$s\'
247
            ',
248
                $simple_tables[$table_to_clean]['table'],
249
                $simple_tables[$table_to_clean]['field'],
250
                $clean_older_than[$table_to_clean]
251
            );
252
        }
253
254
        // Queries.
255
        $query_tables = [
256
            'inline_query'         => ['table' => TB_INLINE_QUERY, 'field' => 'created_at'],
257
            'chosen_inline_result' => ['table' => TB_CHOSEN_INLINE_RESULT, 'field' => 'created_at'],
258
            'callback_query'       => ['table' => TB_CALLBACK_QUERY, 'field' => 'created_at'],
259
        ];
260
        foreach (array_intersect(array_keys($query_tables), $tables_to_clean) as $table_to_clean) {
261
            $queries[] = sprintf(
262
                'DELETE FROM `%1$s`
263
                WHERE `%2$s` < \'%3$s\'
264
                  AND `id` NOT IN (
265
                    SELECT `%4$s`
266
                    FROM `%5$s`
267
                    WHERE `%4$s` = `%1$s`.`id`
268
                  )
269
            ',
270
                $query_tables[$table_to_clean]['table'],
271
                $query_tables[$table_to_clean]['field'],
272
                $clean_older_than[$table_to_clean],
273
                $table_to_clean . '_id',
274
                TB_TELEGRAM_UPDATE
275
            );
276
        }
277
278
        // Messages
279
        if (in_array('edited_message', $tables_to_clean, true)) {
280
            $queries[] = sprintf(
281
                'DELETE FROM `%1$s`
282
                WHERE `edit_date` < \'%2$s\'
283
                  AND `id` NOT IN (
284
                    SELECT `message_id`
285
                    FROM `%3$s`
286
                    WHERE `edited_message_id` = `%1$s`.`id`
287
                  )
288
            ',
289
                TB_EDITED_MESSAGE,
290
                $clean_older_than['edited_message'],
291
                TB_TELEGRAM_UPDATE
292
            );
293
        }
294
295
        if (in_array('message', $tables_to_clean, true)) {
296
            $queries[] = sprintf(
297
                'DELETE FROM `%1$s`
298
                WHERE id IN (
299
                    SELECT id
300
                    FROM (
301
                        SELECT id
302
                        FROM  `%1$s`
303
                        WHERE `date` < \'%2$s\'
304
                          AND `id` NOT IN (
305
                            SELECT `message_id`
306
                            FROM `%3$s`
307
                            WHERE `message_id` = `%1$s`.`id`
308
                          )
309
                          AND `id` NOT IN (
310
                            SELECT `message_id`
311
                            FROM `%4$s`
312
                            WHERE `message_id` = `%1$s`.`id`
313
                          )
314
                          AND `id` NOT IN (
315
                            SELECT `message_id`
316
                            FROM `%5$s`
317
                            WHERE `message_id` = `%1$s`.`id`
318
                          )
319
                          AND `id` NOT IN (
320
                            SELECT a.`reply_to_message` FROM `%1$s` a
321
                            INNER JOIN `%1$s` b ON b.`id` = a.`reply_to_message` AND b.`chat_id` = a.`reply_to_chat`
322
                          )
323
                        ORDER BY `id` DESC
324
                     ) a
325
                 )
326
            ',
327
                TB_MESSAGE,
328
                $clean_older_than['message'],
329
                TB_EDITED_MESSAGE,
330
                TB_TELEGRAM_UPDATE,
331
                TB_CALLBACK_QUERY
332
            );
333
        }
334
335
        return $queries;
336
    }
337
338
    /**
339
     * Execution if MySQL is required but not available
340
     *
341
     * @return \Longman\TelegramBot\Entities\ServerResponse
342
     */
343 View Code Duplication
    public function executeNoDb()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
344
    {
345
        $message = $this->getMessage();
346
        $chat_id = $message->getChat()->getId();
347
348
        $data = [
349
            'chat_id'    => $chat_id,
350
            'parse_mode' => 'Markdown',
351
            'text'       => '*No database connection!*',
352
        ];
353
354
        return Request::sendMessage($data);
355
    }
356
357
    /**
358
     * Command execute method
359
     *
360
     * @return \Longman\TelegramBot\Entities\ServerResponse
361
     * @throws \Longman\TelegramBot\Exception\TelegramException
362
     */
363
    public function execute()
364
    {
365
        $message = $this->getMessage();
366
        $user_id = $message->getFrom()->getId();
367
        $text    = $message->getText(true);
368
369
        $data = [
370
            'chat_id'    => $user_id,
371
            'parse_mode' => 'Markdown',
372
        ];
373
374
        $settings = $this->getSettings($text);
375
        $queries  = $this->getQueries($settings);
376
377
        $infos = [];
378
        foreach ($settings['tables_to_clean'] as $table) {
379
            $info = '*' . $table . '*';
380
381
            if (isset($settings['clean_older_than'][$table])) {
382
                $info .= ' (' . $settings['clean_older_than'][$table] . ')';
383
            }
384
385
            $infos[] = $info;
386
        }
387
388
        $data['text'] = 'Cleaning up tables:' . PHP_EOL . implode(PHP_EOL, $infos);
389
390
        Request::sendMessage($data);
391
392
        $rows = 0;
393
        $pdo  = DB::getPdo();
394
        try {
395
            $pdo->beginTransaction();
396
397
            foreach ($queries as $query) {
398
                if ($dbq = $pdo->query($query)) {
399
                    $rows += $dbq->rowCount();
400
                } else {
401
                    TelegramLog::error('Error while executing query: ' . $query);
402
                }
403
            }
404
405
            $pdo->commit();     // commit changes to the database and end transaction
406
        } catch (PDOException $e) {
407
            $pdo->rollBack();   // rollback changes on exception (useful if you want to track down error - you can't replicate it when some of the data is already deleted...)
408
409
            $data['text'] = '*Database cleanup failed!* _(check your error logs)_';
410
            Request::sendMessage($data);
411
412
            throw new TelegramException($e->getMessage());
413
        }
414
415
        if ($rows > 0) {
416
            $data['text'] = '*Database cleanup done!* _(removed ' . $rows . ' rows)_';
417
        } else {
418
            $data['text'] = '*No data to clean!*';
419
        }
420
421
        return Request::sendMessage($data);
422
    }
423
}
424