| Total Complexity | 101 |
| Total Lines | 651 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 1 | Features | 0 |
Complex classes like DbTable_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.
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 DbTable_MySQL, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 24 | class DbTable_MySQL extends DbTable |
||
| 25 | { |
||
| 26 | /** |
||
| 27 | * Holds this instance of the table interface |
||
| 28 | * @var DbTable_MySQL |
||
| 29 | */ |
||
| 30 | private static $_tbl = null; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * DbTable_MySQL::construct |
||
| 34 | * |
||
| 35 | * @param object $db - A Database_MySQL object |
||
| 36 | */ |
||
| 37 | private function __construct($db) |
||
| 38 | { |
||
| 39 | global $db_prefix; |
||
| 40 | |||
| 41 | // We won't do any remove on these |
||
| 42 | $this->_reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items', |
||
| 43 | 'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories', 'collapsed_categories', |
||
| 44 | 'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards', |
||
| 45 | 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_karma', 'log_mark_read', |
||
| 46 | 'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments', |
||
| 47 | 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects', |
||
| 48 | 'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons', |
||
| 49 | 'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages', |
||
| 50 | 'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys', |
||
| 51 | 'themes', 'topics'); |
||
| 52 | |||
| 53 | foreach ($this->_reservedTables as $k => $table_name) |
||
| 54 | $this->_reservedTables[$k] = strtolower($db_prefix . $table_name); |
||
| 55 | |||
| 56 | // let's be sure. |
||
| 57 | $this->_package_log = array(); |
||
| 58 | |||
| 59 | // This executes queries and things |
||
| 60 | $this->_db = $db; |
||
| 61 | } |
||
| 62 | |||
| 63 | /** |
||
| 64 | * This function can be used to create a table without worrying about schema |
||
| 65 | * compatibilities across supported database systems. |
||
| 66 | * - If the table exists will, by default, do nothing. |
||
| 67 | * - Builds table with columns as passed to it - at least one column must be sent. |
||
| 68 | * The columns array should have one sub-array for each column - these sub arrays contain: |
||
| 69 | * 'name' = Column name |
||
| 70 | * 'type' = Type of column - values from (smallint, mediumint, int, text, varchar, char, tinytext, mediumtext, largetext) |
||
| 71 | * 'size' => Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. |
||
| 72 | * If not set it will pick a size. |
||
| 73 | * - 'default' = Default value - do not set if no default required. |
||
| 74 | * - 'null' => Can it be null (true or false) - if not set default will be false. |
||
| 75 | * - 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set from what |
||
| 76 | * it should begin counting. |
||
| 77 | * - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are: |
||
| 78 | * - 'name' => Index name (If left empty it will be generated). |
||
| 79 | * - 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'. |
||
| 80 | * - 'columns' => Array containing columns that form part of key - in the order the index is to be created. |
||
| 81 | * - parameters: (None yet) |
||
| 82 | * - if_exists values: |
||
| 83 | * - 'ignore' will do nothing if the table exists. (And will return true) |
||
| 84 | * - 'overwrite' will drop any existing table of the same name. |
||
| 85 | * - 'error' will return false if the table already exists. |
||
| 86 | * |
||
| 87 | * @param string $table_name |
||
| 88 | * @param mixed[] $columns in the format specified. |
||
| 89 | * @param mixed[] $indexes default array(), in the format specified. |
||
| 90 | * @param mixed[] $parameters default array() |
||
| 91 | * @param string $if_exists default 'ignore' |
||
| 92 | * @param string $error default 'fatal' |
||
| 93 | */ |
||
| 94 | public function db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal') |
||
| 157 | } |
||
| 158 | |||
| 159 | /** |
||
| 160 | * Drop a table. |
||
| 161 | * |
||
| 162 | * @param string $table_name |
||
| 163 | * @param mixed[] $parameters default array() |
||
| 164 | * @param string $error default 'fatal' |
||
| 165 | */ |
||
| 166 | public function db_drop_table($table_name, $parameters = array(), $error = 'fatal') |
||
| 167 | { |
||
| 168 | global $db_prefix; |
||
| 169 | |||
| 170 | // After stripping away the database name, this is what's left. |
||
| 171 | $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; |
||
| 172 | |||
| 173 | // Get some aliases. |
||
| 174 | $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); |
||
| 175 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 176 | |||
| 177 | // God no - dropping one of these = bad. |
||
| 178 | if (in_array(strtolower($table_name), $this->_reservedTables)) |
||
| 179 | return false; |
||
| 180 | |||
| 181 | // Does it exist? |
||
| 182 | if ($this->table_exists($full_table_name)) |
||
| 183 | { |
||
| 184 | $query = 'DROP TABLE ' . $table_name; |
||
| 185 | $this->_db->query('', |
||
| 186 | $query, |
||
| 187 | array( |
||
| 188 | 'security_override' => true, |
||
| 189 | ) |
||
| 190 | ); |
||
| 191 | |||
| 192 | return true; |
||
| 193 | } |
||
| 194 | |||
| 195 | // Otherwise do 'nout. |
||
| 196 | return false; |
||
| 197 | } |
||
| 198 | |||
| 199 | /** |
||
| 200 | * This function adds a column. |
||
| 201 | * |
||
| 202 | * @param string $table_name the name of the table |
||
| 203 | * @param mixed[] $column_info with column information |
||
| 204 | * @param mixed[] $parameters default array() |
||
| 205 | * @param string $if_exists default 'update' |
||
| 206 | * @param string $error default 'fatal' |
||
| 207 | */ |
||
| 208 | public function db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') |
||
| 209 | { |
||
| 210 | global $db_prefix; |
||
| 211 | |||
| 212 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 213 | |||
| 214 | // Log that we will want to uninstall this! |
||
| 215 | $this->_package_log[] = array('remove_column', $table_name, $column_info['name']); |
||
| 216 | |||
| 217 | // Does it exist - if so don't add it again! |
||
| 218 | if ($this->_get_column_info($table_name, $column_info['name'])) |
||
| 219 | { |
||
| 220 | // If we're going to overwrite then use change column. |
||
| 221 | if ($if_exists == 'update') |
||
| 222 | return $this->db_change_column($table_name, $column_info['name'], $column_info); |
||
| 223 | else |
||
| 224 | return false; |
||
| 225 | } |
||
| 226 | |||
| 227 | // Now add the thing! |
||
| 228 | $this->_alter_table($table_name, ' |
||
| 229 | ADD ' . $this->_db_create_query_column($column_info) . (empty($column_info['auto']) ? '' : ' primary key')); |
||
| 230 | |||
| 231 | return true; |
||
| 232 | } |
||
| 233 | |||
| 234 | /** |
||
| 235 | * Removes a column. |
||
| 236 | * |
||
| 237 | * @param string $table_name |
||
| 238 | * @param string $column_name |
||
| 239 | * @param mixed[] $parameters default array() |
||
| 240 | * @param string $error default 'fatal' |
||
| 241 | */ |
||
| 242 | public function db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal') |
||
| 243 | { |
||
| 244 | global $db_prefix; |
||
| 245 | |||
| 246 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 247 | |||
| 248 | // Does it exist? |
||
| 249 | $column = $this->_get_column_info($table_name, $column_name); |
||
| 250 | if ($column !== false) |
||
| 251 | { |
||
| 252 | $this->_alter_table($table_name, ' |
||
| 253 | DROP COLUMN ' . $column_name); |
||
| 254 | |||
| 255 | return true; |
||
| 256 | } |
||
| 257 | |||
| 258 | // If here we didn't have to work - joy! |
||
| 259 | return false; |
||
| 260 | } |
||
| 261 | |||
| 262 | /** |
||
| 263 | * Change a column. |
||
| 264 | * |
||
| 265 | * @param string $table_name |
||
| 266 | * @param string $old_column |
||
| 267 | * @param mixed[] $column_info |
||
| 268 | * @param mixed[] $parameters default array() |
||
| 269 | * @param string $error default 'fatal' |
||
| 270 | */ |
||
| 271 | public function db_change_column($table_name, $old_column, $column_info, $parameters = array(), $error = 'fatal') |
||
| 272 | { |
||
| 273 | global $db_prefix; |
||
| 274 | |||
| 275 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 276 | |||
| 277 | // Check it does exist! |
||
| 278 | $old_info = $this->_get_column_info($table_name, $old_column); |
||
| 279 | |||
| 280 | // Nothing? |
||
| 281 | if ($old_info === false) |
||
| 282 | return false; |
||
| 283 | |||
| 284 | // Get the right bits. |
||
| 285 | if (!isset($column_info['name'])) |
||
| 286 | $column_info['name'] = $old_column; |
||
| 287 | if (!isset($column_info['default'])) |
||
| 288 | $column_info['default'] = $old_info['default']; |
||
| 289 | if (!isset($column_info['null'])) |
||
| 290 | $column_info['null'] = $old_info['null']; |
||
| 291 | if (!isset($column_info['auto'])) |
||
| 292 | $column_info['auto'] = $old_info['auto']; |
||
| 293 | if (!isset($column_info['type'])) |
||
| 294 | $column_info['type'] = $old_info['type']; |
||
| 295 | if (!isset($column_info['size']) || !is_numeric($column_info['size'])) |
||
| 296 | $column_info['size'] = $old_info['size']; |
||
| 297 | if (!isset($column_info['unsigned']) || !in_array($column_info['type'], array('int', 'tinyint', 'smallint', 'mediumint', 'bigint'))) |
||
| 298 | $column_info['unsigned'] = ''; |
||
| 299 | |||
| 300 | $this->_alter_table($table_name, ' |
||
| 301 | CHANGE COLUMN `' . $old_column . '` ' . $this->_db_create_query_column($column_info)); |
||
| 302 | } |
||
| 303 | |||
| 304 | /** |
||
| 305 | * Add an index. |
||
| 306 | * |
||
| 307 | * @param string $table_name |
||
| 308 | * @param mixed[] $index_info |
||
| 309 | * @param mixed[] $parameters default array() |
||
| 310 | * @param string $if_exists default 'update' |
||
| 311 | * @param string $error default 'fatal' |
||
| 312 | */ |
||
| 313 | public function db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal') |
||
| 314 | { |
||
| 315 | global $db_prefix; |
||
| 316 | |||
| 317 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 318 | |||
| 319 | // No columns = no index. |
||
| 320 | if (empty($index_info['columns'])) |
||
| 321 | return false; |
||
| 322 | $columns = implode(',', $index_info['columns']); |
||
| 323 | |||
| 324 | // No name - make it up! |
||
| 325 | if (empty($index_info['name'])) |
||
| 326 | { |
||
| 327 | // No need for primary. |
||
| 328 | if (isset($index_info['type']) && $index_info['type'] == 'primary') |
||
| 329 | $index_info['name'] = ''; |
||
| 330 | else |
||
| 331 | $index_info['name'] = implode('_', $index_info['columns']); |
||
| 332 | } |
||
| 333 | |||
| 334 | // Log that we are going to want to remove this! |
||
| 335 | $this->_package_log[] = array('remove_index', $table_name, $index_info['name']); |
||
| 336 | |||
| 337 | // Let's get all our indexes. |
||
| 338 | $indexes = $this->db_list_indexes($table_name, true); |
||
| 339 | |||
| 340 | // Do we already have it? |
||
| 341 | foreach ($indexes as $index) |
||
| 342 | { |
||
| 343 | if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary')) |
||
| 344 | { |
||
| 345 | // If we want to overwrite simply remove the current one then continue. |
||
| 346 | if ($if_exists != 'update' || $index['type'] == 'primary') |
||
| 347 | return false; |
||
| 348 | else |
||
| 349 | $this->db_remove_index($table_name, $index_info['name']); |
||
| 350 | } |
||
| 351 | } |
||
| 352 | |||
| 353 | // If we're here we know we don't have the index - so just add it. |
||
| 354 | if (!empty($index_info['type']) && $index_info['type'] == 'primary') |
||
| 355 | { |
||
| 356 | $this->_alter_table($table_name, ' |
||
| 357 | ADD PRIMARY KEY (' . $columns . ')'); |
||
| 358 | } |
||
| 359 | else |
||
| 360 | { |
||
| 361 | $this->_alter_table($table_name, ' |
||
| 362 | ADD ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : 'INDEX') . ' ' . $index_info['name'] . ' (' . $columns . ')'); |
||
| 363 | } |
||
| 364 | } |
||
| 365 | |||
| 366 | /** |
||
| 367 | * Remove an index. |
||
| 368 | * |
||
| 369 | * @param string $table_name |
||
| 370 | * @param string $index_name |
||
| 371 | * @param mixed[] $parameters default array() |
||
| 372 | * @param string $error default 'fatal' |
||
| 373 | */ |
||
| 374 | public function db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal') |
||
| 375 | { |
||
| 376 | global $db_prefix; |
||
| 377 | |||
| 378 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 379 | |||
| 380 | // Better exist! |
||
| 381 | $indexes = $this->db_list_indexes($table_name, true); |
||
| 382 | |||
| 383 | foreach ($indexes as $index) |
||
| 384 | { |
||
| 385 | // If the name is primary we want the primary key! |
||
| 386 | if ($index['type'] == 'primary' && $index_name == 'primary') |
||
| 387 | { |
||
| 388 | // Dropping primary key? |
||
| 389 | $this->_alter_table($table_name, ' |
||
| 390 | DROP PRIMARY KEY'); |
||
| 391 | |||
| 392 | return true; |
||
| 393 | } |
||
| 394 | |||
| 395 | if ($index['name'] == $index_name) |
||
| 396 | { |
||
| 397 | // Drop the bugger... |
||
| 398 | $this->_alter_table($table_name, ' |
||
| 399 | DROP INDEX ' . $index_name); |
||
| 400 | |||
| 401 | return true; |
||
| 402 | } |
||
| 403 | } |
||
| 404 | |||
| 405 | // Not to be found ;( |
||
| 406 | return false; |
||
| 407 | } |
||
| 408 | |||
| 409 | /** |
||
| 410 | * Get the schema formatted name for a type. |
||
| 411 | * |
||
| 412 | * @param string $type_name |
||
| 413 | * @param int|null $type_size |
||
| 414 | * @param boolean $reverse |
||
| 415 | */ |
||
| 416 | public function db_calculate_type($type_name, $type_size = null, $reverse = false) |
||
| 417 | { |
||
| 418 | // MySQL is actually the generic baseline. |
||
| 419 | return array($type_name, $type_size); |
||
| 420 | } |
||
| 421 | |||
| 422 | /** |
||
| 423 | * Get table structure. |
||
| 424 | * |
||
| 425 | * @param string $table_name |
||
| 426 | * @param mixed[] $parameters default array() |
||
| 427 | */ |
||
| 428 | public function db_table_structure($table_name, $parameters = array()) |
||
| 429 | { |
||
| 430 | global $db_prefix; |
||
| 431 | |||
| 432 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 433 | |||
| 434 | return array( |
||
| 435 | 'name' => $table_name, |
||
| 436 | 'columns' => $this->db_list_columns($table_name, true), |
||
| 437 | 'indexes' => $this->db_list_indexes($table_name, true), |
||
| 438 | ); |
||
| 439 | } |
||
| 440 | |||
| 441 | /** |
||
| 442 | * Return column information for a table. |
||
| 443 | * |
||
| 444 | * @param string $table_name |
||
| 445 | * @param bool $detail |
||
| 446 | * @param mixed[] $parameters default array() |
||
| 447 | * @return mixed |
||
| 448 | */ |
||
| 449 | public function db_list_columns($table_name, $detail = false, $parameters = array()) |
||
| 507 | } |
||
| 508 | |||
| 509 | /** |
||
| 510 | * Get index information. |
||
| 511 | * |
||
| 512 | * @param string $table_name |
||
| 513 | * @param bool $detail |
||
| 514 | * @param mixed[] $parameters |
||
| 515 | * @return mixed |
||
| 516 | */ |
||
| 517 | public function db_list_indexes($table_name, $detail = false, $parameters = array()) |
||
| 518 | { |
||
| 519 | global $db_prefix; |
||
| 520 | |||
| 521 | $table_name = str_replace('{db_prefix}', $db_prefix, $table_name); |
||
| 522 | |||
| 523 | $result = $this->_db->query('', ' |
||
| 524 | SHOW KEYS |
||
| 525 | FROM {raw:table_name}', |
||
| 526 | array( |
||
| 527 | 'table_name' => substr($table_name, 0, 1) == '`' ? $table_name : '`' . $table_name . '`', |
||
| 528 | ) |
||
| 529 | ); |
||
| 530 | $indexes = array(); |
||
| 531 | while ($row = $this->_db->fetch_assoc($result)) |
||
| 532 | { |
||
| 533 | if (!$detail) |
||
| 534 | $indexes[] = $row['Key_name']; |
||
| 535 | else |
||
| 536 | { |
||
| 537 | // What is the type? |
||
| 538 | if ($row['Key_name'] == 'PRIMARY') |
||
| 539 | $type = 'primary'; |
||
| 540 | elseif (empty($row['Non_unique'])) |
||
| 541 | $type = 'unique'; |
||
| 542 | elseif (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT') |
||
| 543 | $type = 'fulltext'; |
||
| 544 | else |
||
| 545 | $type = 'index'; |
||
| 546 | |||
| 547 | // This is the first column we've seen? |
||
| 548 | if (empty($indexes[$row['Key_name']])) |
||
| 549 | { |
||
| 550 | $indexes[$row['Key_name']] = array( |
||
| 551 | 'name' => $row['Key_name'], |
||
| 552 | 'type' => $type, |
||
| 553 | 'columns' => array(), |
||
| 554 | ); |
||
| 555 | } |
||
| 556 | |||
| 557 | // Is it a partial index? |
||
| 558 | if (!empty($row['Sub_part'])) |
||
| 559 | $indexes[$row['Key_name']]['columns'][] = $row['Column_name'] . '(' . $row['Sub_part'] . ')'; |
||
| 560 | else |
||
| 561 | $indexes[$row['Key_name']]['columns'][] = $row['Column_name']; |
||
| 562 | } |
||
| 563 | } |
||
| 564 | $this->_db->free_result($result); |
||
| 565 | |||
| 566 | return $indexes; |
||
| 567 | } |
||
| 568 | |||
| 569 | /** |
||
| 570 | * Creates a query for a column |
||
| 571 | * |
||
| 572 | * @param mixed[] $column |
||
| 573 | */ |
||
| 574 | private function _db_create_query_column($column) |
||
| 598 | } |
||
| 599 | |||
| 600 | /** |
||
| 601 | * This function optimizes a table. |
||
| 602 | * |
||
| 603 | * @param string $table - the table to be optimized |
||
| 604 | * |
||
| 605 | * @return int how much it was gained |
||
| 606 | */ |
||
| 607 | public function optimize($table) |
||
| 608 | { |
||
| 609 | global $db_prefix; |
||
| 610 | |||
| 611 | $table = str_replace('{db_prefix}', $db_prefix, $table); |
||
| 612 | |||
| 613 | // Get how much overhead there is. |
||
| 614 | $request = $this->_db->query('', ' |
||
| 615 | SHOW TABLE STATUS LIKE {string:table_name}', |
||
| 616 | array( |
||
| 617 | 'table_name' => str_replace('_', '\_', $table), |
||
| 618 | ) |
||
| 619 | ); |
||
| 620 | $row = $this->_db->fetch_assoc($request); |
||
| 621 | $this->_db->free_result($request); |
||
| 622 | |||
| 623 | // Optimize tables that will benefit from this operation. |
||
| 624 | if (isset($row['Engine']) && $row['Engine'] === 'MyISAM') |
||
| 625 | { |
||
| 626 | $data_before = isset($row['Data_free']) ? $row['Data_free'] : 0; |
||
| 627 | $request = $this->_db->query('', ' |
||
| 628 | OPTIMIZE TABLE `{raw:table}`', |
||
| 629 | array( |
||
| 630 | 'table' => $table, |
||
| 631 | ) |
||
| 632 | ); |
||
| 633 | if (!$request) |
||
| 634 | return -1; |
||
| 635 | |||
| 636 | // How much left? |
||
| 637 | $request = $this->_db->query('', ' |
||
| 638 | SHOW TABLE STATUS LIKE {string:table}', |
||
| 639 | array( |
||
| 640 | 'table' => str_replace('_', '\_', $table), |
||
| 641 | ) |
||
| 642 | ); |
||
| 643 | $row = $this->_db->fetch_assoc($request); |
||
| 644 | $this->_db->free_result($request); |
||
| 645 | |||
| 646 | $total_change = isset($row['Data_free']) && $data_before > $row['Data_free'] ? $data_before / 1024 : 0; |
||
| 647 | } |
||
| 648 | else |
||
| 649 | { |
||
| 650 | $total_change = 0; |
||
| 651 | } |
||
| 652 | |||
| 653 | return $total_change; |
||
| 654 | } |
||
| 655 | |||
| 656 | /** |
||
| 657 | * Return a copy of this instance package log |
||
| 658 | */ |
||
| 659 | public function package_log() |
||
| 660 | { |
||
| 661 | return $this->_package_log; |
||
| 662 | } |
||
| 663 | |||
| 664 | /** |
||
| 665 | * Static method that allows to retrieve or create an instance of this class. |
||
| 666 | * |
||
| 667 | * @param object $db - A Database_MySQL object |
||
| 668 | * @return DbTable_MySQL - A DbTable_MySQL object |
||
| 669 | */ |
||
| 670 | public static function db_table($db) |
||
| 675 | } |
||
| 676 | } |