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 MSSQLDatabase 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 MSSQLDatabase, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 41 | class MSSQLDatabase extends SS_Database |
||
|
|
|||
| 42 | { |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Words that will trigger an error if passed to a SQL Server fulltext search |
||
| 46 | */ |
||
| 47 | public static $noiseWords = array('about', '1', 'after', '2', 'all', 'also', '3', 'an', '4', 'and', '5', 'another', '6', 'any', '7', 'are', '8', 'as', '9', 'at', '0', 'be', '$', 'because', 'been', 'before', 'being', 'between', 'both', 'but', 'by', 'came', 'can', 'come', 'could', 'did', 'do', 'does', 'each', 'else', 'for', 'from', 'get', 'got', 'has', 'had', 'he', 'have', 'her', 'here', 'him', 'himself', 'his', 'how', 'if', 'in', 'into', 'is', 'it', 'its', 'just', 'like', 'make', 'many', 'me', 'might', 'more', 'most', 'much', 'must', 'my', 'never', 'no', 'now', 'of', 'on', 'only', 'or', 'other', 'our', 'out', 'over', 're', 'said', 'same', 'see', 'should', 'since', 'so', 'some', 'still', 'such', 'take', 'than', 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'those', 'through', 'to', 'too', 'under', 'up', 'use', 'very', 'want', 'was', 'way', 'we', 'well', 'were', 'what', 'when', 'where', 'which', 'while', 'who', 'will', 'with', 'would', 'you', 'your', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'); |
||
| 48 | |||
| 49 | /** |
||
| 50 | * Transactions will work with FreeTDS, but not entirely with sqlsrv driver on Windows with MARS enabled. |
||
| 51 | * TODO: |
||
| 52 | * - after the test fails with open transaction, the transaction should be rolled back, |
||
| 53 | * otherwise other tests will break claiming that transaction is still open. |
||
| 54 | * - figure out SAVEPOINTS |
||
| 55 | * - READ ONLY transactions |
||
| 56 | */ |
||
| 57 | protected $supportsTransactions = true; |
||
| 58 | |||
| 59 | /** |
||
| 60 | * Cached flag to determine if full-text is enabled. This is set by |
||
| 61 | * {@link MSSQLDatabase::fullTextEnabled()} |
||
| 62 | * |
||
| 63 | * @var boolean |
||
| 64 | */ |
||
| 65 | protected $fullTextEnabled = null; |
||
| 66 | |||
| 67 | /** |
||
| 68 | * Set the default collation of the MSSQL nvarchar fields that we create. |
||
| 69 | * We don't apply this to the database as a whole, so that we can use unicode collations. |
||
| 70 | * |
||
| 71 | * @param string $collation |
||
| 72 | */ |
||
| 73 | public static function set_collation($collation) |
||
| 77 | |||
| 78 | /** |
||
| 79 | * The default collation of the MSSQL nvarchar fields that we create. |
||
| 80 | * We don't apply this to the database as a whole, so that we can use |
||
| 81 | * unicode collations. |
||
| 82 | * |
||
| 83 | * @return string |
||
| 84 | */ |
||
| 85 | public static function get_collation() |
||
| 89 | |||
| 90 | /** |
||
| 91 | * Connect to a MS SQL database. |
||
| 92 | * @param array $parameters An map of parameters, which should include: |
||
| 93 | * - server: The server, eg, localhost |
||
| 94 | * - username: The username to log on with |
||
| 95 | * - password: The password to log on with |
||
| 96 | * - database: The database to connect to |
||
| 97 | * - windowsauthentication: Set to true to use windows authentication |
||
| 98 | * instead of username/password |
||
| 99 | */ |
||
| 100 | public function connect($parameters) |
||
| 108 | |||
| 109 | /** |
||
| 110 | * Checks whether the current SQL Server version has full-text |
||
| 111 | * support installed and full-text is enabled for this database. |
||
| 112 | * |
||
| 113 | * @return boolean |
||
| 114 | */ |
||
| 115 | public function fullTextEnabled() |
||
| 122 | |||
| 123 | /** |
||
| 124 | * Checks whether the current SQL Server version has full-text |
||
| 125 | * support installed and full-text is enabled for this database. |
||
| 126 | * |
||
| 127 | * @return boolean |
||
| 128 | */ |
||
| 129 | protected function updateFullTextEnabled() |
||
| 145 | |||
| 146 | public function supportsCollations() |
||
| 150 | |||
| 151 | public function supportsTimezoneOverride() |
||
| 155 | |||
| 156 | public function getDatabaseServer() |
||
| 160 | |||
| 161 | public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERROR) |
||
| 167 | |||
| 168 | public function clearTable($table) |
||
| 172 | |||
| 173 | /** |
||
| 174 | * SQL Server uses CURRENT_TIMESTAMP for the current date/time. |
||
| 175 | */ |
||
| 176 | public function now() |
||
| 180 | |||
| 181 | /** |
||
| 182 | * Returns the database-specific version of the random() function |
||
| 183 | */ |
||
| 184 | public function random() |
||
| 188 | |||
| 189 | /** |
||
| 190 | * The core search engine configuration. |
||
| 191 | * Picks up the fulltext-indexed tables from the database and executes search on all of them. |
||
| 192 | * Results are obtained as ID-ClassName pairs which is later used to reconstruct the DataObjectSet. |
||
| 193 | * |
||
| 194 | * @param array classesToSearch computes all descendants and includes them. Check is done via WHERE clause. |
||
| 195 | * @param string $keywords Keywords as a space separated string |
||
| 196 | * @return object DataObjectSet of result pages |
||
| 197 | */ |
||
| 198 | public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC", $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false) |
||
| 325 | |||
| 326 | /** |
||
| 327 | * Allow auto-increment primary key editing on the given table. |
||
| 328 | * Some databases need to enable this specially. |
||
| 329 | * |
||
| 330 | * @param $table The name of the table to have PK editing allowed on |
||
| 331 | * @param $allow True to start, false to finish |
||
| 332 | */ |
||
| 333 | public function allowPrimaryKeyEditing($table, $allow = true) |
||
| 337 | |||
| 338 | /** |
||
| 339 | * Returns a SQL fragment for querying a fulltext search index |
||
| 340 | * |
||
| 341 | * @param $tableName specific - table name |
||
| 342 | * @param $keywords string The search query |
||
| 343 | * @param $fields array The list of field names to search on, or null to include all |
||
| 344 | * |
||
| 345 | * @returns null if keyword set is empty or the string with JOIN clause to be added to SQL query |
||
| 346 | */ |
||
| 347 | public function fullTextSearchMSSQL($tableName, $keywords, $fields = null) |
||
| 380 | |||
| 381 | /** |
||
| 382 | * Remove stopwords that would kill a MSSQL full-text query |
||
| 383 | * |
||
| 384 | * @param array $keywords |
||
| 385 | * |
||
| 386 | * @return array $keywords with stopwords removed |
||
| 387 | */ |
||
| 388 | public function removeStopwords($keywords) |
||
| 399 | |||
| 400 | /** |
||
| 401 | * Does this database support transactions? |
||
| 402 | */ |
||
| 403 | public function supportsTransactions() |
||
| 407 | |||
| 408 | /** |
||
| 409 | * This is a quick lookup to discover if the database supports particular extensions |
||
| 410 | * Currently, MSSQL supports no extensions |
||
| 411 | * |
||
| 412 | * @param array $extensions List of extensions to check for support of. The key of this array |
||
| 413 | * will be an extension name, and the value the configuration for that extension. This |
||
| 414 | * could be one of partitions, tablespaces, or clustering |
||
| 415 | * @return boolean Flag indicating support for all of the above |
||
| 416 | */ |
||
| 417 | public function supportsExtensions($extensions = array('partitions', 'tablespaces', 'clustering')) |
||
| 429 | |||
| 430 | /** |
||
| 431 | * Start transaction. READ ONLY not supported. |
||
| 432 | */ |
||
| 433 | public function transactionStart($transactionMode = false, $sessionCharacteristics = false) |
||
| 441 | |||
| 442 | public function transactionSavepoint($savepoint) |
||
| 446 | |||
| 447 | public function transactionRollback($savepoint = false) |
||
| 457 | |||
| 458 | public function transactionEnd($chain = false) |
||
| 466 | |||
| 467 | public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null, $parameterised = false) |
||
| 504 | |||
| 505 | /** |
||
| 506 | * Function to return an SQL datetime expression for MSSQL |
||
| 507 | * used for querying a datetime in a certain format |
||
| 508 | * |
||
| 509 | * @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
| 510 | * @param string $format to be used, supported specifiers: |
||
| 511 | * %Y = Year (four digits) |
||
| 512 | * %m = Month (01..12) |
||
| 513 | * %d = Day (01..31) |
||
| 514 | * %H = Hour (00..23) |
||
| 515 | * %i = Minutes (00..59) |
||
| 516 | * %s = Seconds (00..59) |
||
| 517 | * %U = unix timestamp, can only be used on it's own |
||
| 518 | * @return string SQL datetime expression to query for a formatted datetime |
||
| 519 | */ |
||
| 520 | public function formattedDatetimeClause($date, $format) |
||
| 570 | |||
| 571 | /** |
||
| 572 | * Function to return an SQL datetime expression for MSSQL. |
||
| 573 | * used for querying a datetime addition |
||
| 574 | * |
||
| 575 | * @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
| 576 | * @param string $interval to be added, use the format [sign][integer] [qualifier], e.g. -1 Day, +15 minutes, +1 YEAR |
||
| 577 | * supported qualifiers: |
||
| 578 | * - years |
||
| 579 | * - months |
||
| 580 | * - days |
||
| 581 | * - hours |
||
| 582 | * - minutes |
||
| 583 | * - seconds |
||
| 584 | * This includes the singular forms as well |
||
| 585 | * @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of the addition |
||
| 586 | */ |
||
| 587 | public function datetimeIntervalClause($date, $interval) |
||
| 615 | |||
| 616 | /** |
||
| 617 | * Function to return an SQL datetime expression for MSSQL. |
||
| 618 | * used for querying a datetime substraction |
||
| 619 | * |
||
| 620 | * @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
| 621 | * @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
| 622 | * @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which is the result of the substraction |
||
| 623 | */ |
||
| 624 | public function datetimeDifferenceClause($date1, $date2) |
||
| 640 | } |
||
| 641 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.