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_mysql 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_mysql, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 8 | class db_mysql { | 
            ||
| 9 | const DB_MYSQL_TRANSACTION_SERIALIZABLE = 'SERIALIZABLE';  | 
            ||
| 10 | const DB_MYSQL_TRANSACTION_REPEATABLE_READ = 'REPEATABLE READ';  | 
            ||
| 11 | const DB_MYSQL_TRANSACTION_READ_COMMITTED = 'READ COMMITTED';  | 
            ||
| 12 | const DB_MYSQL_TRANSACTION_READ_UNCOMMITTED = 'READ UNCOMMITTED';  | 
            ||
| 13 | |||
| 14 | /**  | 
            ||
| 15 | * Статус соеднения с MySQL  | 
            ||
| 16 | *  | 
            ||
| 17 | * @var bool  | 
            ||
| 18 | */  | 
            ||
| 19 | public $connected = false;  | 
            ||
| 20 | /**  | 
            ||
| 21 | * Префикс названий таблиц в БД  | 
            ||
| 22 | *  | 
            ||
| 23 | * @var string  | 
            ||
| 24 | */  | 
            ||
| 25 | public $db_prefix = '';  | 
            ||
| 26 | /**  | 
            ||
| 27 | * Список таблиц в БД  | 
            ||
| 28 | *  | 
            ||
| 29 | * @var array  | 
            ||
| 30 | */  | 
            ||
| 31 | public $table_list = array();  | 
            ||
| 32 | |||
| 33 | /**  | 
            ||
| 34 | * Настройки БД  | 
            ||
| 35 | *  | 
            ||
| 36 | * @var array  | 
            ||
| 37 | */  | 
            ||
| 38 | protected $dbsettings = array();  | 
            ||
| 39 | /**  | 
            ||
| 40 | * Драйвер для прямого обращения к MySQL  | 
            ||
| 41 | *  | 
            ||
| 42 | * @var db_mysql_v5 $driver  | 
            ||
| 43 | */  | 
            ||
| 44 | public $driver = null;  | 
            ||
| 45 | |||
| 46 | /**  | 
            ||
| 47 | * Общее время запросов  | 
            ||
| 48 | *  | 
            ||
| 49 | * @var float $time_mysql_total  | 
            ||
| 50 | */  | 
            ||
| 51 | public $time_mysql_total = 0.0;  | 
            ||
| 52 | |||
| 53 |   public function __construct() { | 
            ||
| 54 | }  | 
            ||
| 55 | |||
| 56 |   function load_db_settings() { | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 57 | $dbsettings = array();  | 
            ||
| 58 | |||
| 59 | require(SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX);  | 
            ||
| 60 | |||
| 61 | $this->dbsettings = $dbsettings;  | 
            ||
| 62 | }  | 
            ||
| 63 | |||
| 64 |   function sn_db_connect($external_db_settings = null) { | 
            ||
| 65 | $this->db_disconnect();  | 
            ||
| 66 | |||
| 67 |     if(!empty($external_db_settings) && is_array($external_db_settings)) { | 
            ||
| 68 | $this->dbsettings = $external_db_settings;  | 
            ||
| 69 | }  | 
            ||
| 70 | |||
| 71 |     if(empty($this->dbsettings)) { | 
            ||
| 72 | $this->load_db_settings();  | 
            ||
| 73 | }  | 
            ||
| 74 | |||
| 75 | // TODO - фатальные (?) ошибки на каждом шагу. Хотя - скорее Эксепшны  | 
            ||
| 76 |     if(!empty($this->dbsettings)) { | 
            ||
| 77 | $driver_name = empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver'];  | 
            ||
| 78 | $this->driver = new $driver_name();  | 
            ||
| 79 | $this->db_prefix = $this->dbsettings['prefix'];  | 
            ||
| 80 | |||
| 81 | $this->connected = $this->connected || $this->driver_connect();  | 
            ||
| 82 | |||
| 83 |       if($this->connected) { | 
            ||
| 84 | $this->table_list = $this->db_get_table_list();  | 
            ||
| 85 | // TODO Проверка на пустоту  | 
            ||
| 86 | }  | 
            ||
| 87 |     } else { | 
            ||
| 88 | $this->connected = false;  | 
            ||
| 89 | }  | 
            ||
| 90 | |||
| 91 | return $this->connected;  | 
            ||
| 92 | }  | 
            ||
| 93 | |||
| 94 |   function driver_connect() { | 
            ||
| 95 | global $debug;  | 
            ||
| 96 | |||
| 97 |     if(!is_object($this->driver)) { | 
            ||
| 98 |       $debug->error_fatal('DB Error - No driver for MySQL found!'); | 
            ||
| 99 | }  | 
            ||
| 100 | |||
| 101 |     if(!method_exists($this->driver, 'mysql_connect')) { | 
            ||
| 102 |       $debug->error_fatal('DB Error - WRONG MySQL driver!'); | 
            ||
| 103 | }  | 
            ||
| 104 | |||
| 105 | return $this->driver->mysql_connect($this->dbsettings);  | 
            ||
| 106 | }  | 
            ||
| 107 | |||
| 108 |   function db_disconnect() { | 
            ||
| 109 |     if($this->connected) { | 
            ||
| 110 | $this->connected = !$this->driver_disconnect();  | 
            ||
| 111 | $this->connected = false;  | 
            ||
| 112 | }  | 
            ||
| 113 | |||
| 114 | return !$this->connected;  | 
            ||
| 115 | }  | 
            ||
| 116 | |||
| 117 |   function doquery($query, $table = '', $fetch = false, $skip_query_check = false) { | 
            ||
| 118 | global $numqueries, $debug;  | 
            ||
| 119 | |||
| 120 |     if(!is_string($table)) { | 
            ||
| 121 | $fetch = $table;  | 
            ||
| 122 | }  | 
            ||
| 123 | |||
| 124 |     if(!$this->connected) { | 
            ||
| 125 | $this->sn_db_connect();  | 
            ||
| 126 | }  | 
            ||
| 127 | |||
| 128 | $query = trim($query);  | 
            ||
| 129 | $this->security_watch_user_queries($query);  | 
            ||
| 130 | !$skip_query_check ? $this->security_query_check_bad_words($query) : false;  | 
            ||
| 131 | |||
| 132 | $sql = $query;  | 
            ||
| 133 |     if(strpos($sql, '{{') !== false) { | 
            ||
| 134 |       foreach($this->table_list as $tableName) { | 
            ||
| 135 |         $sql = str_replace("{{{$tableName}}}", $this->db_prefix . $tableName, $sql); | 
            ||
| 136 | }  | 
            ||
| 137 | }  | 
            ||
| 138 | |||
| 139 |     if(classSupernova::$config->debug) { | 
            ||
| 140 | $numqueries++;  | 
            ||
| 141 | $arr = debug_backtrace();  | 
            ||
| 142 |       $file = end(explode('/', $arr[0]['file'])); | 
            ||
| 143 | $line = $arr[0]['line'];  | 
            ||
| 144 |       $debug->add("<tr><th>Query $numqueries: </th><th>$query</th><th>$file($line)</th><th>$table</th><th>$fetch</th></tr>"); | 
            ||
| 145 | }  | 
            ||
| 146 | |||
| 147 |     if(defined('DEBUG_SQL_COMMENT')) { | 
            ||
| 148 | $backtrace = debug_backtrace();  | 
            ||
| 149 |       $sql_comment = $debug->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG')); | 
            ||
| 150 | |||
| 151 |       $sql_commented = '/* ' . implode("<br />", $sql_comment) . '<br /> */ ' . preg_replace("/\s+/", ' ', $sql); | 
            ||
| 152 |       if(defined('DEBUG_SQL_ONLINE')) { | 
            ||
| 153 | $debug->warning($sql_commented, 'SQL Debug', LOG_DEBUG_SQL);  | 
            ||
| 154 | }  | 
            ||
| 155 | |||
| 156 |       if(defined('DEBUG_SQL_ERROR')) { | 
            ||
| 157 |         array_unshift($sql_comment, preg_replace("/\s+/", ' ', $sql)); | 
            ||
| 158 | $debug->add_to_array($sql_comment);  | 
            ||
| 159 |         // $debug->add_to_array($sql_comment . preg_replace("/\s+/", ' ', $sql)); | 
            ||
| 160 | }  | 
            ||
| 161 | $sql = $sql_commented;  | 
            ||
| 162 | }  | 
            ||
| 163 | |||
| 164 | $sqlquery = $this->db_sql_query($sql) or $debug->error(db_error() . "<br />$sql<br />", 'SQL Error');  | 
            ||
| 165 | |||
| 166 | return $fetch ? $this->db_fetch($sqlquery) : $sqlquery;  | 
            ||
| 167 | }  | 
            ||
| 168 | |||
| 169 | |||
| 170 |   function security_watch_user_queries($query) { | 
            ||
| 171 | // TODO Заменить это на новый логгер  | 
            ||
| 172 | global $is_watching, $user, $debug;  | 
            ||
| 173 | |||
| 174 | if(  | 
            ||
| 175 | !$is_watching // Not already watching  | 
            ||
| 176 | && !empty(classSupernova::$config->game_watchlist_array) // There is some players in watchlist  | 
            ||
| 177 | && in_array($user['id'], classSupernova::$config->game_watchlist_array) // Current player is in watchlist  | 
            ||
| 178 |       && !preg_match('/^(select|commit|rollback|start transaction)/i', $query) // Current query should be watched | 
            ||
| 179 |     ) { | 
            ||
| 180 | $is_watching = true;  | 
            ||
| 181 |       $msg = "\$query = \"{$query}\"\n\r"; | 
            ||
| 182 |       if(!empty($_POST)) { | 
            ||
| 183 | $msg .= "\n\r" . dump($_POST, '$_POST');  | 
            ||
| 184 | }  | 
            ||
| 185 |       if(!empty($_GET)) { | 
            ||
| 186 | $msg .= "\n\r" . dump($_GET, '$_GET');  | 
            ||
| 187 | }  | 
            ||
| 188 |       $debug->warning($msg, "Watching user {$user['id']}", 399, array('base_dump' => true)); | 
            ||
| 189 | $is_watching = false;  | 
            ||
| 190 | }  | 
            ||
| 191 | }  | 
            ||
| 192 | |||
| 193 | |||
| 194 |   function security_query_check_bad_words($query) { | 
            ||
| 195 | global $user, $dm_change_legit, $mm_change_legit;  | 
            ||
| 196 | |||
| 197 |     switch(true) { | 
            ||
| 198 | case stripos($query, 'RUNCATE TABL') != false:  | 
            ||
| 199 | case stripos($query, 'ROP TABL') != false:  | 
            ||
| 200 | case stripos($query, 'ENAME TABL') != false:  | 
            ||
| 201 | case stripos($query, 'REATE DATABAS') != false:  | 
            ||
| 202 | case stripos($query, 'REATE TABL') != false:  | 
            ||
| 203 | case stripos($query, 'ET PASSWOR') != false:  | 
            ||
| 204 | case stripos($query, 'EOAD DAT') != false:  | 
            ||
| 205 | case stripos($query, 'RPG_POINTS') != false && stripos(trim($query), 'UPDATE ') === 0 && !$dm_change_legit:  | 
            ||
| 206 | case stripos($query, 'METAMATTER') != false && stripos(trim($query), 'UPDATE ') === 0 && !$mm_change_legit:  | 
            ||
| 207 | case stripos($query, 'AUTHLEVEL') != false && $user['authlevel'] < 3 && stripos($query, 'SELECT') !== 0:  | 
            ||
| 208 |         $report = "Hacking attempt (" . date("d.m.Y H:i:s") . " - [" . time() . "]):\n"; | 
            ||
| 209 | $report .= ">Database Inforamation\n";  | 
            ||
| 210 | $report .= "\tID - " . $user['id'] . "\n";  | 
            ||
| 211 | $report .= "\tUser - " . $user['username'] . "\n";  | 
            ||
| 212 | $report .= "\tAuth level - " . $user['authlevel'] . "\n";  | 
            ||
| 213 | $report .= "\tAdmin Notes - " . $user['adminNotes'] . "\n";  | 
            ||
| 214 | $report .= "\tCurrent Planet - " . $user['current_planet'] . "\n";  | 
            ||
| 215 | $report .= "\tUser IP - " . $user['user_lastip'] . "\n";  | 
            ||
| 216 | $report .= "\tUser IP at Reg - " . $user['ip_at_reg'] . "\n";  | 
            ||
| 217 | $report .= "\tUser Agent- " . $_SERVER['HTTP_USER_AGENT'] . "\n";  | 
            ||
| 218 | $report .= "\tCurrent Page - " . $user['current_page'] . "\n";  | 
            ||
| 219 | $report .= "\tRegister Time - " . $user['register_time'] . "\n";  | 
            ||
| 220 | $report .= "\n";  | 
            ||
| 221 | |||
| 222 | $report .= ">Query Information\n";  | 
            ||
| 223 | $report .= "\tQuery - " . $query . "\n";  | 
            ||
| 224 | $report .= "\n";  | 
            ||
| 225 | |||
| 226 | $report .= ">\$_SERVER Information\n";  | 
            ||
| 227 | $report .= "\tIP - " . $_SERVER['REMOTE_ADDR'] . "\n";  | 
            ||
| 228 | $report .= "\tHost Name - " . $_SERVER['HTTP_HOST'] . "\n";  | 
            ||
| 229 | $report .= "\tUser Agent - " . $_SERVER['HTTP_USER_AGENT'] . "\n";  | 
            ||
| 230 | $report .= "\tRequest Method - " . $_SERVER['REQUEST_METHOD'] . "\n";  | 
            ||
| 231 | $report .= "\tCame From - " . $_SERVER['HTTP_REFERER'] . "\n";  | 
            ||
| 232 | $report .= "\tPage is - " . $_SERVER['SCRIPT_NAME'] . "\n";  | 
            ||
| 233 | $report .= "\tUses Port - " . $_SERVER['REMOTE_PORT'] . "\n";  | 
            ||
| 234 | $report .= "\tServer Protocol - " . $_SERVER['SERVER_PROTOCOL'] . "\n";  | 
            ||
| 235 | |||
| 236 | $report .= "\n--------------------------------------------------------------------------------------------------\n";  | 
            ||
| 237 | |||
| 238 | $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a');  | 
            ||
| 239 | fwrite($fp, $report);  | 
            ||
| 240 | fclose($fp);  | 
            ||
| 241 | |||
| 242 | $message = 'Привет, я не знаю то, что Вы пробовали сделать, но команда, которую Вы только послали базе данных, не выглядела очень дружественной и она была заблокированна.<br /><br />Ваш IP, и другие данные переданны администрации сервера. Удачи!.';  | 
            ||
| 243 | die($message);  | 
            ||
| 244 | break;  | 
            ||
| 245 | }  | 
            ||
| 246 | }  | 
            ||
| 247 | |||
| 248 | /**  | 
            ||
| 249 | * @param bool $prefixed_only  | 
            ||
| 250 | *  | 
            ||
| 251 | * @return array  | 
            ||
| 252 | */  | 
            ||
| 253 |   function db_get_table_list($prefixed_only = true) { | 
            ||
| 254 | $query = $this->mysql_get_table_list();  | 
            ||
| 255 | |||
| 256 | $prefix_length = strlen($this->db_prefix);  | 
            ||
| 257 | |||
| 258 | $tl = array();  | 
            ||
| 259 |     while($row = $this->db_fetch($query)) { | 
            ||
| 260 |       foreach($row as $table_name) { | 
            ||
| 261 |         if(strpos($table_name, $this->db_prefix) === 0) { | 
            ||
| 262 | $table_name = substr($table_name, $prefix_length);  | 
            ||
| 263 |         } elseif($prefixed_only) { | 
            ||
| 264 | continue;  | 
            ||
| 265 | }  | 
            ||
| 266 | // $table_name = str_replace($db_prefix, '', $table_name);  | 
            ||
| 267 | $tl[$table_name] = $table_name;  | 
            ||
| 268 | }  | 
            ||
| 269 | }  | 
            ||
| 270 | |||
| 271 | return $tl;  | 
            ||
| 272 | }  | 
            ||
| 273 | |||
| 274 |   function mysql_get_table_list() { | 
            ||
| 275 |     return $this->db_sql_query('SHOW TABLES;'); | 
            ||
| 276 | }  | 
            ||
| 277 | |||
| 278 |   function mysql_get_innodb_status() { | 
            ||
| 279 |     return $this->db_sql_query('SHOW ENGINE INNODB STATUS;'); | 
            ||
| 280 | }  | 
            ||
| 281 | |||
| 282 | |||
| 283 | /**  | 
            ||
| 284 | * L1 perform the query  | 
            ||
| 285 | *  | 
            ||
| 286 | * @param $query_string  | 
            ||
| 287 | *  | 
            ||
| 288 | * @return bool|mysqli_result  | 
            ||
| 289 | */  | 
            ||
| 290 | View Code Duplication |   function db_sql_query($query_string) { | 
            |
| 291 | $microtime = microtime(true);  | 
            ||
| 292 | $result = $this->driver->mysql_query($query_string);  | 
            ||
| 293 | $this->time_mysql_total += microtime(true) - $microtime;  | 
            ||
| 294 | |||
| 295 | return $result;  | 
            ||
| 296 | }  | 
            ||
| 297 | |||
| 298 | /**  | 
            ||
| 299 | * L1 fetch assoc array  | 
            ||
| 300 | *  | 
            ||
| 301 | * @param $query  | 
            ||
| 302 | *  | 
            ||
| 303 | * @return array|null  | 
            ||
| 304 | */  | 
            ||
| 305 | View Code Duplication |   function db_fetch(&$query) { | 
            |
| 306 | $microtime = microtime(true);  | 
            ||
| 307 | $result = $this->driver->mysql_fetch_assoc($query);  | 
            ||
| 308 | $this->time_mysql_total += microtime(true) - $microtime;  | 
            ||
| 309 | |||
| 310 | return $result;  | 
            ||
| 311 | }  | 
            ||
| 312 | |||
| 313 |   function db_fetch_row(&$query) { | 
            ||
| 314 | return $this->driver->mysql_fetch_row($query);  | 
            ||
| 315 | }  | 
            ||
| 316 | |||
| 317 |   function db_escape($unescaped_string) { | 
            ||
| 318 | return $this->driver->mysql_real_escape_string($unescaped_string);  | 
            ||
| 319 | }  | 
            ||
| 320 | |||
| 321 |   function driver_disconnect() { | 
            ||
| 324 | |||
| 325 |   function db_error() { | 
            ||
| 326 | return $this->driver->mysql_error();  | 
            ||
| 327 | }  | 
            ||
| 328 | |||
| 329 |   function db_insert_id() { | 
            ||
| 330 | return $this->driver->mysql_insert_id();  | 
            ||
| 331 | }  | 
            ||
| 332 | |||
| 333 |   function db_num_rows(&$result) { | 
            ||
| 334 | return $this->driver->mysql_num_rows($result);  | 
            ||
| 335 | }  | 
            ||
| 336 | |||
| 337 |   function db_affected_rows() { | 
            ||
| 340 | |||
| 341 |   function db_get_client_info() { | 
            ||
| 342 | return $this->driver->mysql_get_client_info();  | 
            ||
| 343 | }  | 
            ||
| 344 | |||
| 345 |   function db_get_server_info() { | 
            ||
| 346 | return $this->driver->mysql_get_server_info();  | 
            ||
| 347 | }  | 
            ||
| 348 | |||
| 349 |   function db_get_host_info() { | 
            ||
| 350 | return $this->driver->mysql_get_host_info();  | 
            ||
| 351 | }  | 
            ||
| 352 | |||
| 353 |   function db_get_server_stat() { | 
            ||
| 354 | return $this->driver->mysql_stat();  | 
            ||
| 355 | }  | 
            ||
| 356 | |||
| 357 | }  | 
            ||
| 358 | 
Adding explicit visibility (
private,protected, orpublic) is generally recommend to communicate to other developers how, and from where this method is intended to be used.