├── README.md ├── core ├── MY_Loader.php └── MY_Router.php └── libraries └── MX ├── Base.php ├── Ci.php ├── Config.php ├── Controller.php ├── Lang.php ├── Loader.php ├── Modules.php └── Router.php /README.md: -------------------------------------------------------------------------------- 1 | Codeigniter-HMVC 2 | ================ 3 | 4 | Forked from https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc 5 | 6 | Modular Extensions makes the CodeIgniter PHP framework modular. Modules are groups of independent components, typically model, controller and view, arranged in an application modules sub-directory, that can be dropped into other CodeIgniter applications. 7 | 8 | HMVC stands for Hierarchical Model View Controller. 9 | 10 | Module Controllers can be used as normal Controllers or HMVC Controllers and they can be used to help you build view partials. 11 | 12 | ##Features: 13 | 14 | All controllers can contain an $autoload class variable, which holds an array of items to load prior to running the constructor. This can be used together with `module/config/autoload.php`, however using the `$autoload` variable only works for that specific controller. 15 | 16 | The `Modules::$locations` array may be set in the `application/config.php` file. ie: 17 | 18 | $config['modules_locations'] = array( 19 | APPPATH.'modules/' => '../modules/', 20 | ); 21 | 22 | `Modules::run()` output is buffered, so any data returned or output directly from the controller is caught and returned to the caller. In particular, `$this->load->view()` can be used as you would in a normal controller, without the need for return. 23 | 24 | Controllers can be loaded as class variables of other controllers using `$this->load->module('module/controller');` or simply `$this->load->module('module');` if the controller name matches the module name. 25 | 26 | Any loaded module controller can then be used like a library, ie: `$this->controller->method()`, but it has access to its own models and libraries independently from the caller. 27 | 28 | All module controllers are accessible from the URL via `module/controller/method` or simply `module/method` if the module and controller names match. 29 | 30 | If you add the `_remap()` method to your controllers you can prevent unwanted access to them from the URL and redirect or flag an error as you like. 31 | 32 | ####Notes: 33 | 34 | To use HMVC functionality, such as `Modules::run()`, controllers must extend the `MX_Controller class`. 35 | 36 | To use Modular Separation only, without HMVC, controllers will extend the CodeIgniter Controller class. 37 | 38 | You must use PHP5 style constructors in your controllers. ie: 39 | 40 | load->model('module/model');` 62 | 63 | `Modules::run()` is designed for returning view partials, and it will return buffered output (a view) from a controller. The syntax for using modules::run is a URI style segmented string and unlimited variables. 64 | 65 | /** module and controller names are different, you must include the method name also, including 'index' **/ 66 | modules::run('module/controller/method', $params, $...); 67 | 68 | /** module and controller names are the same but the method is not 'index' **/ 69 | modules::run('module/method', $params, $...); 70 | 71 | /** module and controller names are the same and the method is 'index' **/ 72 | modules::run('module', $params, $...); 73 | 74 | /** Parameters are optional, You may pass any number of parameters. **/ 75 | 76 | To call a module controller from within a controller you can use `$this->load->module()` or `Modules::load()` and PHP5 method chaining is available for any object loaded by MX. ie: `$this->load->library('validation')->run()`. 77 | 78 | To load languages for modules it is recommended to use the Loader method which will pass the active module name to the Lang instance; ie: `$this->load->language('language_file')`; 79 | 80 | The PHP5 spl_autoload feature allows you to freely extend your controllers, models and libraries `from application/core` or `application/libraries` base classes without the need to specifically include or require them. 81 | 82 | The library loader has also been updated to accommodate some CI 1.7 features: ie Library aliases are accepted in the same fashion as model aliases, and loading config files from the module config directory as library parameters (re: form_validation.php) have beed added. 83 | 84 | Returns the loaded config array to your variable: `$config = $this->load->config('config_file')` 85 | 86 | Models and libraries can also be loaded from sub-directories in their respective application directories. 87 | 88 | When using form validation with MX you will need to extend the `CI_Form_validation` class as shown below, before assigning the current controller as the `$CI` variable to the form_validation library. This will allow your callback methods to function properly. (This has been discussed on the CI forums also). ie: 89 | 90 | load->library('form_validation'); 107 | $this->form_validation->CI =& $this; 108 | } 109 | } 110 | 111 | ##View Partials 112 | 113 | Using a Module as a view partial from within a view is as easy as writing: 114 | 115 | 116 | 117 | Parameters are optional, You may pass any number of parameters. 118 | 119 | ##Modular Extensions installation 120 | 121 | 1) Start with a clean CI install 122 | 123 | 2) Set `$config[‘base_url’]` correctly for your installation 124 | 125 | 3) Access the URL `/index.php/welcome` => shows Welcome to CodeIgniter 126 | 127 | 4) Drop Modular Extensions libraries files into the CI 2.0 `application/libraries` directory 128 | 129 | 5) Drop Modular Extensions core files into `application/core`, the `MY_Controller.php` file is not required unless you wish to create your own Controller extension 130 | 131 | 6) Access the URL `/index.php/welcome` => shows Welcome to CodeIgniter 132 | 133 | 7) Create module directory structure `application/modules/welcome/controllers` 134 | 135 | 8) Move controller `application/controllers/welcome.php` to `application/modules/welcome/controllers/welcome.php` 136 | 137 | 9) Access the URL `/index.php/welcome` => shows Welcome to CodeIgniter 138 | 139 | 10) Create directory `application/modules/welcome/views` 140 | 141 | 11) Move view `application/views/welcome_message.php` to `application/modules/welcome/views/welcome_message.php` 142 | 143 | 12) Access the URL `/index.php/welcome` => shows Welcome to CodeIgniter 144 | 145 | You should now have a running Modular Extensions installation. 146 | 147 | ####Installation Guide Hints: 148 | 149 | -Steps 1-3 tell you how to get a standard CI install working - if you have a clean/tested CI install, skip to step 4. 150 | 151 | -Steps 4-5 show that normal CI still works after installing MX - it shouldn’t interfere with the normal CI setup. 152 | 153 | -Steps 6-8 show MX working alongside CI - controller moved to the “welcome” module, the view file remains in the CI `application/views` directory - MX can find module resources in several places, including the application directory. 154 | 155 | -Steps 9-11 show MX working with both controller and view in the “welcome” module - there should be no files in the `application/controllers` or `application/views` directories. -------------------------------------------------------------------------------- /core/MY_Loader.php: -------------------------------------------------------------------------------- 1 | is_loaded, TRUE)) return $this->item($file); 41 | 42 | $_module OR $_module = CI::$APP->router->fetch_module(); 43 | list($path, $file) = Modules::find($file, $_module, 'config/'); 44 | 45 | if ($path === FALSE) { 46 | parent::load($file, $use_sections, $fail_gracefully); 47 | return $this->item($file); 48 | } 49 | 50 | if ($config = Modules::load_file($file, $path, 'config')) { 51 | 52 | /* reference to the config array */ 53 | $current_config =& $this->config; 54 | 55 | if ($use_sections === TRUE) { 56 | 57 | if (isset($current_config[$file])) { 58 | $current_config[$file] = array_merge($current_config[$file], $config); 59 | } else { 60 | $current_config[$file] = $config; 61 | } 62 | 63 | } else { 64 | $current_config = array_merge($current_config, $config); 65 | } 66 | $this->is_loaded[] = $file; 67 | unset($config); 68 | return $this->item($file); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /libraries/MX/Controller.php: -------------------------------------------------------------------------------- 1 | config->item('controller_suffix'), '', get_class($this)); 46 | log_message('debug', $class." MX_Controller Initialized"); 47 | Modules::$registry[strtolower($class)] = $this; 48 | 49 | /* copy a loader instance and initialize */ 50 | $this->load = clone load_class('Loader'); 51 | $this->load->initialize($this); 52 | 53 | /* autoload module items */ 54 | $this->load->_autoloader($this->autoload); 55 | } 56 | 57 | public function __get($class) { 58 | return CI::$APP->$class; 59 | } 60 | } -------------------------------------------------------------------------------- /libraries/MX/Lang.php: -------------------------------------------------------------------------------- 1 | load($_lang); 42 | return $this->language; 43 | } 44 | 45 | $deft_lang = CI::$APP->config->item('language'); 46 | $idiom = ($lang == '') ? $deft_lang : $lang; 47 | 48 | if (in_array($langfile.'_lang'.EXT, $this->is_loaded, TRUE)) 49 | return $this->language; 50 | 51 | $_module OR $_module = CI::$APP->router->fetch_module(); 52 | list($path, $_langfile) = Modules::find($langfile.'_lang', $_module, 'language/'.$idiom.'/'); 53 | 54 | if ($path === FALSE) { 55 | 56 | if ($lang = parent::load($langfile, $lang, $return, $add_suffix, $alt_path)) return $lang; 57 | 58 | } else { 59 | 60 | if($lang = Modules::load_file($_langfile, $path, 'lang')) { 61 | if ($return) return $lang; 62 | $this->language = array_merge($this->language, $lang); 63 | $this->is_loaded[] = $langfile.'_lang'.EXT; 64 | unset($lang); 65 | } 66 | } 67 | 68 | return $this->language; 69 | } 70 | } -------------------------------------------------------------------------------- /libraries/MX/Loader.php: -------------------------------------------------------------------------------- 1 | _module = CI::$APP->router->fetch_module(); 48 | 49 | if (is_a($controller, 'MX_Controller')) { 50 | 51 | /* reference to the module controller */ 52 | $this->controller = $controller; 53 | 54 | /* references to ci loader variables */ 55 | foreach (get_class_vars('CI_Loader') as $var => $val) { 56 | if ($var != '_ci_ob_level') { 57 | $this->$var =& CI::$APP->load->$var; 58 | } 59 | } 60 | 61 | } else { 62 | parent::initialize(); 63 | 64 | /* autoload module items */ 65 | $this->_autoloader(array()); 66 | } 67 | 68 | /* add this module path to the loader variables */ 69 | $this->_add_module_paths($this->_module); 70 | } 71 | 72 | /** Add a module path loader variables **/ 73 | public function _add_module_paths($module = '') { 74 | 75 | if (empty($module)) return; 76 | 77 | foreach (Modules::$locations as $location => $offset) { 78 | 79 | /* only add a module path if it exists */ 80 | if (is_dir($module_path = $location.$module.'/') && ! in_array($module_path, $this->_ci_model_paths)) 81 | { 82 | array_unshift($this->_ci_model_paths, $module_path); 83 | } 84 | } 85 | } 86 | 87 | /** Load a module config file **/ 88 | public function config($file = 'config', $use_sections = FALSE, $fail_gracefully = FALSE) { 89 | return CI::$APP->config->load($file, $use_sections, $fail_gracefully, $this->_module); 90 | } 91 | 92 | /** Load the database drivers **/ 93 | public function database($params = '', $return = FALSE, $active_record = NULL) { 94 | 95 | if (class_exists('CI_DB', FALSE) AND $return == FALSE AND $active_record == NULL AND isset(CI::$APP->db) AND is_object(CI::$APP->db)) 96 | return; 97 | 98 | require_once BASEPATH.'database/DB'.EXT; 99 | 100 | if ($return === TRUE) return DB($params, $active_record); 101 | 102 | CI::$APP->db = DB($params, $active_record); 103 | 104 | return CI::$APP->db; 105 | } 106 | 107 | /** Load a module helper **/ 108 | public function helper($helper = array()) { 109 | 110 | if (is_array($helper)) return $this->helpers($helper); 111 | 112 | if (isset($this->_ci_helpers[$helper])) return; 113 | 114 | list($path, $_helper) = Modules::find($helper.'_helper', $this->_module, 'helpers/'); 115 | 116 | if ($path === FALSE) return parent::helper($helper); 117 | 118 | Modules::load_file($_helper, $path); 119 | $this->_ci_helpers[$_helper] = TRUE; 120 | } 121 | 122 | /** Load an array of helpers **/ 123 | public function helpers($helpers = array()) { 124 | foreach ($helpers as $_helper) $this->helper($_helper); 125 | } 126 | 127 | /** Load a module language file **/ 128 | public function language($langfile = array(), $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '') { 129 | return CI::$APP->lang->load($langfile, $idiom, $return, $add_suffix, $alt_path, $this->_module); 130 | } 131 | 132 | public function languages($languages) { 133 | foreach($languages as $_language) $this->language($_language); 134 | } 135 | 136 | /** Load a module library **/ 137 | public function library($library = '', $params = NULL, $object_name = NULL) { 138 | 139 | if (is_array($library)) return $this->libraries($library); 140 | 141 | $class = strtolower(basename($library)); 142 | 143 | if (isset($this->_ci_classes[$class]) AND $_alias = $this->_ci_classes[$class]) 144 | return CI::$APP->$_alias; 145 | 146 | ($_alias = strtolower($object_name)) OR $_alias = $class; 147 | 148 | list($path, $_library) = Modules::find($library, $this->_module, 'libraries/'); 149 | 150 | /* load library config file as params */ 151 | if ($params == NULL) { 152 | list($path2, $file) = Modules::find($_alias, $this->_module, 'config/'); 153 | ($path2) AND $params = Modules::load_file($file, $path2, 'config'); 154 | } 155 | 156 | if ($path === FALSE) { 157 | 158 | $this->_ci_load_class($library, $params, $object_name); 159 | $_alias = $this->_ci_classes[$class]; 160 | 161 | } else { 162 | 163 | Modules::load_file($_library, $path); 164 | 165 | $library = ucfirst($_library); 166 | CI::$APP->$_alias = new $library($params); 167 | 168 | $this->_ci_classes[$class] = $_alias; 169 | } 170 | 171 | return CI::$APP->$_alias; 172 | } 173 | 174 | /** Load an array of libraries **/ 175 | public function libraries($libraries) { 176 | foreach ($libraries as $_library) $this->library($_library); 177 | } 178 | 179 | /** Load a module model **/ 180 | public function model($model, $object_name = NULL, $connect = FALSE) { 181 | 182 | if (is_array($model)) return $this->models($model); 183 | 184 | ($_alias = $object_name) OR $_alias = basename($model); 185 | 186 | if (in_array($_alias, $this->_ci_models, TRUE)) 187 | return CI::$APP->$_alias; 188 | 189 | /* check module */ 190 | list($path, $_model) = Modules::find(strtolower($model), $this->_module, 'models/'); 191 | 192 | if ($path == FALSE) { 193 | 194 | /* check application & packages */ 195 | parent::model($model, $object_name, $connect); 196 | 197 | } else { 198 | 199 | class_exists('CI_Model', FALSE) OR load_class('Model', 'core'); 200 | 201 | if ($connect !== FALSE AND ! class_exists('CI_DB', FALSE)) { 202 | if ($connect === TRUE) $connect = ''; 203 | $this->database($connect, FALSE, TRUE); 204 | } 205 | 206 | Modules::load_file($_model, $path); 207 | 208 | $model = ucfirst($_model); 209 | CI::$APP->$_alias = new $model(); 210 | 211 | $this->_ci_models[] = $_alias; 212 | } 213 | 214 | return CI::$APP->$_alias; 215 | } 216 | 217 | /** Load an array of models **/ 218 | public function models($models) { 219 | foreach ($models as $_model) $this->model($_model); 220 | } 221 | 222 | /** Load a module controller **/ 223 | public function module($module, $params = NULL) { 224 | 225 | if (is_array($module)) return $this->modules($module); 226 | 227 | $_alias = strtolower(basename($module)); 228 | CI::$APP->$_alias = Modules::load(array($module => $params)); 229 | return CI::$APP->$_alias; 230 | } 231 | 232 | /** Load an array of controllers **/ 233 | public function modules($modules) { 234 | foreach ($modules as $_module) $this->module($_module); 235 | } 236 | 237 | /** Load a module plugin **/ 238 | public function plugin($plugin) { 239 | 240 | if (is_array($plugin)) return $this->plugins($plugin); 241 | 242 | if (isset($this->_ci_plugins[$plugin])) 243 | return; 244 | 245 | list($path, $_plugin) = Modules::find($plugin.'_pi', $this->_module, 'plugins/'); 246 | 247 | if ($path === FALSE AND ! is_file($_plugin = APPPATH.'plugins/'.$_plugin.EXT)) { 248 | show_error("Unable to locate the plugin file: {$_plugin}"); 249 | } 250 | 251 | Modules::load_file($_plugin, $path); 252 | $this->_ci_plugins[$plugin] = TRUE; 253 | } 254 | 255 | /** Load an array of plugins **/ 256 | public function plugins($plugins) { 257 | foreach ($plugins as $_plugin) $this->plugin($_plugin); 258 | } 259 | 260 | /** Load a module view **/ 261 | public function view($view, $vars = array(), $return = FALSE) { 262 | list($path, $_view) = Modules::find($view, $this->_module, 'views/'); 263 | 264 | if ($path != FALSE) { 265 | $this->_ci_view_paths = array($path => TRUE) + $this->_ci_view_paths; 266 | $view = $_view; 267 | } 268 | 269 | return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); 270 | } 271 | 272 | public function _ci_is_instance() {} 273 | 274 | protected function &_ci_get_component($component) { 275 | return CI::$APP->$component; 276 | } 277 | 278 | public function __get($class) { 279 | return (isset($this->controller)) ? $this->controller->$class : CI::$APP->$class; 280 | } 281 | 282 | public function _ci_load($_ci_data) { 283 | 284 | extract($_ci_data); 285 | 286 | if (isset($_ci_view)) { 287 | 288 | $_ci_path = ''; 289 | 290 | /* add file extension if not provided */ 291 | $_ci_file = (pathinfo($_ci_view, PATHINFO_EXTENSION)) ? $_ci_view : $_ci_view.EXT; 292 | 293 | foreach ($this->_ci_view_paths as $path => $cascade) { 294 | if (file_exists($view = $path.$_ci_file)) { 295 | $_ci_path = $view; 296 | break; 297 | } 298 | 299 | if ( ! $cascade) break; 300 | } 301 | 302 | } elseif (isset($_ci_path)) { 303 | 304 | $_ci_file = basename($_ci_path); 305 | if( ! file_exists($_ci_path)) $_ci_path = ''; 306 | } 307 | 308 | if (empty($_ci_path)) 309 | show_error('Unable to load the requested file: '.$_ci_file); 310 | 311 | if (isset($_ci_vars)) 312 | $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, (array) $_ci_vars); 313 | 314 | extract($this->_ci_cached_vars); 315 | 316 | ob_start(); 317 | 318 | if ((bool) @ini_get('short_open_tag') === FALSE AND CI::$APP->config->item('rewrite_short_tags') == TRUE) { 319 | echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace(' $this->_ci_ob_level + 1) { 329 | ob_end_flush(); 330 | } else { 331 | CI::$APP->output->append_output(ob_get_clean()); 332 | } 333 | } 334 | 335 | /** Autoload module items **/ 336 | public function _autoloader($autoload) { 337 | 338 | $path = FALSE; 339 | 340 | if ($this->_module) { 341 | 342 | list($path, $file) = Modules::find('constants', $this->_module, 'config/'); 343 | 344 | /* module constants file */ 345 | if ($path != FALSE) { 346 | include_once $path.$file.EXT; 347 | } 348 | 349 | list($path, $file) = Modules::find('autoload', $this->_module, 'config/'); 350 | 351 | /* module autoload file */ 352 | if ($path != FALSE) { 353 | $autoload = array_merge(Modules::load_file($file, $path, 'autoload'), $autoload); 354 | } 355 | } 356 | 357 | /* nothing to do */ 358 | if (count($autoload) == 0) return; 359 | 360 | /* autoload package paths */ 361 | if (isset($autoload['packages'])) { 362 | foreach ($autoload['packages'] as $package_path) { 363 | $this->add_package_path($package_path); 364 | } 365 | } 366 | 367 | /* autoload config */ 368 | if (isset($autoload['config'])) { 369 | foreach ($autoload['config'] as $config) { 370 | $this->config($config); 371 | } 372 | } 373 | 374 | /* autoload helpers, plugins, languages */ 375 | foreach (array('helper', 'plugin', 'language') as $type) { 376 | if (isset($autoload[$type])){ 377 | foreach ($autoload[$type] as $item) { 378 | $this->$type($item); 379 | } 380 | } 381 | } 382 | 383 | /* autoload database & libraries */ 384 | if (isset($autoload['libraries'])) { 385 | if (in_array('database', $autoload['libraries'])) { 386 | /* autoload database */ 387 | if ( ! $db = CI::$APP->config->item('database')) { 388 | $db['params'] = 'default'; 389 | $db['active_record'] = TRUE; 390 | } 391 | $this->database($db['params'], FALSE, $db['active_record']); 392 | $autoload['libraries'] = array_diff($autoload['libraries'], array('database')); 393 | } 394 | 395 | /* autoload libraries */ 396 | foreach ($autoload['libraries'] as $library) { 397 | $this->library($library); 398 | } 399 | } 400 | 401 | /* autoload models */ 402 | if (isset($autoload['model'])) { 403 | foreach ($autoload['model'] as $model => $alias) { 404 | (is_numeric($model)) ? $this->model($alias) : $this->model($model, $alias); 405 | } 406 | } 407 | 408 | /* autoload module controllers */ 409 | if (isset($autoload['modules'])) { 410 | foreach ($autoload['modules'] as $controller) { 411 | ($controller != $this->_module) AND $this->module($controller); 412 | } 413 | } 414 | } 415 | } 416 | 417 | /** load the CI class for Modular Separation **/ 418 | (class_exists('CI', FALSE)) OR require dirname(__FILE__).'/Ci.php'; -------------------------------------------------------------------------------- /libraries/MX/Modules.php: -------------------------------------------------------------------------------- 1 | item('modules_locations')) OR Modules::$locations = array( 9 | APPPATH.'modules/' => '../modules/', 10 | ); 11 | 12 | /* PHP5 spl_autoload */ 13 | spl_autoload_register('Modules::autoload'); 14 | 15 | /** 16 | * Modular Extensions - HMVC 17 | * 18 | * Adapted from the CodeIgniter Core Classes 19 | * @link http://codeigniter.com 20 | * 21 | * Description: 22 | * This library provides functions to load and instantiate controllers 23 | * and module controllers allowing use of modules and the HMVC design pattern. 24 | * 25 | * Install this file as application/libraries/MX/Modules.php 26 | * 27 | * @copyright Copyright (c) 2011 Wiredesignz 28 | * @version 5.4 29 | * 30 | * Permission is hereby granted, free of charge, to any person obtaining a copy 31 | * of this software and associated documentation files (the "Software"), to deal 32 | * in the Software without restriction, including without limitation the rights 33 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | * copies of the Software, and to permit persons to whom the Software is 35 | * furnished to do so, subject to the following conditions: 36 | * 37 | * The above copyright notice and this permission notice shall be included in 38 | * all copies or substantial portions of the Software. 39 | * 40 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 46 | * THE SOFTWARE. 47 | **/ 48 | class Modules 49 | { 50 | public static $routes, $registry, $locations; 51 | 52 | /** 53 | * Run a module controller method 54 | * Output from module is buffered and returned. 55 | **/ 56 | public static function run($module) { 57 | 58 | $method = 'index'; 59 | 60 | if(($pos = strrpos($module, '/')) != FALSE) { 61 | $method = substr($module, $pos + 1); 62 | $module = substr($module, 0, $pos); 63 | } 64 | 65 | if($class = self::load($module)) { 66 | 67 | if (method_exists($class, $method)) { 68 | ob_start(); 69 | $args = func_get_args(); 70 | $output = call_user_func_array(array($class, $method), array_slice($args, 1)); 71 | $buffer = ob_get_clean(); 72 | return ($output !== NULL) ? $output : $buffer; 73 | } 74 | } 75 | 76 | log_message('error', "Module controller failed to run: {$module}/{$method}"); 77 | } 78 | 79 | /** Load a module controller **/ 80 | public static function load($module) { 81 | 82 | (is_array($module)) ? list($module, $params) = [key($module),current($module)] : $params = NULL; 83 | 84 | /* get the requested controller class name */ 85 | $alias = strtolower(basename($module)); 86 | 87 | /* create or return an existing controller from the registry */ 88 | if ( ! isset(self::$registry[$alias])) { 89 | 90 | /* find the controller */ 91 | list($class) = CI::$APP->router->locate(explode('/', $module)); 92 | 93 | /* controller cannot be located */ 94 | if (empty($class)) return; 95 | 96 | /* set the module directory */ 97 | $path = APPPATH.'controllers/'.CI::$APP->router->fetch_directory(); 98 | 99 | /* load the controller class */ 100 | $class = $class.CI::$APP->config->item('controller_suffix'); 101 | self::load_file($class, $path); 102 | 103 | /* create and register the new controller */ 104 | $controller = ucfirst($class); 105 | self::$registry[$alias] = new $controller($params); 106 | } 107 | 108 | return self::$registry[$alias]; 109 | } 110 | 111 | /** Library base class autoload **/ 112 | public static function autoload($class) { 113 | 114 | /* don't autoload CI_ prefixed classes or those using the config subclass_prefix */ 115 | if (strstr($class, 'CI_') OR strstr($class, config_item('subclass_prefix'))) return; 116 | 117 | /* autoload Modular Extensions MX core classes */ 118 | if (strstr($class, 'MX_') AND is_file($location = dirname(__FILE__).'/'.substr($class, 3).EXT)) { 119 | include_once $location; 120 | return; 121 | } 122 | 123 | /* autoload core classes */ 124 | if(is_file($location = APPPATH.'core/'.$class.EXT)) { 125 | include_once $location; 126 | return; 127 | } 128 | 129 | /* autoload library classes */ 130 | if(is_file($location = APPPATH.'libraries/'.$class.EXT)) { 131 | include_once $location; 132 | return; 133 | } 134 | } 135 | 136 | /** Load a module file **/ 137 | public static function load_file($file, $path, $type = 'other', $result = TRUE) { 138 | 139 | $file = str_replace(EXT, '', $file); 140 | $location = $path.$file.EXT; 141 | 142 | if ($type === 'other') { 143 | if (class_exists($file, FALSE)) { 144 | log_message('debug', "File already loaded: {$location}"); 145 | return $result; 146 | } 147 | include_once $location; 148 | } else { 149 | 150 | /* load config or language array */ 151 | include $location; 152 | 153 | if ( ! isset($$type) OR ! is_array($$type)) 154 | show_error("{$location} does not contain a valid {$type} array"); 155 | 156 | $result = $$type; 157 | } 158 | log_message('debug', "File loaded: {$location}"); 159 | return $result; 160 | } 161 | 162 | /** 163 | * Find a file 164 | * Scans for files located within modules directories. 165 | * Also scans application directories for models, plugins and views. 166 | * Generates fatal error if file not found. 167 | **/ 168 | public static function find($file, $module, $base) { 169 | 170 | $segments = explode('/', $file); 171 | 172 | $file = array_pop($segments); 173 | $file_ext = (pathinfo($file, PATHINFO_EXTENSION)) ? $file : $file.EXT; 174 | 175 | $path = ltrim(implode('/', $segments).'/', '/'); 176 | $module ? $modules[$module] = $path : $modules = array(); 177 | 178 | if ( ! empty($segments)) { 179 | $modules[array_shift($segments)] = ltrim(implode('/', $segments).'/','/'); 180 | } 181 | 182 | foreach (Modules::$locations as $location => $offset) { 183 | foreach($modules as $module => $subpath) { 184 | $fullpath = $location.$module.'/'.$base.$subpath; 185 | 186 | if ($base == 'libraries/' AND is_file($fullpath.ucfirst($file_ext))) 187 | return array($fullpath, ucfirst($file)); 188 | 189 | if (is_file($fullpath.$file_ext)) return array($fullpath, $file); 190 | } 191 | } 192 | 193 | return array(FALSE, $file); 194 | } 195 | 196 | /** Parse module routes **/ 197 | public static function parse_routes($module, $uri) { 198 | 199 | /* load the route file */ 200 | if ( ! isset(self::$routes[$module])) { 201 | if (list($path) = self::find('routes', $module, 'config/') AND $path) 202 | self::$routes[$module] = self::load_file('routes', $path, 'route'); 203 | } 204 | 205 | if ( ! isset(self::$routes[$module])) return; 206 | 207 | /* parse module routes */ 208 | foreach (self::$routes[$module] as $key => $val) { 209 | 210 | $key = str_replace(array(':any', ':num'), array('.+', '[0-9]+'), $key); 211 | 212 | if (preg_match('#^'.$key.'$#', $uri)) { 213 | if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) { 214 | $val = preg_replace('#^'.$key.'$#', $val, $uri); 215 | } 216 | 217 | return explode('/', $module.'/'.$val); 218 | } 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /libraries/MX/Router.php: -------------------------------------------------------------------------------- 1 | module; 44 | } 45 | 46 | public function _validate_request($segments) { 47 | 48 | if (count($segments) == 0) return $segments; 49 | 50 | /* locate module controller */ 51 | if ($located = $this->locate($segments)) return $located; 52 | 53 | /* use a default 404_override controller */ 54 | if (isset($this->routes['404_override']) AND $this->routes['404_override']) { 55 | $segments = explode('/', $this->routes['404_override']); 56 | if ($located = $this->locate($segments)) return $located; 57 | } 58 | 59 | /* no controller found */ 60 | show_404(); 61 | } 62 | 63 | /** Locate the controller **/ 64 | public function locate($segments) { 65 | 66 | $this->module = ''; 67 | $this->directory = ''; 68 | $ext = $this->config->item('controller_suffix').EXT; 69 | 70 | /* use module route if available */ 71 | if (isset($segments[0]) AND $routes = Modules::parse_routes($segments[0], implode('/', $segments))) { 72 | $segments = $routes; 73 | } 74 | 75 | /* get the segments array elements */ 76 | list($module, $directory, $controller) = array_pad($segments, 3, NULL); 77 | 78 | /* check modules */ 79 | foreach (Modules::$locations as $location => $offset) { 80 | 81 | /* module exists? */ 82 | if (is_dir($source = $location.$module.'/controllers/')) { 83 | 84 | $this->module = $module; 85 | $this->directory = $offset.$module.'/controllers/'; 86 | 87 | /* module sub-controller exists? */ 88 | if($directory AND is_file($source.$directory.$ext)) { 89 | return array_slice($segments, 1); 90 | } 91 | 92 | /* module sub-directory exists? */ 93 | if($directory AND is_dir($source.$directory.'/')) { 94 | 95 | $source = $source.$directory.'/'; 96 | $this->directory .= $directory.'/'; 97 | 98 | /* module sub-directory controller exists? */ 99 | if(is_file($source.$directory.$ext)) { 100 | return array_slice($segments, 1); 101 | } 102 | 103 | /* module sub-directory sub-controller exists? */ 104 | if($controller AND is_file($source.$controller.$ext)) { 105 | return array_slice($segments, 2); 106 | } 107 | } 108 | 109 | /* module controller exists? */ 110 | if(is_file($source.$module.$ext)) { 111 | return $segments; 112 | } 113 | } 114 | } 115 | 116 | /* application controller exists? */ 117 | if (is_file(APPPATH.'controllers/'.$module.$ext)) { 118 | return $segments; 119 | } 120 | 121 | /* application sub-directory controller exists? */ 122 | if($directory AND is_file(APPPATH.'controllers/'.$module.'/'.$directory.$ext)) { 123 | $this->directory = $module.'/'; 124 | return array_slice($segments, 1); 125 | } 126 | 127 | /* application sub-directory default controller exists? */ 128 | if (is_file(APPPATH.'controllers/'.$module.'/'.$this->default_controller.$ext)) { 129 | $this->directory = $module.'/'; 130 | return array($this->default_controller); 131 | } 132 | } 133 | 134 | public function set_class($class) { 135 | $this->class = $class.$this->config->item('controller_suffix'); 136 | } 137 | } --------------------------------------------------------------------------------