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_Loader 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_Loader, and based on these observations, apply Extract Interface, too.
1 | <?php if ( ! defined('BASEPATH')) { |
||
31 | class CI_Loader { |
||
32 | |||
33 | // All these are set automatically. Don't mess with them. |
||
34 | /** |
||
35 | * Nesting level of the output buffering mechanism |
||
36 | * |
||
37 | * @var int |
||
38 | * @access protected |
||
39 | */ |
||
40 | protected $_ci_ob_level; |
||
41 | /** |
||
42 | * List of paths to load views from |
||
43 | * |
||
44 | * @var array |
||
45 | * @access protected |
||
46 | */ |
||
47 | protected $_ci_view_paths = array(); |
||
48 | /** |
||
49 | * List of paths to load libraries from |
||
50 | * |
||
51 | * @var array |
||
52 | * @access protected |
||
53 | */ |
||
54 | protected $_ci_library_paths = array(); |
||
55 | /** |
||
56 | * List of paths to load models from |
||
57 | * |
||
58 | * @var array |
||
59 | * @access protected |
||
60 | */ |
||
61 | protected $_ci_model_paths = array(); |
||
62 | /** |
||
63 | * List of paths to load helpers from |
||
64 | * |
||
65 | * @var array |
||
66 | * @access protected |
||
67 | */ |
||
68 | protected $_ci_helper_paths = array(); |
||
69 | /** |
||
70 | * List of loaded base classes |
||
71 | * Set by the controller class |
||
72 | * |
||
73 | * @var array |
||
74 | * @access protected |
||
75 | */ |
||
76 | protected $_base_classes = array(); // Set by the controller class |
||
77 | /** |
||
78 | * List of cached variables |
||
79 | * |
||
80 | * @var array |
||
81 | * @access protected |
||
82 | */ |
||
83 | protected $_ci_cached_vars = array(); |
||
84 | /** |
||
85 | * List of loaded classes |
||
86 | * |
||
87 | * @var array |
||
88 | * @access protected |
||
89 | */ |
||
90 | protected $_ci_classes = array(); |
||
91 | /** |
||
92 | * List of loaded files |
||
93 | * |
||
94 | * @var array |
||
95 | * @access protected |
||
96 | */ |
||
97 | protected $_ci_loaded_files = array(); |
||
98 | /** |
||
99 | * List of loaded models |
||
100 | * |
||
101 | * @var array |
||
102 | * @access protected |
||
103 | */ |
||
104 | protected $_ci_models = array(); |
||
105 | /** |
||
106 | * List of loaded helpers |
||
107 | * |
||
108 | * @var array |
||
109 | * @access protected |
||
110 | */ |
||
111 | protected $_ci_helpers = array(); |
||
112 | /** |
||
113 | * List of class name mappings |
||
114 | * |
||
115 | * @var array |
||
116 | * @access protected |
||
117 | */ |
||
118 | protected $_ci_varmap = array('unit_test' => 'unit', |
||
119 | 'user_agent' => 'agent'); |
||
120 | |||
121 | /** |
||
122 | * Constructor |
||
123 | * |
||
124 | * Sets the path to the view files and gets the initial output buffering level |
||
125 | */ |
||
126 | public function __construct() |
||
136 | |||
137 | // -------------------------------------------------------------------- |
||
138 | |||
139 | /** |
||
140 | * Initialize the Loader |
||
141 | * |
||
142 | * This method is called once in CI_Controller. |
||
143 | * |
||
144 | * @param array |
||
145 | * @return CI_Loader |
||
146 | */ |
||
147 | public function initialize() |
||
158 | |||
159 | // -------------------------------------------------------------------- |
||
160 | |||
161 | /** |
||
162 | * Is Loaded |
||
163 | * |
||
164 | * A utility function to test if a class is in the self::$_ci_classes array. |
||
165 | * This function returns the object name if the class tested for is loaded, |
||
166 | * and returns FALSE if it isn't. |
||
167 | * |
||
168 | * It is mainly used in the form_helper -> _get_validation_object() |
||
169 | * |
||
170 | * @param string class being checked for |
||
171 | * @return mixed class object name on the CI SuperObject or FALSE |
||
172 | */ |
||
173 | public function is_loaded($class) |
||
182 | |||
183 | // -------------------------------------------------------------------- |
||
184 | |||
185 | /** |
||
186 | * Class Loader |
||
187 | * |
||
188 | * This function lets users load and instantiate classes. |
||
189 | * It is designed to be called from a user's app controllers. |
||
190 | * |
||
191 | * @param string the name of the class |
||
192 | * @param mixed the optional parameters |
||
193 | * @param string an optional object name |
||
194 | * @return null|false |
||
195 | */ |
||
196 | public function library($library = '', $params = NULL, $object_name = NULL) |
||
220 | |||
221 | // -------------------------------------------------------------------- |
||
222 | |||
223 | /** |
||
224 | * Model Loader |
||
225 | * |
||
226 | * This function lets users load and instantiate models. |
||
227 | * |
||
228 | * @param string the name of the class |
||
229 | * @param string name for the model |
||
230 | * @param bool database connection |
||
231 | * @return void |
||
232 | */ |
||
233 | public function model($model, $name = '', $db_conn = FALSE) |
||
314 | |||
315 | // -------------------------------------------------------------------- |
||
316 | |||
317 | /** |
||
318 | * Database Loader |
||
319 | * |
||
320 | * @param string the DB credentials |
||
321 | * @param bool whether to return the DB object |
||
322 | * @param bool whether to enable active record (this allows us to override the config setting) |
||
323 | * @return object |
||
324 | */ |
||
325 | public function database($params = '', $return = FALSE, $active_record = NULL) |
||
350 | |||
351 | // -------------------------------------------------------------------- |
||
352 | |||
353 | /** |
||
354 | * Load the Utilities Class |
||
355 | * |
||
356 | * @return string |
||
357 | */ |
||
358 | View Code Duplication | public function dbutil() |
|
377 | |||
378 | // -------------------------------------------------------------------- |
||
379 | |||
380 | /** |
||
381 | * Load the Database Forge Class |
||
382 | * |
||
383 | * @return string |
||
384 | */ |
||
385 | View Code Duplication | public function dbforge() |
|
400 | |||
401 | // -------------------------------------------------------------------- |
||
402 | |||
403 | /** |
||
404 | * Load View |
||
405 | * |
||
406 | * This function is used to load a "view" file. It has three parameters: |
||
407 | * |
||
408 | * 1. The name of the "view" file to be included. |
||
409 | * 2. An associative array of data to be extracted for use in the view. |
||
410 | * 3. TRUE/FALSE - whether to return the data or load it. In |
||
411 | * some cases it's advantageous to be able to return data so that |
||
412 | * a developer can process it in some way. |
||
413 | * |
||
414 | * @param string |
||
415 | * @param array |
||
416 | * @param bool |
||
417 | * @return string|null |
||
418 | */ |
||
419 | public function view($view, $vars = array(), $return = FALSE) |
||
423 | |||
424 | // -------------------------------------------------------------------- |
||
425 | |||
426 | /** |
||
427 | * Load File |
||
428 | * |
||
429 | * This is a generic file loader |
||
430 | * |
||
431 | * @param string |
||
432 | * @param bool |
||
433 | * @return string |
||
434 | */ |
||
435 | public function file($path, $return = FALSE) |
||
439 | |||
440 | // -------------------------------------------------------------------- |
||
441 | |||
442 | /** |
||
443 | * Set Variables |
||
444 | * |
||
445 | * Once variables are set they become available within |
||
446 | * the controller class and its "view" files. |
||
447 | * |
||
448 | * @param array |
||
449 | * @param string |
||
450 | * @return void |
||
451 | */ |
||
452 | public function vars($vars = array(), $val = '') |
||
469 | |||
470 | // -------------------------------------------------------------------- |
||
471 | |||
472 | /** |
||
473 | * Get Variable |
||
474 | * |
||
475 | * Check if a variable is set and retrieve it. |
||
476 | * |
||
477 | * @param array |
||
478 | * @return void |
||
479 | */ |
||
480 | public function get_var($key) |
||
484 | |||
485 | // -------------------------------------------------------------------- |
||
486 | |||
487 | /** |
||
488 | * Load Helper |
||
489 | * |
||
490 | * This function loads the specified helper file. |
||
491 | * |
||
492 | * @param mixed |
||
493 | * @return void |
||
494 | */ |
||
495 | public function helper($helpers = array()) |
||
544 | |||
545 | // -------------------------------------------------------------------- |
||
546 | |||
547 | /** |
||
548 | * Load Helpers |
||
549 | * |
||
550 | * This is simply an alias to the above function in case the |
||
551 | * user has written the plural form of this function. |
||
552 | * |
||
553 | * @param array |
||
554 | * @return void |
||
555 | */ |
||
556 | public function helpers($helpers = array()) |
||
560 | |||
561 | // -------------------------------------------------------------------- |
||
562 | |||
563 | /** |
||
564 | * Loads a language file |
||
565 | * |
||
566 | * @param array |
||
567 | * @param string |
||
568 | * @return void |
||
569 | */ |
||
570 | public function language($file = array(), $lang = '') |
||
584 | |||
585 | // -------------------------------------------------------------------- |
||
586 | |||
587 | /** |
||
588 | * Loads a config file |
||
589 | * |
||
590 | * @param string |
||
591 | * @param bool |
||
592 | * @param bool |
||
593 | * @return void |
||
594 | */ |
||
595 | public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) |
||
600 | |||
601 | // -------------------------------------------------------------------- |
||
602 | |||
603 | /** |
||
604 | * Driver |
||
605 | * |
||
606 | * Loads a driver library |
||
607 | * |
||
608 | * @param string the name of the class |
||
609 | * @param mixed the optional parameters |
||
610 | * @param string an optional object name |
||
611 | * @return false|null |
||
612 | */ |
||
613 | public function driver($library = '', $params = NULL, $object_name = NULL) |
||
635 | |||
636 | // -------------------------------------------------------------------- |
||
637 | |||
638 | /** |
||
639 | * Add Package Path |
||
640 | * |
||
641 | * Prepends a parent path to the library, model, helper, and config path arrays |
||
642 | * |
||
643 | * @param string |
||
644 | * @param boolean |
||
645 | * @return void |
||
646 | */ |
||
647 | public function add_package_path($path, $view_cascade = TRUE) |
||
661 | |||
662 | // -------------------------------------------------------------------- |
||
663 | |||
664 | /** |
||
665 | * Get Package Paths |
||
666 | * |
||
667 | * Return a list of all package paths, by default it will ignore BASEPATH. |
||
668 | * |
||
669 | * @param string |
||
670 | * @return void |
||
671 | */ |
||
672 | public function get_package_paths($include_base = FALSE) |
||
676 | |||
677 | // -------------------------------------------------------------------- |
||
678 | |||
679 | /** |
||
680 | * Remove Package Path |
||
681 | * |
||
682 | * Remove a path from the library, model, and helper path arrays if it exists |
||
683 | * If no path is provided, the most recently added path is removed. |
||
684 | * |
||
685 | * @param type |
||
686 | * @param bool |
||
687 | * @return type |
||
688 | */ |
||
689 | public function remove_package_path($path = '', $remove_config_path = TRUE) |
||
729 | |||
730 | // -------------------------------------------------------------------- |
||
731 | |||
732 | /** |
||
733 | * Loader |
||
734 | * |
||
735 | * This function is used to load views and files. |
||
736 | * Variables are prefixed with _ci_ to avoid symbol collision with |
||
737 | * variables made available to view files |
||
738 | * |
||
739 | * @param array |
||
740 | * @return string|null |
||
741 | */ |
||
742 | protected function _ci_load($_ci_data) |
||
864 | |||
865 | // -------------------------------------------------------------------- |
||
866 | |||
867 | /** |
||
868 | * Load class |
||
869 | * |
||
870 | * This function loads the requested class. |
||
871 | * |
||
872 | * @param string the item that is being loaded |
||
873 | * @param mixed any additional parameters |
||
874 | * @param string an optional object name |
||
875 | * @param string $class |
||
876 | * @return void |
||
877 | */ |
||
878 | protected function _ci_load_class($class, $params = NULL, $object_name = NULL) |
||
879 | { |
||
880 | // Get the class name, and while we're at it trim any slashes. |
||
881 | // The directory path can be included as part of the class name, |
||
882 | // but we don't want a leading slash |
||
883 | $class = str_replace('.php', '', trim($class, '/')); |
||
884 | |||
885 | // Was the path included with the class name? |
||
886 | // We look for a slash to determine this |
||
887 | $subdir = ''; |
||
888 | View Code Duplication | if (($last_slash = strrpos($class, '/')) !== FALSE) |
|
889 | { |
||
890 | // Extract the path |
||
891 | $subdir = substr($class, 0, $last_slash + 1); |
||
892 | |||
893 | // Get the filename from the path |
||
894 | $class = substr($class, $last_slash + 1); |
||
895 | } |
||
896 | |||
897 | // We'll test for both lowercase and capitalized versions of the file name |
||
898 | foreach (array(ucfirst($class), strtolower($class)) as $class) |
||
899 | { |
||
900 | $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php'; |
||
901 | |||
902 | // Is this a class extension request? |
||
903 | if (file_exists($subclass)) |
||
904 | { |
||
905 | $baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php'; |
||
906 | |||
907 | if ( ! file_exists($baseclass)) |
||
908 | { |
||
909 | log_message('error', "Unable to load the requested class: ".$class); |
||
910 | show_error("Unable to load the requested class: ".$class); |
||
911 | } |
||
912 | |||
913 | // Safety: Was the class already loaded by a previous call? |
||
914 | View Code Duplication | if (in_array($subclass, $this->_ci_loaded_files)) |
|
915 | { |
||
916 | // Before we deem this to be a duplicate request, let's see |
||
917 | // if a custom object name is being supplied. If so, we'll |
||
918 | // return a new instance of the object |
||
919 | if ( ! is_null($object_name)) |
||
920 | { |
||
921 | $CI = & get_instance(); |
||
922 | if ( ! isset($CI->$object_name)) |
||
923 | { |
||
924 | return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name); |
||
925 | } |
||
926 | } |
||
927 | |||
928 | $is_duplicate = TRUE; |
||
929 | log_message('debug', $class." class already loaded. Second attempt ignored."); |
||
930 | return; |
||
931 | } |
||
932 | |||
933 | include_once($baseclass); |
||
934 | include_once($subclass); |
||
935 | $this->_ci_loaded_files[] = $subclass; |
||
936 | |||
937 | return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name); |
||
938 | } |
||
939 | |||
940 | // Lets search for the requested library file and load it. |
||
941 | $is_duplicate = FALSE; |
||
942 | foreach ($this->_ci_library_paths as $path) |
||
943 | { |
||
944 | $filepath = $path.'libraries/'.$subdir.$class.'.php'; |
||
945 | |||
946 | // Does the file exist? No? Bummer... |
||
947 | if ( ! file_exists($filepath)) |
||
948 | { |
||
949 | continue; |
||
950 | } |
||
951 | |||
952 | // Safety: Was the class already loaded by a previous call? |
||
953 | View Code Duplication | if (in_array($filepath, $this->_ci_loaded_files)) |
|
954 | { |
||
955 | // Before we deem this to be a duplicate request, let's see |
||
956 | // if a custom object name is being supplied. If so, we'll |
||
957 | // return a new instance of the object |
||
958 | if ( ! is_null($object_name)) |
||
959 | { |
||
960 | $CI = & get_instance(); |
||
961 | if ( ! isset($CI->$object_name)) |
||
962 | { |
||
963 | return $this->_ci_init_class($class, '', $params, $object_name); |
||
964 | } |
||
965 | } |
||
966 | |||
967 | $is_duplicate = TRUE; |
||
968 | log_message('debug', $class." class already loaded. Second attempt ignored."); |
||
969 | return; |
||
970 | } |
||
971 | |||
972 | include_once($filepath); |
||
973 | $this->_ci_loaded_files[] = $filepath; |
||
974 | return $this->_ci_init_class($class, '', $params, $object_name); |
||
975 | } |
||
976 | |||
977 | } // END FOREACH |
||
978 | |||
979 | // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified? |
||
980 | if ($subdir == '') |
||
981 | { |
||
982 | $path = strtolower($class).'/'.$class; |
||
983 | return $this->_ci_load_class($path, $params); |
||
984 | } |
||
985 | |||
986 | // If we got this far we were unable to find the requested class. |
||
987 | // We do not issue errors if the load call failed due to a duplicate request |
||
988 | if ($is_duplicate == FALSE) |
||
989 | { |
||
990 | log_message('error', "Unable to load the requested class: ".$class); |
||
991 | show_error("Unable to load the requested class: ".$class); |
||
992 | } |
||
993 | } |
||
994 | |||
995 | // -------------------------------------------------------------------- |
||
996 | |||
997 | /** |
||
998 | * Instantiates a class |
||
999 | * |
||
1000 | * @param string |
||
1001 | * @param string |
||
1002 | * @param bool |
||
1003 | * @param string an optional object name |
||
1004 | * @param string $class |
||
1005 | * @return null |
||
1006 | */ |
||
1007 | protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL) |
||
1008 | { |
||
1009 | // Is there an associated config file for this class? Note: these should always be lowercase |
||
1010 | if ($config === NULL) |
||
1011 | { |
||
1012 | // Fetch the config paths containing any package paths |
||
1013 | $config_component = $this->_ci_get_component('config'); |
||
1014 | |||
1015 | if (is_array($config_component->_config_paths)) |
||
1016 | { |
||
1017 | // Break on the first found file, thus package files |
||
1018 | // are not overridden by default paths |
||
1019 | foreach ($config_component->_config_paths as $path) |
||
1020 | { |
||
1021 | // We test for both uppercase and lowercase, for servers that |
||
1022 | // are case-sensitive with regard to file names. Check for environment |
||
1023 | // first, global next |
||
1024 | if (defined('ENVIRONMENT') AND file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php')) |
||
1025 | { |
||
1026 | include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'); |
||
1027 | break; |
||
1028 | } |
||
1029 | elseif (defined('ENVIRONMENT') AND file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php')) |
||
1030 | { |
||
1031 | include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'); |
||
1032 | break; |
||
1033 | } |
||
1034 | View Code Duplication | elseif (file_exists($path.'config/'.strtolower($class).'.php')) |
|
1035 | { |
||
1036 | include($path.'config/'.strtolower($class).'.php'); |
||
1037 | break; |
||
1038 | } |
||
1039 | View Code Duplication | elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php')) |
|
1040 | { |
||
1041 | include($path.'config/'.ucfirst(strtolower($class)).'.php'); |
||
1042 | break; |
||
1043 | } |
||
1044 | } |
||
1045 | } |
||
1046 | } |
||
1047 | |||
1048 | if ($prefix == '') |
||
1049 | { |
||
1050 | if (class_exists('CI_'.$class)) |
||
1051 | { |
||
1052 | $name = 'CI_'.$class; |
||
1053 | } elseif (class_exists(config_item('subclass_prefix').$class)) |
||
1054 | { |
||
1055 | $name = config_item('subclass_prefix').$class; |
||
1056 | } else |
||
1057 | { |
||
1058 | $name = $class; |
||
1059 | } |
||
1060 | } else |
||
1061 | { |
||
1062 | $name = $prefix.$class; |
||
1063 | } |
||
1064 | |||
1065 | // Is the class name valid? |
||
1066 | if ( ! class_exists($name)) |
||
1067 | { |
||
1068 | log_message('error', "Non-existent class: ".$name); |
||
1069 | show_error("Non-existent class: ".$class); |
||
1070 | } |
||
1071 | |||
1072 | // Set the variable name we will assign the class to |
||
1073 | // Was a custom class name supplied? If so we'll use it |
||
1074 | $class = strtolower($class); |
||
1075 | |||
1076 | if (is_null($object_name)) |
||
1077 | { |
||
1078 | $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class]; |
||
1079 | } else |
||
1080 | { |
||
1081 | $classvar = $object_name; |
||
1082 | } |
||
1083 | |||
1084 | // Save the class name and object name |
||
1085 | $this->_ci_classes[$class] = $classvar; |
||
1086 | |||
1087 | // Instantiate the class |
||
1088 | $CI = & get_instance(); |
||
1089 | if ($config !== NULL) |
||
1090 | { |
||
1091 | $CI->$classvar = new $name($config); |
||
1092 | } else |
||
1093 | { |
||
1094 | $CI->$classvar = new $name; |
||
1095 | } |
||
1096 | } |
||
1097 | |||
1098 | // -------------------------------------------------------------------- |
||
1099 | |||
1100 | /** |
||
1101 | * Autoloader |
||
1102 | * |
||
1103 | * The config/autoload.php file contains an array that permits sub-systems, |
||
1104 | * libraries, and helpers to be loaded automatically. |
||
1105 | * |
||
1106 | * @param array |
||
1107 | * @return false|null |
||
1108 | */ |
||
1109 | private function _ci_autoloader() |
||
1182 | |||
1183 | // -------------------------------------------------------------------- |
||
1184 | |||
1185 | /** |
||
1186 | * Object to Array |
||
1187 | * |
||
1188 | * Takes an object as input and converts the class variables to array key/vals |
||
1189 | * |
||
1190 | * @param object |
||
1191 | * @return array |
||
1192 | */ |
||
1193 | protected function _ci_object_to_array($object) |
||
1197 | |||
1198 | // -------------------------------------------------------------------- |
||
1199 | |||
1200 | /** |
||
1201 | * Get a reference to a specific library or model |
||
1202 | * |
||
1203 | * @param string |
||
1204 | * @param string $component |
||
1205 | * @return bool |
||
1206 | */ |
||
1207 | protected function &_ci_get_component($component) |
||
1212 | |||
1213 | // -------------------------------------------------------------------- |
||
1214 | |||
1215 | /** |
||
1216 | * Prep filename |
||
1217 | * |
||
1218 | * This function preps the name of various items to make loading them more reliable. |
||
1219 | * |
||
1220 | * @param mixed |
||
1221 | * @param string |
||
1222 | * @param string $extension |
||
1223 | * @return array |
||
1224 | */ |
||
1225 | protected function _ci_prep_filename($filename, $extension) |
||
1240 | } |
||
1241 | |||
1243 | /* Location: ./system/core/Loader.php */ |
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.