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 DBRow 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 DBRow, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 24 | abstract class DBRow implements IDbRow { |
||
| 25 | // TODO |
||
| 26 | /** |
||
| 27 | * Should be this object - (!) not class - cached |
||
| 28 | * There exists tables that didn't need to cache rows - logs as example |
||
| 29 | * And there can be special needs to not cache some class instances when stream-reading many rows i.e. fleets in stat calculation |
||
| 30 | * |
||
| 31 | * @var bool $_cacheable |
||
| 32 | */ |
||
| 33 | public $_cacheable = true; // |
||
| 34 | // TODO |
||
| 35 | /** |
||
| 36 | * БД для доступа к данным |
||
| 37 | * |
||
| 38 | * @var db_mysql $db |
||
| 39 | */ |
||
| 40 | protected static $db = null; |
||
| 41 | /** |
||
| 42 | * Table name in DB |
||
| 43 | * |
||
| 44 | * @var string |
||
| 45 | */ |
||
| 46 | protected static $_table = ''; |
||
| 47 | /** |
||
| 48 | * Name of ID field in DB |
||
| 49 | * |
||
| 50 | * @var string |
||
| 51 | */ |
||
| 52 | protected static $_dbIdFieldName = 'id'; |
||
| 53 | /** |
||
| 54 | * DB_ROW to Class translation scheme |
||
| 55 | * |
||
| 56 | * @var array |
||
| 57 | */ |
||
| 58 | protected static $_properties = array( |
||
| 59 | 'dbId' => array( |
||
| 60 | P_DB_FIELD => 'id', |
||
| 61 | ), |
||
| 62 | ); |
||
| 63 | |||
| 64 | /** |
||
| 65 | * Object list that should mimic object DB operations - i.e. units on fleet |
||
| 66 | * |
||
| 67 | * @var IDbRow[] |
||
| 68 | */ |
||
| 69 | protected $triggerDbOperationOn = array(); // Not a static - because it's an object array |
||
| 70 | /** |
||
| 71 | * List of property names that was changed since last DB operation |
||
| 72 | * |
||
| 73 | * @var string[] |
||
| 74 | */ |
||
| 75 | protected $propertiesChanged = array(); |
||
| 76 | /** |
||
| 77 | * List of property names->$delta that was adjusted since last DB operation - and then need to be processed as Deltas |
||
| 78 | * |
||
| 79 | * @var string[] |
||
| 80 | */ |
||
| 81 | protected $propertiesAdjusted = array(); |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @var int |
||
| 85 | */ |
||
| 86 | protected $_dbId = 0; |
||
| 87 | |||
| 88 | /** |
||
| 89 | * Flag to skip lock on current Load operation |
||
| 90 | * |
||
| 91 | * @var bool |
||
| 92 | */ |
||
| 93 | protected $lockSkip = false; |
||
| 94 | |||
| 95 | |||
| 96 | // Some magic ******************************************************************************************************** |
||
| 97 | |||
| 98 | public function __construct() { |
||
| 101 | |||
| 102 | /** |
||
| 103 | * Getter with support of protected methods |
||
| 104 | * |
||
| 105 | * @param $name |
||
| 106 | * |
||
| 107 | * @return mixed |
||
| 108 | */ |
||
| 109 | public function __get($name) { |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Setter with support of protected properties/methods |
||
| 116 | * |
||
| 117 | * @param $name |
||
| 118 | * @param $value |
||
| 119 | */ |
||
| 120 | // TODO - сеттер должен параллельно изменять значение db_row - for now... |
||
| 121 | public function __set($name, $value) { |
||
| 125 | |||
| 126 | /** |
||
| 127 | * Handles getters and setters |
||
| 128 | * |
||
| 129 | * @param string $name |
||
| 130 | * @param array $arguments |
||
| 131 | * |
||
| 132 | * @return mixed |
||
| 133 | * @throws ExceptionPropertyNotExists |
||
| 134 | */ |
||
| 135 | public function __call($name, $arguments) { |
||
| 172 | |||
| 173 | // IDBrow Implementation ********************************************************************************************* |
||
| 174 | |||
| 175 | /** |
||
| 176 | * Loading object from DB by primary ID |
||
| 177 | * |
||
| 178 | * @param int $dbId |
||
| 179 | * @param bool $lockSkip |
||
| 180 | * |
||
| 181 | * @return |
||
| 182 | */ |
||
| 183 | public function dbLoad($dbId, $lockSkip = false) { |
||
| 206 | |||
| 207 | /** |
||
| 208 | * Lock all fields that belongs to operation |
||
| 209 | * |
||
| 210 | * @param int $dbId |
||
| 211 | * |
||
| 212 | * @return |
||
| 213 | * param DBLock $dbRow - Object that accumulates locks |
||
| 214 | * |
||
| 215 | */ |
||
| 216 | abstract public function dbGetLockById($dbId); |
||
| 217 | |||
| 218 | /** |
||
| 219 | * Saving object to DB |
||
| 220 | * This is meta-method: |
||
| 221 | * - if object is new - then it inserted to DB; |
||
| 222 | * - if object is empty - it deleted from DB; |
||
| 223 | * - otherwise object is updated in DB; |
||
| 224 | */ |
||
| 225 | // TODO - perform operations only if properties was changed |
||
| 226 | public function dbSave() { |
||
| 254 | |||
| 255 | |||
| 256 | |||
| 257 | // CRUD ************************************************************************************************************** |
||
| 258 | |||
| 259 | /** |
||
| 260 | * Inserts record to DB |
||
| 261 | * |
||
| 262 | * @return int|string |
||
| 263 | */ |
||
| 264 | // TODO - protected |
||
| 265 | public function dbInsert() { |
||
| 277 | |||
| 278 | /** |
||
| 279 | * Updates record in DB |
||
| 280 | */ |
||
| 281 | // TODO - protected |
||
| 282 | public function dbUpdate() { |
||
| 289 | |||
| 290 | /** |
||
| 291 | * Deletes record from DB |
||
| 292 | */ |
||
| 293 | // TODO - protected |
||
| 294 | public function dbDelete() { |
||
| 302 | |||
| 303 | /** |
||
| 304 | * Является ли запись новой - т.е. не имеет своей записи в БД |
||
| 305 | * |
||
| 306 | * @return bool |
||
| 307 | */ |
||
| 308 | public function isNew() { |
||
| 311 | |||
| 312 | /** |
||
| 313 | * Является ли запись пустой - т.е. при исполнении _dbSave должен быть удалён |
||
| 314 | * |
||
| 315 | * @return bool |
||
| 316 | */ |
||
| 317 | abstract public function isEmpty(); |
||
| 318 | |||
| 319 | // Other Methods ***************************************************************************************************** |
||
| 320 | |||
| 321 | // /** |
||
| 322 | // * Resets object to zero state |
||
| 323 | // * @see DBRow::dbLoad() |
||
| 324 | // * |
||
| 325 | // * @return void |
||
| 326 | // */ |
||
| 327 | // protected function _reset() { |
||
| 328 | // $this->dbRowParse(array()); |
||
| 329 | // } |
||
| 330 | |||
| 331 | /** |
||
| 332 | * Парсит запись из БД в поля объекта |
||
| 333 | * |
||
| 334 | * @param array $db_row |
||
| 335 | */ |
||
| 336 | public function dbRowParse(array $db_row) { |
||
| 366 | |||
| 367 | /** |
||
| 368 | * Делает из свойств класса массив db_field_name => db_field_value |
||
| 369 | * |
||
| 370 | * @return array |
||
| 371 | */ |
||
| 372 | protected function dbMakeFieldSet($isUpdate = false) { |
||
| 409 | |||
| 410 | /** |
||
| 411 | * Check if DB field changed on property change and if it changed - returns name of property which triggered change |
||
| 412 | * |
||
| 413 | * @param string $fieldName |
||
| 414 | * |
||
| 415 | * @return string|false |
||
| 416 | */ |
||
| 417 | protected function isFieldChanged($fieldName) { |
||
| 437 | |||
| 438 | /** |
||
| 439 | * @param array $field_set |
||
| 440 | * |
||
| 441 | * @return int|string |
||
| 442 | */ |
||
| 443 | protected function db_field_set_create(array $field_set) { |
||
| 457 | |||
| 458 | /** |
||
| 459 | * @param array $field_set |
||
| 460 | * |
||
| 461 | * @return array|bool|mysqli_result|null |
||
| 462 | */ |
||
| 463 | // TODO - UPDATE ONLY CHANGED FIELDS |
||
| 464 | protected function db_field_update(array $field_set) { |
||
| 489 | |||
| 490 | } |
||
| 491 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.