Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
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() |
|
356 | |||
357 | /** |
||
358 | * Command execute method |
||
359 | * |
||
360 | * @return \Longman\TelegramBot\Entities\ServerResponse |
||
361 | * @throws \Longman\TelegramBot\Exception\TelegramException |
||
362 | */ |
||
363 | public function execute() |
||
423 | } |
||
424 |
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.