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 CI_DB_oci8_driver 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 CI_DB_oci8_driver, and based on these observations, apply Extract Interface, too.
1 | <?php if ( ! defined('BASEPATH')) { |
||
44 | class CI_DB_oci8_driver extends CI_DB { |
||
45 | |||
46 | var $dbdriver = 'oci8'; |
||
47 | |||
48 | // The character used for excaping |
||
49 | var $_escape_char = '"'; |
||
50 | |||
51 | // clause and character used for LIKE escape sequences |
||
52 | var $_like_escape_str = " escape '%s' "; |
||
53 | var $_like_escape_chr = '!'; |
||
54 | |||
55 | /** |
||
56 | * The syntax to count rows is slightly different across different |
||
57 | * database engines, so this string appears in each driver and is |
||
58 | * used for the count_all() and count_all_results() functions. |
||
59 | */ |
||
60 | var $_count_string = "SELECT COUNT(1) AS "; |
||
61 | var $_random_keyword = ' ASC'; // not currently supported |
||
62 | |||
63 | // Set "auto commit" by default |
||
64 | var $_commit = OCI_COMMIT_ON_SUCCESS; |
||
65 | |||
66 | // need to track statement id and cursor id |
||
67 | var $stmt_id; |
||
68 | var $curs_id; |
||
69 | |||
70 | // if we use a limit, we will add a field that will |
||
71 | // throw off num_fields later |
||
72 | var $limit_used; |
||
73 | |||
74 | /** |
||
75 | * Non-persistent database connection |
||
76 | * |
||
77 | * @access private called by the base class |
||
78 | * @return resource |
||
79 | */ |
||
80 | public function db_connect() |
||
84 | |||
85 | // -------------------------------------------------------------------- |
||
86 | |||
87 | /** |
||
88 | * Persistent database connection |
||
89 | * |
||
90 | * @access private called by the base class |
||
91 | * @return resource |
||
92 | */ |
||
93 | public function db_pconnect() |
||
94 | { |
||
95 | return @oci_pconnect($this->username, $this->password, $this->hostname, $this->char_set); |
||
96 | } |
||
97 | |||
98 | // -------------------------------------------------------------------- |
||
99 | |||
100 | /** |
||
101 | * Reconnect |
||
102 | * |
||
103 | * Keep / reestablish the db connection if no queries have been |
||
104 | * sent for a length of time exceeding the server's idle timeout |
||
105 | * |
||
106 | * @access public |
||
107 | * @return void |
||
108 | */ |
||
109 | public function reconnect() |
||
110 | { |
||
111 | // not implemented in oracle |
||
112 | return; |
||
113 | } |
||
114 | |||
115 | // -------------------------------------------------------------------- |
||
116 | |||
117 | /** |
||
118 | * Select the database |
||
119 | * |
||
120 | * @access private called by the base class |
||
121 | * @return boolean |
||
122 | */ |
||
123 | public function db_select() |
||
124 | { |
||
125 | // Not in Oracle - schemas are actually usernames |
||
126 | return TRUE; |
||
127 | } |
||
128 | |||
129 | // -------------------------------------------------------------------- |
||
130 | |||
131 | /** |
||
132 | * Set client character set |
||
133 | * |
||
134 | * @access public |
||
135 | * @param string |
||
136 | * @param string |
||
137 | * @return boolean |
||
138 | */ |
||
139 | public function db_set_charset($charset, $collation) |
||
140 | { |
||
141 | // @todo - add support if needed |
||
142 | return TRUE; |
||
143 | } |
||
144 | |||
145 | // -------------------------------------------------------------------- |
||
146 | |||
147 | /** |
||
148 | * Version number query string |
||
149 | * |
||
150 | * @access protected |
||
151 | * @return string |
||
152 | */ |
||
153 | protected function _version() |
||
154 | { |
||
155 | return oci_server_version($this->conn_id); |
||
156 | } |
||
157 | |||
158 | // -------------------------------------------------------------------- |
||
159 | |||
160 | /** |
||
161 | * Execute the query |
||
162 | * |
||
163 | * @access protected called by the base class |
||
164 | * @param string an SQL query |
||
165 | * @return resource |
||
166 | */ |
||
167 | protected function _execute($sql) |
||
176 | |||
177 | /** |
||
178 | * Generate a statement ID |
||
179 | * |
||
180 | * @access private |
||
181 | * @param string an SQL query |
||
182 | * @return none |
||
183 | */ |
||
184 | private function _set_stmt_id($sql) |
||
185 | { |
||
186 | if ( ! is_resource($this->stmt_id)) |
||
187 | { |
||
188 | $this->stmt_id = oci_parse($this->conn_id, $this->_prep_query($sql)); |
||
189 | } |
||
190 | } |
||
191 | |||
192 | // -------------------------------------------------------------------- |
||
193 | |||
194 | /** |
||
195 | * Prep the query |
||
196 | * |
||
197 | * If needed, each database adapter can prep the query string |
||
198 | * |
||
199 | * @access private called by execute() |
||
200 | * @param string an SQL query |
||
201 | * @return string |
||
202 | */ |
||
203 | private function _prep_query($sql) |
||
204 | { |
||
205 | return $sql; |
||
206 | } |
||
207 | |||
208 | // -------------------------------------------------------------------- |
||
209 | |||
210 | /** |
||
211 | * getCursor. Returns a cursor from the datbase |
||
212 | * |
||
213 | * @access public |
||
214 | * @return resource id |
||
215 | */ |
||
216 | public function get_cursor() |
||
221 | |||
222 | // -------------------------------------------------------------------- |
||
223 | |||
224 | /** |
||
225 | * Stored Procedure. Executes a stored procedure |
||
226 | * |
||
227 | * @access public |
||
228 | * @param package package stored procedure is in |
||
229 | * @param procedure stored procedure to execute |
||
230 | * @param params array of parameters |
||
231 | * @return array |
||
232 | * |
||
233 | * params array keys |
||
234 | * |
||
235 | * KEY OPTIONAL NOTES |
||
236 | * name no the name of the parameter should be in :<param_name> format |
||
237 | * value no the value of the parameter. If this is an OUT or IN OUT parameter, |
||
238 | * this should be a reference to a variable |
||
239 | * type yes the type of the parameter |
||
240 | * length yes the max size of the parameter |
||
241 | */ |
||
242 | public function stored_procedure($package, $procedure, $params) |
||
274 | |||
275 | // -------------------------------------------------------------------- |
||
276 | |||
277 | /** |
||
278 | * Bind parameters |
||
279 | * |
||
280 | * @access private |
||
281 | * @return none |
||
282 | */ |
||
283 | private function _bind_params($params) |
||
303 | |||
304 | // -------------------------------------------------------------------- |
||
305 | |||
306 | /** |
||
307 | * Begin Transaction |
||
308 | * |
||
309 | * @access public |
||
310 | * @return bool |
||
311 | */ |
||
312 | View Code Duplication | public function trans_begin($test_mode = FALSE) |
|
333 | |||
334 | // -------------------------------------------------------------------- |
||
335 | |||
336 | /** |
||
337 | * Commit Transaction |
||
338 | * |
||
339 | * @access public |
||
340 | * @return bool |
||
341 | */ |
||
342 | View Code Duplication | public function trans_commit() |
|
359 | |||
360 | // -------------------------------------------------------------------- |
||
361 | |||
362 | /** |
||
363 | * Rollback Transaction |
||
364 | * |
||
365 | * @access public |
||
366 | * @return bool |
||
367 | */ |
||
368 | View Code Duplication | public function trans_rollback() |
|
385 | |||
386 | // -------------------------------------------------------------------- |
||
387 | |||
388 | /** |
||
389 | * Escape String |
||
390 | * |
||
391 | * @access public |
||
392 | * @param string |
||
393 | * @param bool whether or not the string will be used in a LIKE condition |
||
394 | * @return string |
||
395 | */ |
||
396 | View Code Duplication | public function escape_str($str, $like = FALSE) |
|
397 | { |
||
398 | if (is_array($str)) |
||
399 | { |
||
400 | foreach ($str as $key => $val) |
||
401 | { |
||
402 | $str[$key] = $this->escape_str($val, $like); |
||
403 | } |
||
404 | |||
405 | return $str; |
||
406 | } |
||
407 | |||
408 | $str = remove_invisible_characters($str); |
||
409 | |||
410 | // escape LIKE condition wildcards |
||
411 | if ($like === TRUE) |
||
412 | { |
||
413 | $str = str_replace(array('%', '_', $this->_like_escape_chr), |
||
414 | array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), |
||
415 | $str); |
||
416 | } |
||
417 | |||
418 | return $str; |
||
419 | } |
||
420 | |||
421 | // -------------------------------------------------------------------- |
||
422 | |||
423 | /** |
||
424 | * Affected Rows |
||
425 | * |
||
426 | * @access public |
||
427 | * @return integer |
||
428 | */ |
||
429 | public function affected_rows() |
||
433 | |||
434 | // -------------------------------------------------------------------- |
||
435 | |||
436 | /** |
||
437 | * Insert ID |
||
438 | * |
||
439 | * @access public |
||
440 | * @return integer |
||
441 | */ |
||
442 | public function insert_id() |
||
447 | |||
448 | // -------------------------------------------------------------------- |
||
449 | |||
450 | /** |
||
451 | * "Count All" query |
||
452 | * |
||
453 | * Generates a platform-specific query string that counts all records in |
||
454 | * the specified database |
||
455 | * |
||
456 | * @access public |
||
457 | * @param string |
||
458 | * @return integer |
||
459 | */ |
||
460 | View Code Duplication | public function count_all($table = '') |
|
461 | { |
||
462 | if ($table == '') |
||
463 | { |
||
464 | return 0; |
||
465 | } |
||
466 | |||
467 | $query = $this->query($this->_count_string.$this->_protect_identifiers('numrows')." FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE)); |
||
468 | |||
469 | if ($query == FALSE) |
||
470 | { |
||
471 | return 0; |
||
472 | } |
||
473 | |||
474 | $row = $query->row(); |
||
475 | $this->_reset_select(); |
||
476 | return (int)$row->numrows; |
||
477 | } |
||
478 | |||
479 | // -------------------------------------------------------------------- |
||
480 | |||
481 | /** |
||
482 | * Show table query |
||
483 | * |
||
484 | * Generates a platform-specific query string so that the table names can be fetched |
||
485 | * |
||
486 | * @access protected |
||
487 | * @param boolean |
||
488 | * @return string |
||
489 | */ |
||
490 | View Code Duplication | protected function _list_tables($prefix_limit = FALSE) |
|
501 | |||
502 | // -------------------------------------------------------------------- |
||
503 | |||
504 | /** |
||
505 | * Show column query |
||
506 | * |
||
507 | * Generates a platform-specific query string so that the column names can be fetched |
||
508 | * |
||
509 | * @access protected |
||
510 | * @param string the table name |
||
511 | * @return string |
||
512 | */ |
||
513 | protected function _list_columns($table = '') |
||
517 | |||
518 | // -------------------------------------------------------------------- |
||
519 | |||
520 | /** |
||
521 | * Field data query |
||
522 | * |
||
523 | * Generates a platform-specific query so that the column data can be retrieved |
||
524 | * |
||
525 | * @access public |
||
526 | * @param string the table name |
||
527 | * @return string |
||
528 | */ |
||
529 | protected function _field_data($table) |
||
530 | { |
||
531 | return "SELECT * FROM ".$table." where rownum = 1"; |
||
532 | } |
||
533 | |||
534 | // -------------------------------------------------------------------- |
||
535 | |||
536 | /** |
||
537 | * The error message string |
||
538 | * |
||
539 | * @access protected |
||
540 | * @return string |
||
541 | */ |
||
542 | protected function _error_message() |
||
543 | { |
||
544 | // If the error was during connection, no conn_id should be passed |
||
545 | $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error(); |
||
546 | return $error['message']; |
||
547 | } |
||
548 | |||
549 | // -------------------------------------------------------------------- |
||
550 | |||
551 | /** |
||
552 | * The error message number |
||
553 | * |
||
554 | * @access protected |
||
555 | * @return integer |
||
556 | */ |
||
557 | protected function _error_number() |
||
558 | { |
||
559 | // Same as _error_message() |
||
560 | $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error(); |
||
561 | return $error['code']; |
||
562 | } |
||
563 | |||
564 | // -------------------------------------------------------------------- |
||
565 | |||
566 | /** |
||
567 | * Escape the SQL Identifiers |
||
568 | * |
||
569 | * This function escapes column and table names |
||
570 | * |
||
571 | * @access protected |
||
572 | * @param string |
||
573 | * @return string |
||
574 | */ |
||
575 | View Code Duplication | protected function _escape_identifiers($item) |
|
576 | { |
||
577 | if ($this->_escape_char == '') |
||
578 | { |
||
579 | return $item; |
||
580 | } |
||
581 | |||
582 | foreach ($this->_reserved_identifiers as $id) |
||
583 | { |
||
584 | if (strpos($item, '.'.$id) !== FALSE) |
||
585 | { |
||
586 | $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.', $item); |
||
587 | |||
588 | // remove duplicates if the user already included the escape |
||
589 | return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); |
||
590 | } |
||
591 | } |
||
592 | |||
593 | if (strpos($item, '.') !== FALSE) |
||
594 | { |
||
595 | $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; |
||
596 | } else |
||
597 | { |
||
598 | $str = $this->_escape_char.$item.$this->_escape_char; |
||
599 | } |
||
600 | |||
601 | // remove duplicates if the user already included the escape |
||
602 | return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); |
||
603 | } |
||
604 | |||
605 | // -------------------------------------------------------------------- |
||
606 | |||
607 | /** |
||
608 | * From Tables |
||
609 | * |
||
610 | * This function implicitly groups FROM tables so there is no confusion |
||
611 | * about operator precedence in harmony with SQL standards |
||
612 | * |
||
613 | * @access protected |
||
614 | * @param type |
||
615 | * @return string |
||
616 | */ |
||
617 | protected function _from_tables($tables) |
||
618 | { |
||
619 | if ( ! is_array($tables)) |
||
620 | { |
||
621 | $tables = array($tables); |
||
622 | } |
||
623 | |||
624 | return implode(', ', $tables); |
||
625 | } |
||
626 | |||
627 | // -------------------------------------------------------------------- |
||
628 | |||
629 | /** |
||
630 | * Insert statement |
||
631 | * |
||
632 | * Generates a platform-specific insert string from the supplied data |
||
633 | * |
||
634 | * @access public |
||
635 | * @param string the table name |
||
636 | * @param array the insert keys |
||
637 | * @param array the insert values |
||
638 | * @return string |
||
639 | */ |
||
640 | protected function _insert($table, $keys, $values) |
||
641 | { |
||
642 | return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; |
||
643 | } |
||
644 | |||
645 | // -------------------------------------------------------------------- |
||
646 | |||
647 | /** |
||
648 | * Insert_batch statement |
||
649 | * |
||
650 | * Generates a platform-specific insert string from the supplied data |
||
651 | * |
||
652 | * @access protected |
||
653 | * @param string the table name |
||
654 | * @param array the insert keys |
||
655 | * @param array the insert values |
||
656 | * @return string |
||
657 | */ |
||
658 | protected function _insert_batch($table, $keys, $values) |
||
672 | |||
673 | // -------------------------------------------------------------------- |
||
674 | |||
675 | /** |
||
676 | * Update statement |
||
677 | * |
||
678 | * Generates a platform-specific update string from the supplied data |
||
679 | * |
||
680 | * @access protected |
||
681 | * @param string the table name |
||
682 | * @param array the update data |
||
683 | * @param array the where clause |
||
684 | * @param array the orderby clause |
||
685 | * @param array the limit clause |
||
686 | * @return string |
||
687 | */ |
||
688 | View Code Duplication | protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE) |
|
689 | { |
||
690 | foreach ($values as $key => $val) |
||
691 | { |
||
692 | $valstr[] = $key." = ".$val; |
||
693 | } |
||
694 | |||
695 | $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; |
||
696 | |||
697 | $orderby = (count($orderby) >= 1) ? ' ORDER BY '.implode(", ", $orderby) : ''; |
||
698 | |||
699 | $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); |
||
700 | |||
701 | $sql .= ($where != '' AND count($where) >= 1) ? " WHERE ".implode(" ", $where) : ''; |
||
702 | |||
703 | $sql .= $orderby.$limit; |
||
704 | |||
705 | return $sql; |
||
706 | } |
||
707 | |||
708 | // -------------------------------------------------------------------- |
||
709 | |||
710 | /** |
||
711 | * Truncate statement |
||
712 | * |
||
713 | * Generates a platform-specific truncate string from the supplied data |
||
714 | * If the database does not support the truncate() command |
||
715 | * This function maps to "DELETE FROM table" |
||
716 | * |
||
717 | * @access protected |
||
718 | * @param string the table name |
||
719 | * @return string |
||
720 | */ |
||
721 | protected function _truncate($table) |
||
725 | |||
726 | // -------------------------------------------------------------------- |
||
727 | |||
728 | /** |
||
729 | * Delete statement |
||
730 | * |
||
731 | * Generates a platform-specific delete string from the supplied data |
||
732 | * |
||
733 | * @access protected |
||
734 | * @param string the table name |
||
735 | * @param array the where clause |
||
736 | * @param string the limit clause |
||
737 | * @return string |
||
738 | */ |
||
739 | View Code Duplication | protected function _delete($table, $where = array(), $like = array(), $limit = FALSE) |
|
759 | |||
760 | // -------------------------------------------------------------------- |
||
761 | |||
762 | /** |
||
763 | * Limit string |
||
764 | * |
||
765 | * Generates a platform-specific LIMIT clause |
||
766 | * |
||
767 | * @access protected |
||
768 | * @param string the sql query string |
||
769 | * @param integer the number of rows to limit the query to |
||
770 | * @param integer the offset value |
||
771 | * @return string |
||
772 | */ |
||
773 | protected function _limit($sql, $limit, $offset) |
||
788 | |||
789 | // -------------------------------------------------------------------- |
||
790 | |||
791 | /** |
||
792 | * Close DB Connection |
||
793 | * |
||
794 | * @access protected |
||
795 | * @param resource |
||
796 | * @return void |
||
797 | */ |
||
798 | protected function _close($conn_id) |
||
802 | |||
803 | |||
804 | } |
||
805 | |||
810 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.