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 Xhgui_Storage_PDO 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 Xhgui_Storage_PDO, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 5 | class Xhgui_Storage_PDO extends Xhgui_Storage_Abstract implements |
||
| 6 | \Xhgui_StorageInterface, |
||
| 7 | \Xhgui_WatchedFunctionsStorageInterface |
||
| 8 | { |
||
| 9 | |||
| 10 | /** |
||
| 11 | * @var \PDO |
||
| 12 | */ |
||
| 13 | protected $connection; |
||
| 14 | |||
| 15 | /** |
||
| 16 | * @var bool |
||
| 17 | */ |
||
| 18 | protected $jsonMode = false; |
||
| 19 | |||
| 20 | /** |
||
| 21 | * PDO constructor. |
||
| 22 | * @param $config |
||
| 23 | */ |
||
| 24 | public function __construct($config) |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @inheritDoc |
||
| 44 | * @param \Xhgui_Storage_Filter $filter |
||
| 45 | * @param bool $projections |
||
| 46 | * @return \Xhgui_Storage_ResultSet |
||
| 47 | */ |
||
| 48 | public function find(\Xhgui_Storage_Filter $filter, $projections = false) |
||
| 49 | { |
||
| 50 | list($query, $params) = $this->getQuery($filter, false); |
||
| 51 | |||
| 52 | try { |
||
| 53 | $stmt = $this->connection->prepare($query); |
||
| 54 | $stmt->execute($params); |
||
| 55 | } catch (\Exception $e) { |
||
| 56 | print_r($e->getMessage()); |
||
| 57 | exit; |
||
| 58 | } |
||
| 59 | |||
| 60 | $tmp = []; |
||
| 61 | while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { |
||
| 62 | $meta = json_decode($row['meta'], true); |
||
| 63 | View Code Duplication | if ($filter->getCookie()) { |
|
| 64 | // because cookie is not parsed and stored in separate structure we need to double check if |
||
| 65 | // value that we search is in fact in http cookie server field. SQL filter only checks whole |
||
| 66 | // meta |
||
| 67 | if (strpos($meta['SERVER']['HTTP_COOKIE'], $filter->getCookie()) === false) { |
||
| 68 | continue; |
||
| 69 | } |
||
| 70 | } |
||
| 71 | $tmp[$row['id']] = $row; |
||
| 72 | $tmp[$row['id']]['profile'] = json_decode($row['profiles'], true); |
||
| 73 | $tmp[$row['id']]['meta'] = $meta; |
||
| 74 | } |
||
| 75 | |||
| 76 | return new \Xhgui_Storage_ResultSet($tmp); |
||
| 77 | } |
||
| 78 | |||
| 79 | /** |
||
| 80 | * Get query that is used for both list and count |
||
| 81 | * |
||
| 82 | * @param \Xhgui_Storage_Filter $filter |
||
| 83 | * @param bool $count |
||
| 84 | * @return array |
||
| 85 | */ |
||
| 86 | protected function getQuery(\Xhgui_Storage_Filter $filter, $count = false) |
||
| 244 | |||
| 245 | /** |
||
| 246 | * @inheritDoc |
||
| 247 | * @param Xhgui_Storage_Filter $filter |
||
| 248 | * @param $col |
||
| 249 | * @param int $percentile |
||
| 250 | * @return array |
||
| 251 | * @throws Exception |
||
| 252 | */ |
||
| 253 | public function aggregate(\Xhgui_Storage_Filter $filter, $col, $percentile = 1) |
||
| 293 | |||
| 294 | /** |
||
| 295 | * @inheritDoc |
||
| 296 | * @@param Xhgui_Storage_Filter $filter |
||
| 297 | * @return int |
||
| 298 | */ |
||
| 299 | public function count(\Xhgui_Storage_Filter $filter) |
||
| 317 | |||
| 318 | /** |
||
| 319 | * @inheritDoc |
||
| 320 | * @param $id |
||
| 321 | * @return mixed |
||
| 322 | */ |
||
| 323 | public function findOne($id) |
||
| 344 | |||
| 345 | /** |
||
| 346 | * @inheritDoc |
||
| 347 | * @param $id |
||
| 348 | */ |
||
| 349 | public function remove($id) |
||
| 367 | |||
| 368 | /** |
||
| 369 | * @inheritDoc |
||
| 370 | * Remove all data from profile tables |
||
| 371 | */ |
||
| 372 | public function drop() |
||
| 378 | |||
| 379 | /** |
||
| 380 | * @inheritDoc |
||
| 381 | * @return array |
||
| 382 | */ |
||
| 383 | public function getWatchedFunctions() |
||
| 388 | |||
| 389 | /** |
||
| 390 | * @inheritDoc |
||
| 391 | * @param $name |
||
| 392 | * @return bool |
||
| 393 | */ |
||
| 394 | public function addWatchedFunction($name) |
||
| 404 | |||
| 405 | /** |
||
| 406 | * @inheritDoc |
||
| 407 | * @param $id |
||
| 408 | * @param $name |
||
| 409 | */ |
||
| 410 | public function updateWatchedFunction($id, $name) |
||
| 415 | |||
| 416 | /** |
||
| 417 | * @inheritDoc |
||
| 418 | * @param $id |
||
| 419 | */ |
||
| 420 | public function removeWatchedFunction($id) |
||
| 425 | |||
| 426 | /** |
||
| 427 | * This method will look into json stored data in native way (if db supports that) and it will match row based on that. |
||
| 428 | * |
||
| 429 | * @todo this should be moved to engine specific storage classes in the future. |
||
| 430 | * @param array $where |
||
| 431 | * @param array $params |
||
| 432 | * @param $field |
||
| 433 | * @param $value |
||
| 434 | * @param $fieldToLookIn |
||
| 435 | * @param array $path |
||
| 436 | * @return array |
||
| 437 | */ |
||
| 438 | protected function compareWithJson(array $where, array $params, $field, $value, $fieldToLookIn, array $path) |
||
| 460 | } |
||
| 461 |
This check looks for assignments to scalar types that may be of the wrong type.
To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.