Completed
Push — master ( fae82d...75d992 )
by Armando
05:54 queued 03:56
created

CleanupCommand::execute()   B

Complexity

Conditions 7
Paths 51

Size

Total Lines 60
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 60
ccs 0
cts 32
cp 0
rs 7.4661
cc 7
eloc 37
nc 51
nop 0
crap 56

How to fix   Long Method   

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
        'botan_shortener',
72
        'callback_query',
73
        'chosen_inline_result',
74
        'conversation',
75
        'edited_message',
76
        'inline_query',
77
        'message',
78
        'request_limiter',
79
        'telegram_update',
80
    ];
81
82
    /**
83
     * By default, remove records older than X days/hours/anything from these tables.
84
     *
85
     * @var array
86
     */
87
    protected static $default_clean_older_than = [
88
        'botan_shortener'      => '30 days',
89
        'chat'                 => '365 days',
90
        'callback_query'       => '30 days',
91
        'chosen_inline_result' => '30 days',
92
        'conversation'         => '30 days',
93
        'edited_message'       => '30 days',
94
        'inline_query'         => '30 days',
95
        'message'              => '30 days',
96
        'request_limiter'      => '1 minute',
97
        'telegram_update'      => '30 days',
98
        'user'                 => '365 days',
99
        'user_chat'            => '365 days',
100
    ];
101
102
    /**
103
     * Set command config
104
     *
105
     * @param string $custom_time
106
     *
107
     * @return array
108
     */
109
    private function getSettings($custom_time = '')
110
    {
111
        $tables_to_clean      = self::$default_tables_to_clean;
112
        $user_tables_to_clean = $this->getConfig('tables_to_clean');
113
        if (is_array($user_tables_to_clean)) {
114
            $tables_to_clean = $user_tables_to_clean;
115
        }
116
117
        $clean_older_than      = self::$default_clean_older_than;
118
        $user_clean_older_than = $this->getConfig('clean_older_than');
119
        if (is_array($user_clean_older_than)) {
120
            $clean_older_than = array_merge($clean_older_than, $user_clean_older_than);
121
        }
122
123
        // Convert numeric-only values to days.
124
        array_walk($clean_older_than, function (&$time) use ($custom_time) {
125
            if (!empty($custom_time)) {
126
                $time = $custom_time;
127
            }
128
            if (is_numeric($time)) {
129
                $time .= ' days';
130
            }
131
        });
132
133
        return compact('tables_to_clean', 'clean_older_than');
134
    }
135
136
    /**
137
     * Get SQL queries array based on settings provided
138
     *
139
     * @param $settings
140
     *
141
     * @return array
142
     * @throws TelegramException
143
     */
144
    private function getQueries($settings)
145
    {
146
        if (empty($settings) || !is_array($settings)) {
147
            throw new TelegramException('Settings variable is not an array or is empty!');
148
        }
149
150
        // Convert all clean_older_than times to correct format.
151
        $clean_older_than = $settings['clean_older_than'];
152
        foreach ($clean_older_than as $table => $time) {
153
            $clean_older_than[$table] = date('Y-m-d H:i:s', strtotime('-' . $time));
154
        }
155
        $tables_to_clean = $settings['tables_to_clean'];
156
157
        $queries = [];
158
159
        if (in_array('telegram_update', $tables_to_clean, true)) {
160
            $queries[] = sprintf(
161
                'DELETE FROM `%3$s`
162
                WHERE `id` != \'%1$s\'
163
                  AND `chat_id` NOT IN (
164
                    SELECT `id`
165
                    FROM `%4$s`
166
                    WHERE `chat_id` = `%4$s`.`id`
167
                  )
168
                  AND (
169
                    `message_id` IS NOT NULL
170
                    AND `message_id` IN (
171
                      SELECT f.id
172
                      FROM `%5$s` f
173
                      WHERE `date` < \'%2$s\'
174
                    )
175
                  )
176
                  OR (
177
                    `edited_message_id` IS NOT NULL
178
                    AND `edited_message_id` IN (
179
                      SELECT f.id
180
                      FROM `%6$s` f
181
                      WHERE `edit_date` < \'%2$s\'
182
                    )
183
                  )
184
                  OR (
185
                    `inline_query_id` IS NOT NULL
186
                    AND `inline_query_id` IN (
187
                      SELECT f.id
188
                      FROM `%7$s` f
189
                      WHERE `created_at` < \'%2$s\'
190
                    )
191
                  )
192
                  OR (
193
                    `chosen_inline_result_id` IS NOT NULL
194
                    AND `chosen_inline_result_id` IN (
195
                      SELECT f.id
196
                      FROM `%8$s` f
197
                      WHERE `created_at` < \'%2$s\'
198
                    )
199
                  )
200
                  OR (
201
                    `callback_query_id` IS NOT NULL
202
                    AND `callback_query_id` IN (
203
                      SELECT f.id
204
                      FROM `%9$s` f
205
                      WHERE `created_at` < \'%2$s\'
206
                    )
207
                  )
208
            ',
209
                $this->getUpdate()->getUpdateId(),
210
                $clean_older_than['telegram_update'],
211
                TB_TELEGRAM_UPDATE,
212
                TB_CHAT,
213
                TB_MESSAGE,
214
                TB_EDITED_MESSAGE,
215
                TB_INLINE_QUERY,
216
                TB_CHOSEN_INLINE_RESULT,
217
                TB_CALLBACK_QUERY
218
            );
219
        }
220
221
        if (in_array('user_chat', $tables_to_clean, true)) {
222
            $queries[] = sprintf(
223
                'DELETE FROM `%1$s`
224
                WHERE `user_id` IN (
225
                  SELECT f.id
226
                  FROM `%2$s` f
227
                  WHERE `updated_at` < \'%3$s\'
228
                )
229
            ',
230
                TB_USER_CHAT,
231
                TB_USER,
232
                $clean_older_than['chat']
233
            );
234
        }
235
236
        // Simple.
237
        $simple_tables = [
238
            'user'            => ['table' => TB_USER, 'field' => 'updated_at'],
239
            'chat'            => ['table' => TB_CHAT, 'field' => 'updated_at'],
240
            'conversation'    => ['table' => TB_CONVERSATION, 'field' => 'updated_at'],
241
            'request_limiter' => ['table' => TB_REQUEST_LIMITER, 'field' => 'created_at'],
242
        ];
243
244
        // Botan table is only available if enabled.
245
        if (defined('TB_BOTAN_SHORTENER')) {
246
            $simple_tables['botan_shortener'] = ['table' => TB_BOTAN_SHORTENER, 'field' => 'created_at'];
247
        }
248
249
        foreach (array_intersect(array_keys($simple_tables), $tables_to_clean) as $table_to_clean) {
250
            $queries[] = sprintf(
251
                'DELETE FROM `%1$s`
252
                WHERE `%2$s` < \'%3$s\'
253
            ',
254
                $simple_tables[$table_to_clean]['table'],
255
                $simple_tables[$table_to_clean]['field'],
256
                $clean_older_than[$table_to_clean]
257
            );
258
        }
259
260
        // Queries.
261
        $query_tables = [
262
            'inline_query'         => ['table' => TB_INLINE_QUERY, 'field' => 'created_at'],
263
            'chosen_inline_result' => ['table' => TB_CHOSEN_INLINE_RESULT, 'field' => 'created_at'],
264
            'callback_query'       => ['table' => TB_CALLBACK_QUERY, 'field' => 'created_at'],
265
        ];
266
        foreach (array_intersect(array_keys($query_tables), $tables_to_clean) as $table_to_clean) {
267
            $queries[] = sprintf(
268
                'DELETE FROM `%1$s`
269
                WHERE `%2$s` < \'%3$s\'
270
                  AND `id` NOT IN (
271
                    SELECT `%4$s`
272
                    FROM `%5$s`
273
                    WHERE `%4$s` = `%1$s`.`id`
274
                  )
275
            ',
276
                $query_tables[$table_to_clean]['table'],
277
                $query_tables[$table_to_clean]['field'],
278
                $clean_older_than[$table_to_clean],
279
                $table_to_clean . '_id',
280
                TB_TELEGRAM_UPDATE
281
            );
282
        }
283
284
        // Messages
285
        if (in_array('edited_message', $tables_to_clean, true)) {
286
            $queries[] = sprintf(
287
                'DELETE FROM `%1$s`
288
                WHERE `edit_date` < \'%2$s\'
289
                  AND `id` NOT IN (
290
                    SELECT `message_id`
291
                    FROM `%3$s`
292
                    WHERE `edited_message_id` = `%1$s`.`id`
293
                  )
294
            ',
295
                TB_EDITED_MESSAGE,
296
                $clean_older_than['edited_message'],
297
                TB_TELEGRAM_UPDATE
298
            );
299
        }
300
301
        if (in_array('message', $tables_to_clean, true)) {
302
            $queries[] = sprintf(
303
                'DELETE FROM `%1$s`
304
                WHERE `date` < \'%2$s\'
305
                  AND `id` NOT IN (
306
                    SELECT `message_id`
307
                    FROM `%3$s`
308
                    WHERE `message_id` = `%1$s`.`id`
309
                  )
310
                  AND `id` NOT IN (
311
                    SELECT `message_id`
312
                    FROM `%4$s`
313
                    WHERE `message_id` = `%1$s`.`id`
314
                  )
315
            ',
316
                TB_MESSAGE,
317
                $clean_older_than['message'],
318
                TB_TELEGRAM_UPDATE,
319
                TB_CALLBACK_QUERY
320
            );
321
        }
322
323
        return $queries;
324
    }
325
326
    /**
327
     * Execution if MySQL is required but not available
328
     *
329
     * @return \Longman\TelegramBot\Entities\ServerResponse
330
     */
331 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...
332
    {
333
        $message = $this->getMessage();
334
        $chat_id = $message->getChat()->getId();
335
336
        $data = [
337
            'chat_id'    => $chat_id,
338
            'parse_mode' => 'Markdown',
339
            'text'       => '*No database connection!*',
340
        ];
341
342
        return Request::sendMessage($data);
343
    }
344
345
    /**
346
     * Command execute method
347
     *
348
     * @return \Longman\TelegramBot\Entities\ServerResponse
349
     * @throws \Longman\TelegramBot\Exception\TelegramException
350
     */
351
    public function execute()
352
    {
353
        $message = $this->getMessage();
354
        $user_id = $message->getFrom()->getId();
355
        $text    = $message->getText(true);
356
357
        $data = [
358
            'chat_id'    => $user_id,
359
            'parse_mode' => 'Markdown',
360
        ];
361
362
        $settings = $this->getSettings($text);
363
        $queries  = $this->getQueries($settings);
364
365
        $infos = [];
366
        foreach ($settings['tables_to_clean'] as $table) {
367
            $info = '*' . $table . '*';
368
369
            if (isset($settings['clean_older_than'][$table])) {
370
                $info .= ' (' . $settings['clean_older_than'][$table] . ')';
371
            }
372
373
            $infos[] = $info;
374
        }
375
376
        $data['text'] = 'Cleaning up tables:' . PHP_EOL . implode(PHP_EOL, $infos);
377
378
        Request::sendMessage($data);
379
380
        $rows = 0;
381
        $pdo  = DB::getPdo();
382
        try {
383
            $pdo->beginTransaction();
384
385
            foreach ($queries as $query) {
386
                if ($dbq = $pdo->query($query)) {
387
                    $rows += $dbq->rowCount();
388
                } else {
389
                    TelegramLog::error('Error while executing query: ' . $query);
390
                }
391
            }
392
393
            $pdo->commit();     // commit changes to the database and end transaction
394
        } catch (PDOException $e) {
395
            $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...)
396
397
            $data['text'] = '*Database cleanup failed!* _(check your error logs)_';
398
            Request::sendMessage($data);
399
400
            throw new TelegramException($e->getMessage());
401
        }
402
403
        if ($rows > 0) {
404
            $data['text'] = '*Database cleanup done!* _(removed ' . $rows . ' rows)_';
405
        } else {
406
            $data['text'] = '*No data to clean!*';
407
        }
408
409
        return Request::sendMessage($data);
410
    }
411
}
412