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:
Complex classes like DB often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use DB, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | class DB |
||
30 | { |
||
31 | /** |
||
32 | * MySQL credentials |
||
33 | * |
||
34 | * @var array |
||
35 | */ |
||
36 | protected static $mysql_credentials = []; |
||
37 | |||
38 | /** |
||
39 | * PDO object |
||
40 | * |
||
41 | * @var PDO |
||
42 | */ |
||
43 | protected static $pdo; |
||
44 | |||
45 | /** |
||
46 | * Table prefix |
||
47 | * |
||
48 | * @var string |
||
49 | */ |
||
50 | protected static $table_prefix; |
||
51 | |||
52 | /** |
||
53 | * Telegram class object |
||
54 | * |
||
55 | * @var Telegram |
||
56 | */ |
||
57 | protected static $telegram; |
||
58 | |||
59 | /** |
||
60 | * Initialize |
||
61 | * |
||
62 | * @param array $credentials Database connection details |
||
63 | * @param Telegram $telegram Telegram object to connect with this object |
||
64 | * @param string $table_prefix Table prefix |
||
65 | * @param string $encoding Database character encoding |
||
66 | * |
||
67 | * @return PDO PDO database object |
||
68 | * @throws TelegramException |
||
69 | */ |
||
70 | 9 | public static function initialize( |
|
102 | |||
103 | /** |
||
104 | * External Initialize |
||
105 | * |
||
106 | * Let you use the class with an external already existing Pdo Mysql connection. |
||
107 | * |
||
108 | * @param PDO $external_pdo_connection PDO database object |
||
109 | * @param Telegram $telegram Telegram object to connect with this object |
||
110 | * @param string $table_prefix Table prefix |
||
111 | * |
||
112 | * @return PDO PDO database object |
||
113 | * @throws TelegramException |
||
114 | */ |
||
115 | public static function externalInitialize( |
||
133 | |||
134 | /** |
||
135 | * Define all the tables with the proper prefix |
||
136 | */ |
||
137 | 9 | protected static function defineTables() |
|
161 | |||
162 | /** |
||
163 | * Check if database connection has been created |
||
164 | * |
||
165 | * @return bool |
||
166 | */ |
||
167 | 9 | public static function isDbConnected() |
|
171 | |||
172 | /** |
||
173 | * Get the PDO object of the connected database |
||
174 | * |
||
175 | * @return PDO |
||
176 | */ |
||
177 | public static function getPdo() |
||
181 | |||
182 | /** |
||
183 | * Fetch update(s) from DB |
||
184 | * |
||
185 | * @param int $limit Limit the number of updates to fetch |
||
186 | * @param string $id Check for unique update id |
||
187 | * |
||
188 | * @return array|bool Fetched data or false if not connected |
||
189 | * @throws TelegramException |
||
190 | */ |
||
191 | public static function selectTelegramUpdate($limit = null, $id = null) |
||
229 | |||
230 | /** |
||
231 | * Fetch message(s) from DB |
||
232 | * |
||
233 | * @param int $limit Limit the number of messages to fetch |
||
234 | * |
||
235 | * @return array|bool Fetched data or false if not connected |
||
236 | * @throws TelegramException |
||
237 | */ |
||
238 | public static function selectMessages($limit = null) |
||
268 | |||
269 | /** |
||
270 | * Convert from unix timestamp to timestamp |
||
271 | * |
||
272 | * @param int $time Unix timestamp (if empty, current timestamp is used) |
||
273 | * |
||
274 | * @return string |
||
275 | */ |
||
276 | 7 | protected static function getTimestamp($time = null) |
|
280 | |||
281 | /** |
||
282 | * Convert array of Entity items to a JSON array |
||
283 | * |
||
284 | * @todo Find a better way, as json_* functions are very heavy |
||
285 | * |
||
286 | * @param array|null $entities |
||
287 | * @param mixed $default |
||
288 | * |
||
289 | * @return mixed |
||
290 | */ |
||
291 | 6 | public static function entitiesArrayToJson($entities, $default = null) |
|
304 | |||
305 | /** |
||
306 | * Insert entry to telegram_update table |
||
307 | * |
||
308 | * @param string $update_id |
||
309 | * @param string|null $chat_id |
||
310 | * @param string|null $message_id |
||
311 | * @param string|null $edited_message_id |
||
312 | * @param string|null $channel_post_id |
||
313 | * @param string|null $edited_channel_post_id |
||
314 | * @param string|null $inline_query_id |
||
315 | * @param string|null $chosen_inline_result_id |
||
316 | * @param string|null $callback_query_id |
||
317 | * @param string|null $shipping_query_id |
||
318 | * @param string|null $pre_checkout_query_id |
||
319 | * @param string|null $poll_id |
||
320 | * |
||
321 | * @return bool If the insert was successful |
||
322 | * @throws TelegramException |
||
323 | */ |
||
324 | protected static function insertTelegramUpdate( |
||
372 | |||
373 | /** |
||
374 | * Insert users and save their connection to chats |
||
375 | * |
||
376 | * @param User $user |
||
377 | * @param string $date |
||
378 | * @param Chat $chat |
||
379 | * |
||
380 | * @return bool If the insert was successful |
||
381 | * @throws TelegramException |
||
382 | */ |
||
383 | 6 | public static function insertUser(User $user, $date = null, Chat $chat = null) |
|
440 | |||
441 | /** |
||
442 | * Insert chat |
||
443 | * |
||
444 | * @param Chat $chat |
||
445 | * @param string $date |
||
446 | * @param string $migrate_to_chat_id |
||
447 | * |
||
448 | * @return bool If the insert was successful |
||
449 | * @throws TelegramException |
||
450 | */ |
||
451 | 6 | public static function insertChat(Chat $chat, $date = null, $migrate_to_chat_id = null) |
|
501 | |||
502 | /** |
||
503 | * Insert request into database |
||
504 | * |
||
505 | * @todo self::$pdo->lastInsertId() - unsafe usage if expected previous insert fails? |
||
506 | * |
||
507 | * @param Update $update |
||
508 | * |
||
509 | * @return bool |
||
510 | * @throws TelegramException |
||
511 | */ |
||
512 | public static function insertRequest(Update $update) |
||
573 | |||
574 | /** |
||
575 | * Insert inline query request into database |
||
576 | * |
||
577 | * @param InlineQuery $inline_query |
||
578 | * |
||
579 | * @return bool If the insert was successful |
||
580 | * @throws TelegramException |
||
581 | */ |
||
582 | View Code Duplication | public static function insertInlineQueryRequest(InlineQuery $inline_query) |
|
617 | |||
618 | /** |
||
619 | * Insert chosen inline result request into database |
||
620 | * |
||
621 | * @param ChosenInlineResult $chosen_inline_result |
||
622 | * |
||
623 | * @return bool If the insert was successful |
||
624 | * @throws TelegramException |
||
625 | */ |
||
626 | View Code Duplication | public static function insertChosenInlineResultRequest(ChosenInlineResult $chosen_inline_result) |
|
661 | |||
662 | /** |
||
663 | * Insert callback query request into database |
||
664 | * |
||
665 | * @param CallbackQuery $callback_query |
||
666 | * |
||
667 | * @return bool If the insert was successful |
||
668 | * @throws TelegramException |
||
669 | */ |
||
670 | public static function insertCallbackQueryRequest(CallbackQuery $callback_query) |
||
730 | |||
731 | /** |
||
732 | * Insert shipping query request into database |
||
733 | * |
||
734 | * @param ShippingQuery $shipping_query |
||
735 | * |
||
736 | * @return bool If the insert was successful |
||
737 | * @throws TelegramException |
||
738 | */ |
||
739 | View Code Duplication | public static function insertShippingQueryRequest(ShippingQuery $shipping_query) |
|
773 | |||
774 | /** |
||
775 | * Insert pre checkout query request into database |
||
776 | * |
||
777 | * @param PreCheckoutQuery $pre_checkout_query |
||
778 | * |
||
779 | * @return bool If the insert was successful |
||
780 | * @throws TelegramException |
||
781 | */ |
||
782 | public static function insertPreCheckoutQueryRequest(PreCheckoutQuery $pre_checkout_query) |
||
819 | |||
820 | /** |
||
821 | * Insert poll request into database |
||
822 | * |
||
823 | * @param Poll $poll |
||
824 | * |
||
825 | * @return bool If the insert was successful |
||
826 | * @throws TelegramException |
||
827 | */ |
||
828 | public static function insertPollRequest(Poll $poll) |
||
856 | |||
857 | /** |
||
858 | * Insert Message request in db |
||
859 | * |
||
860 | * @param Message $message |
||
861 | * |
||
862 | * @return bool If the insert was successful |
||
863 | * @throws TelegramException |
||
864 | */ |
||
865 | 6 | public static function insertMessageRequest(Message $message) |
|
866 | { |
||
867 | 6 | if (!self::isDbConnected()) { |
|
868 | return false; |
||
869 | } |
||
870 | |||
871 | 6 | $date = self::getTimestamp($message->getDate()); |
|
872 | |||
873 | // Insert chat, update chat id in case it migrated |
||
874 | 6 | $chat = $message->getChat(); |
|
875 | 6 | self::insertChat($chat, $date, $message->getMigrateToChatId()); |
|
876 | |||
877 | // Insert user and the relation with the chat |
||
878 | 6 | $user = $message->getFrom(); |
|
879 | 6 | if ($user instanceof User) { |
|
880 | 6 | self::insertUser($user, $date, $chat); |
|
881 | } |
||
882 | |||
883 | // Insert the forwarded message user in users table |
||
884 | 6 | $forward_date = null; |
|
885 | 6 | $forward_from = $message->getForwardFrom(); |
|
886 | 6 | if ($forward_from instanceof User) { |
|
887 | self::insertUser($forward_from, $forward_date); |
||
888 | $forward_from = $forward_from->getId(); |
||
889 | $forward_date = self::getTimestamp($message->getForwardDate()); |
||
890 | } |
||
891 | 6 | $forward_from_chat = $message->getForwardFromChat(); |
|
892 | 6 | if ($forward_from_chat instanceof Chat) { |
|
893 | self::insertChat($forward_from_chat, $forward_date); |
||
894 | $forward_from_chat = $forward_from_chat->getId(); |
||
895 | $forward_date = self::getTimestamp($message->getForwardDate()); |
||
896 | } |
||
897 | |||
898 | // New and left chat member |
||
899 | 6 | $new_chat_members_ids = null; |
|
900 | 6 | $left_chat_member_id = null; |
|
901 | |||
902 | 6 | $new_chat_members = $message->getNewChatMembers(); |
|
903 | 6 | $left_chat_member = $message->getLeftChatMember(); |
|
904 | 6 | if (!empty($new_chat_members)) { |
|
905 | foreach ($new_chat_members as $new_chat_member) { |
||
906 | if ($new_chat_member instanceof User) { |
||
907 | // Insert the new chat user |
||
908 | self::insertUser($new_chat_member, $date, $chat); |
||
909 | $new_chat_members_ids[] = $new_chat_member->getId(); |
||
910 | } |
||
911 | } |
||
912 | $new_chat_members_ids = implode(',', $new_chat_members_ids); |
||
913 | 6 | } elseif ($left_chat_member instanceof User) { |
|
914 | // Insert the left chat user |
||
915 | self::insertUser($left_chat_member, $date, $chat); |
||
916 | $left_chat_member_id = $left_chat_member->getId(); |
||
917 | } |
||
918 | |||
919 | try { |
||
920 | 6 | $sth = self::$pdo->prepare(' |
|
921 | 6 | INSERT IGNORE INTO `' . TB_MESSAGE . '` |
|
922 | ( |
||
923 | `id`, `user_id`, `chat_id`, `date`, `forward_from`, `forward_from_chat`, `forward_from_message_id`, |
||
924 | `forward_signature`, `forward_sender_name`, `forward_date`, |
||
925 | `reply_to_chat`, `reply_to_message`, `edit_date`, `media_group_id`, `author_signature`, `text`, `entities`, `caption_entities`, |
||
926 | `audio`, `document`, `animation`, `game`, `photo`, `sticker`, `video`, `voice`, `video_note`, `caption`, `contact`, |
||
927 | `location`, `venue`, `poll`, `new_chat_members`, `left_chat_member`, |
||
928 | `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, |
||
929 | `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, |
||
930 | `pinned_message`, `invoice`, `successful_payment`, `connected_website`, `passport_data` |
||
931 | ) VALUES ( |
||
932 | :message_id, :user_id, :chat_id, :date, :forward_from, :forward_from_chat, :forward_from_message_id, |
||
933 | :forward_signature, :forward_sender_name, :forward_date, |
||
934 | :reply_to_chat, :reply_to_message, :edit_date, :media_group_id, :author_signature, :text, :entities, :caption_entities, |
||
935 | :audio, :document, :animation, :game, :photo, :sticker, :video, :voice, :video_note, :caption, :contact, |
||
936 | :location, :venue, :poll, :new_chat_members, :left_chat_member, |
||
937 | :new_chat_title, :new_chat_photo, :delete_chat_photo, :group_chat_created, |
||
938 | :supergroup_chat_created, :channel_chat_created, :migrate_to_chat_id, :migrate_from_chat_id, |
||
939 | :pinned_message, :invoice, :successful_payment, :connected_website, :passport_data |
||
940 | ) |
||
941 | '); |
||
942 | |||
943 | 6 | $user_id = null; |
|
944 | 6 | if ($user instanceof User) { |
|
945 | 6 | $user_id = $user->getId(); |
|
946 | } |
||
947 | 6 | $chat_id = $chat->getId(); |
|
948 | |||
949 | 6 | $reply_to_message = $message->getReplyToMessage(); |
|
950 | 6 | $reply_to_message_id = null; |
|
951 | 6 | if ($reply_to_message instanceof ReplyToMessage) { |
|
952 | $reply_to_message_id = $reply_to_message->getMessageId(); |
||
953 | // please notice that, as explained in the documentation, reply_to_message don't contain other |
||
954 | // reply_to_message field so recursion deep is 1 |
||
955 | self::insertMessageRequest($reply_to_message); |
||
956 | } |
||
957 | |||
958 | 6 | $sth->bindValue(':message_id', $message->getMessageId()); |
|
959 | 6 | $sth->bindValue(':chat_id', $chat_id); |
|
960 | 6 | $sth->bindValue(':user_id', $user_id); |
|
961 | 6 | $sth->bindValue(':date', $date); |
|
962 | 6 | $sth->bindValue(':forward_from', $forward_from); |
|
963 | 6 | $sth->bindValue(':forward_from_chat', $forward_from_chat); |
|
964 | 6 | $sth->bindValue(':forward_from_message_id', $message->getForwardFromMessageId()); |
|
965 | 6 | $sth->bindValue(':forward_signature', $message->getForwardSignature()); |
|
966 | 6 | $sth->bindValue(':forward_sender_name', $message->getForwardSenderName()); |
|
967 | 6 | $sth->bindValue(':forward_date', $forward_date); |
|
968 | |||
969 | 6 | $reply_to_chat_id = null; |
|
970 | 6 | if ($reply_to_message_id !== null) { |
|
971 | $reply_to_chat_id = $chat_id; |
||
972 | } |
||
973 | 6 | $sth->bindValue(':reply_to_chat', $reply_to_chat_id); |
|
974 | 6 | $sth->bindValue(':reply_to_message', $reply_to_message_id); |
|
975 | |||
976 | 6 | $sth->bindValue(':edit_date', $message->getEditDate()); |
|
977 | 6 | $sth->bindValue(':media_group_id', $message->getMediaGroupId()); |
|
978 | 6 | $sth->bindValue(':author_signature', $message->getAuthorSignature()); |
|
979 | 6 | $sth->bindValue(':text', $message->getText()); |
|
980 | 6 | $sth->bindValue(':entities', self::entitiesArrayToJson($message->getEntities())); |
|
981 | 6 | $sth->bindValue(':caption_entities', self::entitiesArrayToJson($message->getCaptionEntities())); |
|
982 | 6 | $sth->bindValue(':audio', $message->getAudio()); |
|
983 | 6 | $sth->bindValue(':document', $message->getDocument()); |
|
984 | 6 | $sth->bindValue(':animation', $message->getAnimation()); |
|
985 | 6 | $sth->bindValue(':game', $message->getGame()); |
|
986 | 6 | $sth->bindValue(':photo', self::entitiesArrayToJson($message->getPhoto())); |
|
987 | 6 | $sth->bindValue(':sticker', $message->getSticker()); |
|
988 | 6 | $sth->bindValue(':video', $message->getVideo()); |
|
989 | 6 | $sth->bindValue(':voice', $message->getVoice()); |
|
990 | 6 | $sth->bindValue(':video_note', $message->getVideoNote()); |
|
991 | 6 | $sth->bindValue(':caption', $message->getCaption()); |
|
992 | 6 | $sth->bindValue(':contact', $message->getContact()); |
|
993 | 6 | $sth->bindValue(':location', $message->getLocation()); |
|
994 | 6 | $sth->bindValue(':venue', $message->getVenue()); |
|
995 | 6 | $sth->bindValue(':poll', $message->getPoll()); |
|
996 | 6 | $sth->bindValue(':new_chat_members', $new_chat_members_ids); |
|
997 | 6 | $sth->bindValue(':left_chat_member', $left_chat_member_id); |
|
998 | 6 | $sth->bindValue(':new_chat_title', $message->getNewChatTitle()); |
|
999 | 6 | $sth->bindValue(':new_chat_photo', self::entitiesArrayToJson($message->getNewChatPhoto())); |
|
1000 | 6 | $sth->bindValue(':delete_chat_photo', $message->getDeleteChatPhoto()); |
|
1001 | 6 | $sth->bindValue(':group_chat_created', $message->getGroupChatCreated()); |
|
1002 | 6 | $sth->bindValue(':supergroup_chat_created', $message->getSupergroupChatCreated()); |
|
1003 | 6 | $sth->bindValue(':channel_chat_created', $message->getChannelChatCreated()); |
|
1004 | 6 | $sth->bindValue(':migrate_to_chat_id', $message->getMigrateToChatId()); |
|
1005 | 6 | $sth->bindValue(':migrate_from_chat_id', $message->getMigrateFromChatId()); |
|
1006 | 6 | $sth->bindValue(':pinned_message', $message->getPinnedMessage()); |
|
1007 | 6 | $sth->bindValue(':invoice', $message->getInvoice()); |
|
1008 | 6 | $sth->bindValue(':successful_payment', $message->getSuccessfulPayment()); |
|
1009 | 6 | $sth->bindValue(':connected_website', $message->getConnectedWebsite()); |
|
1010 | 6 | $sth->bindValue(':passport_data', $message->getPassportData()); |
|
1011 | |||
1012 | 6 | return $sth->execute(); |
|
1013 | } catch (PDOException $e) { |
||
1014 | throw new TelegramException($e->getMessage()); |
||
1015 | } |
||
1016 | } |
||
1017 | |||
1018 | /** |
||
1019 | * Insert Edited Message request in db |
||
1020 | * |
||
1021 | * @param Message $edited_message |
||
1022 | * |
||
1023 | * @return bool If the insert was successful |
||
1024 | * @throws TelegramException |
||
1025 | */ |
||
1026 | public static function insertEditedMessageRequest(Message $edited_message) |
||
1070 | |||
1071 | /** |
||
1072 | * Select Groups, Supergroups, Channels and/or single user Chats (also by ID or text) |
||
1073 | * |
||
1074 | * @param $select_chats_params |
||
1075 | * |
||
1076 | * @return array|bool |
||
1077 | * @throws TelegramException |
||
1078 | */ |
||
1079 | public static function selectChats($select_chats_params) |
||
1183 | |||
1184 | /** |
||
1185 | * Get Telegram API request count for current chat / message |
||
1186 | * |
||
1187 | * @param integer $chat_id |
||
1188 | * @param string $inline_message_id |
||
1189 | * |
||
1190 | * @return array|bool Array containing TOTAL and CURRENT fields or false on invalid arguments |
||
1191 | * @throws TelegramException |
||
1192 | */ |
||
1193 | public static function getTelegramRequestCount($chat_id = null, $inline_message_id = null) |
||
1223 | |||
1224 | /** |
||
1225 | * Insert Telegram API request in db |
||
1226 | * |
||
1227 | * @param string $method |
||
1228 | * @param array $data |
||
1229 | * |
||
1230 | * @return bool If the insert was successful |
||
1231 | * @throws TelegramException |
||
1232 | */ |
||
1233 | public static function insertTelegramRequest($method, $data) |
||
1259 | |||
1260 | /** |
||
1261 | * Bulk update the entries of any table |
||
1262 | * |
||
1263 | * @param string $table |
||
1264 | * @param array $fields_values |
||
1265 | * @param array $where_fields_values |
||
1266 | * |
||
1267 | * @return bool |
||
1268 | * @throws TelegramException |
||
1269 | */ |
||
1270 | 3 | public static function update($table, array $fields_values, array $where_fields_values) |
|
1302 | } |
||
1303 |
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.