├── .hg_archival.txt ├── core ├── MY_Loader.php ├── MY_Router.php └── MY_URI.php ├── readme.md └── third_party └── MX ├── Base.php ├── Ci.php ├── Config.php ├── Controller.php ├── Lang.php ├── Loader.php ├── Modules.php ├── Router.php └── URI.php /.hg_archival.txt: -------------------------------------------------------------------------------- 1 | repo: d88ec269c6ab8f5603f17fde8fcd7ff65e6d93e0 2 | node: 868e97533562e910d8263af22750985d57004baa 3 | branch: default 4 | latesttag: null 5 | latesttagdistance: 147 6 | -------------------------------------------------------------------------------- /core/MY_Loader.php: -------------------------------------------------------------------------------- 1 | array('url', 'form'), 132 | 'libraries' => array('email'), 133 | ); 134 | } 135 | 136 | The Modules::$locations array may be set in the application/config.php file. ie: 137 | 138 | :::php 139 | '../modules/', 142 | ); 143 | 144 | Modules::run() output is buffered, so any data returned or output directly from the controller is caught and 145 | returned to the caller. In particular, $this->load->view() can be used as you would in a normal controller, without the need for return. 146 | 147 | Controllers can be loaded as class variables of other controllers using $this->load->module('module/controller'); 148 | or simply $this->load->module('module'); if the controller name matches the module name. 149 | 150 | 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. 151 | 152 | All module controllers are accessible from the URL via module/controller/method or simply module/method if the module and controller names match. 153 | 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. 154 | 155 | ### Notes: 156 | 157 | To use HMVC functionality, such as Modules::run(), controllers must extend the MX_Controller class. 158 | 159 | To use Modular Separation only, without HMVC, controllers will extend the CodeIgniter Controller class. 160 | 161 | You must use PHP5 style constructors in your controllers. ie: 162 | 163 | :::php 164 | load->model('module/model'); 188 | 189 | 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. 190 | 191 | :::php 192 | load->module() or Modules::load() 205 | and PHP5 method chaining is available for any object loaded by MX. 206 | ie: $this->load->library(‘validation’)->run(). 207 | 208 | To load languages for modules it is recommended to use the Loader method which will pass the active module 209 | name to the Lang instance; ie: $this->load->language('language_file'); 210 | 211 | The PHP5 spl_autoload feature allows you to freely extend your controllers, models and libraries from 212 | application/core or application/libraries base classes without the need to specifically include or require 213 | them. 214 | 215 | The library loader has also been updated to accommodate some CI 1.7 features: ie Library aliases are 216 | accepted in the same fashion as model aliases, and loading config files from the module config directory 217 | as library parameters (re: form_validation.php) have beed added. 218 | 219 | $config = $this->load->config(‘config_file’), Returns the loaded config array to your variable. 220 | 221 | Models and libraries can also be loaded from sub-directories in their respective application directories. 222 | 223 | When using form validation with MX you will need to extend the CI_Form_validation class as shown below, 224 | 225 | :::php 226 | load->library('form_validation'); 245 | $this->form_validation->CI =& $this; 246 | } 247 | } 248 | 249 | ### View Partials 250 | 251 | Using a Module as a view partial from within a view is as easy as writing: 252 | 253 | :::php 254 | 255 | 256 | Parameters are optional, You may pass any number of parameters. 257 | 258 | ### Modular Extensions installation 259 | 260 | 1. Start with a clean CI install 261 | 2. Set $config[‘base_url’] correctly for your installation 262 | 3. Access the URL /index.php/welcome => shows Welcome to CodeIgniter 263 | 4. Drop Modular Extensions third_party files into the CI 2.0 application/third_party directory 264 | 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 265 | 6. Access the URL /index.php/welcome => shows Welcome to CodeIgniter 266 | 7. Create module directory structure application/modules/welcome/controllers 267 | 8. Move controller application/controllers/welcome.php to application/modules/welcome/controllers/welcome.php 268 | 9. Access the URL /index.php/welcome => shows Welcome to CodeIgniter 269 | 10. Create directory application/modules/welcome/views 270 | 11. Move view application/views/welcome_message.php to application/modules/welcome/views/welcome_message.php 271 | 12. Access the URL /index.php/welcome => shows Welcome to CodeIgniter 272 | 273 | You should now have a running Modular Extensions installation. 274 | 275 | ### Installation Guide Hints: 276 | -Steps 1-3 tell you how to get a standard CI install working - if you have a clean/tested CI install, skip 277 | to step 4. 278 | 279 | -Steps 4-5 show that normal CI still works after installing MX - it shouldn’t interfere with the normal CI 280 | setup. 281 | 282 | -Steps 6-8 show MX working alongside CI - controller moved to the “welcome” module, the view file remains 283 | in the CI application/views directory - MX can find module resources in several places, including the application directory. 284 | 285 | -Steps 9-11 show MX working with both controller and view in the “welcome” module - there should be no 286 | files in the application/controllers or application/views directories. 287 | 288 | 289 | ### FAQ 290 | 291 | Q. What are modules, why should I use them? 292 | 293 | A. (http://en.wikipedia.org/wiki/Module) 294 | 295 | (http://en.wikipedia.org/wiki/Modular_programming) 296 | 297 | (http://blog.fedecarg.com/2008/06/28/a-modular-approach-to-web-development) 298 | 299 | Q. What is Modular HMVC, why should I use it? 300 | 301 | A. Modular HMVC = Multiple MVC triads 302 | 303 | This is most useful when you need to load a view and its data within a view. Think about adding a shopping 304 | cart to a page. The shopping cart needs its own controller which may call a model to get cart data. 305 | Then the controller needs to load the data into a view. So instead of the main controller handling the 306 | page and the shopping cart, the shopping cart MVC can be loaded directly in the page. 307 | The main controller doesn’t need to know about it, and is totally isolated from it. 308 | 309 | In CI we can’t call more than 1 controller per request. Therefore, to achieve HMVC, we have to simulate 310 | controllers. It can be done with libraries, or with this “Modular Extensions HMVC” contribution. 311 | 312 | The differences between using a library and a “Modular HMVC” HMVC class is: 313 | 1. No need to get and use the CI instance within an HMVC class 314 | 2. HMVC classes are stored in a modules directory as opposed to the libraries directory. 315 | 316 | Q. Is Modular Extensions HMVC the same as Modular Separation? 317 | 318 | A. Yes and No. Like Modular Separation, Modular Extensions makes modules “portable” to other installations. For example, if you make a nice self-contained model-controller-view set of files you can bring that MVC into another project by copying just one folder - everything is in one place instead of spread around model, view and controller folders. 319 | 320 | Modular HMVC means modular MVC triads. Modular Separation and Modular Extensions allows related 321 | controllers, models, libraries, views, etc. to be grouped together in module directories and used 322 | like a mini application. But, Modular Extensions goes one step further and allows those modules to 323 | “talk” to each other. You can get controller output without having to go out through the http interface 324 | again. 325 | -------------------------------------------------------------------------------- /third_party/MX/Base.php: -------------------------------------------------------------------------------- 1 | is_loaded, TRUE)) return $this->item($file); 48 | 49 | $_module OR $_module = CI::$APP->router->fetch_module(); 50 | $_module_location = CI::$APP->router->fetch_location(); 51 | 52 | list($path, $file) = Modules::find($file, $_module, 'config/'); 53 | 54 | if ($path === FALSE) { 55 | parent::load($file, $use_sections, $fail_gracefully); 56 | return $this->item($file); 57 | } 58 | 59 | if (defined('ENVIRONMENT') AND file_exists($path.ENVIRONMENT.'/'.$file.'.php')) { 60 | $path = $path.ENVIRONMENT.'/'; 61 | } 62 | 63 | if ($config = Modules::load_file($file, $path, 'config')) { 64 | 65 | /* reference to the config array */ 66 | $current_config =& $this->config; 67 | 68 | if ($use_sections === TRUE) { 69 | 70 | if (isset($current_config[$file])) { 71 | $current_config[$file] = array_merge($current_config[$file], $config); 72 | } else { 73 | $current_config[$file] = $config; 74 | } 75 | 76 | } else { 77 | $current_config = array_merge($current_config, $config); 78 | } 79 | $this->is_loaded[] = $file; 80 | unset($config); 81 | return $this->item($file); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /third_party/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 | } -------------------------------------------------------------------------------- /third_party/MX/Lang.php: -------------------------------------------------------------------------------- 1 | load($_lang); 49 | return $this->language; 50 | } 51 | 52 | $deft_lang = CI::$APP->config->item('language'); 53 | $idiom = ($lang == '') ? $deft_lang : $lang; 54 | 55 | if (in_array($langfile.'_lang'.EXT, $this->is_loaded, TRUE)) 56 | return $this->language; 57 | 58 | $_module OR $_module = CI::$APP->router->fetch_module(); 59 | $_module_location = CI::$APP->router->fetch_location(); 60 | list($path, $_langfile) = Modules::find($langfile.'_lang', $_module, 'language/'.$idiom.'/', $_module_location); 61 | 62 | if ($path === FALSE) { 63 | 64 | if ($lang = parent::load($langfile, $lang, $return, $add_suffix, $alt_path)) return $lang; 65 | 66 | } else { 67 | 68 | if($lang = Modules::load_file($_langfile, $path, 'lang')) { 69 | if ($return) return $lang; 70 | $this->language = array_merge($this->language, $lang); 71 | $this->is_loaded[] = $langfile.'_lang'.EXT; 72 | unset($lang); 73 | } 74 | } 75 | 76 | return $this->language; 77 | } 78 | } -------------------------------------------------------------------------------- /third_party/MX/Loader.php: -------------------------------------------------------------------------------- 1 | _module = CI::$APP->router->fetch_module(); 65 | 66 | if (is_a($controller, 'MX_Controller')) { 67 | 68 | /* set the module location */ 69 | $this->_module_location = CI::$APP->router->fetch_location(); 70 | 71 | /* reference to the module controller */ 72 | $this->controller = $controller; 73 | 74 | /* references to ci loader variables */ 75 | foreach (get_class_vars('CI_Loader') as $var => $val) { 76 | if ($var != '_ci_ob_level') { 77 | $this->$var =& CI::$APP->load->$var; 78 | } 79 | } 80 | 81 | } else { 82 | parent::initialize(); 83 | 84 | /* autoload module items */ 85 | $this->_autoloader(array()); 86 | } 87 | 88 | /* add this module path to the loader variables */ 89 | $this->_add_module_paths($this->_module, $this->_module_location); 90 | } 91 | 92 | /** Add a module path loader variables **/ 93 | public function _add_module_paths($module = '', $location = FALSE) { 94 | 95 | if (empty($module)) return; 96 | 97 | /* only add a module path if it exists */ 98 | if (is_dir($module_path = $location.$module.'/') && ! in_array($module_path, $this->_ci_model_paths)) 99 | { 100 | array_unshift($this->_ci_model_paths, $module_path); 101 | } 102 | } 103 | 104 | /** Load a module config file **/ 105 | public function config($file = 'config', $use_sections = FALSE, $fail_gracefully = FALSE) { 106 | return CI::$APP->config->load($file, $use_sections, $fail_gracefully, $this->_module); 107 | } 108 | 109 | /** Load the database drivers **/ 110 | public function database($params = '', $return = FALSE, $active_record = NULL) { 111 | 112 | if (class_exists('CI_DB', FALSE) AND $return == FALSE AND $active_record == NULL AND isset(CI::$APP->db) AND is_object(CI::$APP->db)) 113 | return; 114 | 115 | require_once BASEPATH.'database/DB'.EXT; 116 | 117 | if ($return === TRUE) return DB($params, $active_record); 118 | 119 | CI::$APP->db = DB($params, $active_record); 120 | 121 | return CI::$APP->db; 122 | } 123 | 124 | /** Load a module helper **/ 125 | public function helper($helper = array()) { 126 | 127 | if (is_array($helper)) return $this->helpers($helper); 128 | 129 | if (isset($this->_ci_helpers[$helper])) return; 130 | 131 | list($path, $_helper, $ext_helper) = Modules::find($helper.'_helper', $this->_module, 'helpers/', $this->_module_location); 132 | 133 | // Are we simply loading an extended helper? 134 | if($ext_helper) { 135 | Modules::load_file(config_item('subclass_prefix').$_helper, $path); 136 | 137 | // Force the helper we are extending to load as well 138 | $segments = explode('/', $helper); 139 | $helper = isset($segments[1]) ? $segments[1] : $helper; 140 | $path = FALSE; 141 | } 142 | 143 | if ($path === FALSE) return parent::helper($helper); 144 | 145 | Modules::load_file($_helper, $path); 146 | $this->_ci_helpers[$_helper] = TRUE; 147 | } 148 | 149 | /** Load an array of helpers **/ 150 | public function helpers($helpers = array()) { 151 | foreach ($helpers as $_helper) $this->helper($_helper); 152 | } 153 | 154 | /** Load a module language file **/ 155 | public function language($langfile = array(), $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '') { 156 | return CI::$APP->lang->load($langfile, $idiom, $return, $add_suffix, $alt_path, $this->_module); 157 | } 158 | 159 | public function languages($languages) { 160 | foreach($languages as $_language) $this->language($_language); 161 | } 162 | 163 | /** Load a module library **/ 164 | public function library($library = '', $params = NULL, $object_name = NULL) { 165 | 166 | if (is_array($library)) return $this->libraries($library); 167 | 168 | $class = strtolower(basename($library)); 169 | 170 | if (isset($this->_ci_classes[$class]) AND $_alias = $this->_ci_classes[$class]) { 171 | log_message('debug', "Skipping loading $class library as it has already loaded or another alias already exists with same name that conflicts"); 172 | return CI::$APP->$_alias; 173 | } 174 | 175 | // Below for benchmark tracking 176 | if($this->module_benchmarks) { 177 | $BM =& load_class('Benchmark', 'core'); 178 | self::$loaded_benchmarks++; 179 | $benchmark = 'library ' . self::$loaded_benchmarks . ':_( ' . str_replace('/', ' / ', $library) . ' )'; 180 | $BM->mark($benchmark . '_start'); 181 | } 182 | 183 | ($_alias = strtolower($object_name)) OR $_alias = $class; 184 | 185 | list($path, $_library) = Modules::find($library, '', 'libraries/'); 186 | 187 | /* load library config file as params */ 188 | if ($params == NULL) { 189 | list($path2, $file) = Modules::find($_alias, '', 'config/'); 190 | 191 | if (defined('ENVIRONMENT') AND file_exists($path2.ENVIRONMENT.'/'.$file.'.php')) { 192 | $path2 = $path2.ENVIRONMENT.'/'; 193 | } 194 | 195 | ($path2) AND $params = Modules::load_file($file, $path2, 'config'); 196 | } 197 | 198 | if ($path === FALSE) { 199 | 200 | $this->_ci_load_class($library, $params, $object_name); 201 | $_alias = $this->_ci_classes[$class]; 202 | 203 | } else { 204 | 205 | Modules::load_file($_library, $path); 206 | 207 | $library = ucfirst($_library); 208 | 209 | //Make sure to also check if a module's library should be overriding a CI library 210 | if (class_exists('CI_'.$library)) 211 | { 212 | $library = 'CI_'.$library; 213 | } 214 | elseif (class_exists(config_item('subclass_prefix').$library)) 215 | { 216 | $library = config_item('subclass_prefix').$library; 217 | } 218 | 219 | //Small but important change. Moved this above loading the new library below 220 | //as in some isolated situations an infinite loop can happen if a library 221 | //is using a duplicate alias name as a controller, and that controller or 222 | //parent controller has tried to load the library again which mistakenly calls 223 | //the clashed controller which calls what thinks is the library again over and 224 | //over (but really it is calling itself). Can be confusing, and the 225 | //preferred method would be to fail instantiating a new class or overwriting 226 | //the other alias that exists instead of going into an infinite loop. 227 | $this->_ci_classes[$class] = $_alias; 228 | 229 | CI::$APP->$_alias = new $library($params); 230 | 231 | 232 | } 233 | 234 | // Below for benchmark tracking 235 | if($this->module_benchmarks) { 236 | $BM->mark($benchmark . '_end'); 237 | } 238 | 239 | return CI::$APP->$_alias; 240 | } 241 | 242 | /** Load an array of libraries **/ 243 | public function libraries($libraries) { 244 | foreach ($libraries as $_library) $this->library($_library); 245 | } 246 | 247 | /** Load a module model **/ 248 | public function model($model, $object_name = NULL, $connect = FALSE) { 249 | 250 | if (is_array($model)) return $this->models($model); 251 | 252 | ($_alias = $object_name) OR $_alias = basename($model); 253 | 254 | if (in_array($_alias, $this->_ci_models, TRUE)) 255 | return CI::$APP->$_alias; 256 | 257 | /* check module */ 258 | list($path, $_model) = Modules::find(strtolower($model), $this->_module, 'models/', $this->_module_location); 259 | 260 | if ($path == FALSE) { 261 | 262 | /* check application & packages */ 263 | parent::model($model, $object_name, $connect); 264 | 265 | } else { 266 | 267 | class_exists('CI_Model', FALSE) OR load_class('Model', 'core'); 268 | 269 | if ($connect !== FALSE AND ! class_exists('CI_DB', FALSE)) { 270 | if ($connect === TRUE) $connect = ''; 271 | $this->database($connect, FALSE, TRUE); 272 | } 273 | 274 | Modules::load_file($_model, $path); 275 | 276 | $model = ucfirst($_model); 277 | CI::$APP->$_alias = new $model(); 278 | 279 | $this->_ci_models[] = $_alias; 280 | } 281 | 282 | return CI::$APP->$_alias; 283 | } 284 | 285 | /** Load an array of models **/ 286 | public function models($models) { 287 | foreach ($models as $_model) $this->model($_model); 288 | } 289 | 290 | /** Load a module controller **/ 291 | public function module($module, $params = NULL) { 292 | 293 | if (is_array($module)) return $this->modules($module); 294 | 295 | $_alias = strtolower(basename($module)); 296 | CI::$APP->$_alias = Modules::load(array($module => $params)); 297 | return CI::$APP->$_alias; 298 | } 299 | 300 | /** Load an array of controllers **/ 301 | public function modules($modules) { 302 | foreach ($modules as $_module) $this->module($_module); 303 | } 304 | 305 | /** Load a module plugin **/ 306 | public function plugin($plugin) { 307 | 308 | if (is_array($plugin)) return $this->plugins($plugin); 309 | 310 | if (isset($this->_ci_plugins[$plugin])) 311 | return; 312 | 313 | list($path, $_plugin) = Modules::find($plugin.'_pi', $this->_module, 'plugins/', $this->_module_location); 314 | 315 | if ($path === FALSE AND ! is_file($_plugin = APPPATH.'plugins/'.$_plugin.EXT)) { 316 | show_error("Unable to locate the plugin file: {$_plugin}"); 317 | } 318 | 319 | Modules::load_file($_plugin, $path); 320 | $this->_ci_plugins[$plugin] = TRUE; 321 | } 322 | 323 | /** Load an array of plugins **/ 324 | public function plugins($plugins) { 325 | foreach ($plugins as $_plugin) $this->plugin($_plugin); 326 | } 327 | 328 | /** Load a module view **/ 329 | public function view($view, $vars = array(), $return = FALSE) { 330 | // If a view begins with an underscore, it should be looked everywhere 331 | list($path, $_view) = Modules::find($view, (!isset($view[0]) || $view[0] != '_') ? $this->_module : '', 'views/', (!isset($view[0]) || $view[0] != '_') ? $this->_module_location : ''); 332 | 333 | if ($path != FALSE) { 334 | $this->_ci_view_paths = array($path => TRUE) + $this->_ci_view_paths; 335 | $view = $_view; 336 | } 337 | 338 | return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); 339 | } 340 | 341 | public function _ci_is_instance() {} 342 | 343 | protected function &_ci_get_component($component) { 344 | return CI::$APP->$component; 345 | } 346 | 347 | public function __get($class) { 348 | return (isset($this->controller)) ? $this->controller->$class : CI::$APP->$class; 349 | } 350 | 351 | public function _ci_load($_ci_data) { 352 | 353 | extract($_ci_data); 354 | 355 | if (isset($_ci_view)) { 356 | 357 | $_ci_path = ''; 358 | 359 | /* add file extension if not provided */ 360 | $_ci_file = (pathinfo($_ci_view, PATHINFO_EXTENSION)) ? $_ci_view : $_ci_view.EXT; 361 | 362 | foreach ($this->_ci_view_paths as $path => $cascade) { 363 | if (file_exists($view = $path.$_ci_file)) { 364 | $_ci_path = $view; 365 | break; 366 | } 367 | 368 | if ( ! $cascade) break; 369 | } 370 | 371 | } elseif (isset($_ci_path)) { 372 | 373 | $_ci_file = basename($_ci_path); 374 | if( ! file_exists($_ci_path)) $_ci_path = ''; 375 | } 376 | 377 | if (empty($_ci_path)) 378 | show_error('Unable to load the requested file: '.$_ci_file); 379 | 380 | if (isset($_ci_vars)) 381 | $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, (array) $_ci_vars); 382 | 383 | extract($this->_ci_cached_vars); 384 | 385 | ob_start(); 386 | 387 | if ((bool) @ini_get('short_open_tag') === FALSE AND CI::$APP->config->item('rewrite_short_tags') == TRUE) { 388 | echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace(' $this->_ci_ob_level + 1) { 398 | ob_end_flush(); 399 | } else { 400 | CI::$APP->output->append_output(ob_get_clean()); 401 | } 402 | } 403 | 404 | /** Autoload module items **/ 405 | public function _autoloader($autoload) { 406 | 407 | $path = FALSE; 408 | 409 | if ($this->_module) { 410 | 411 | list($path, $file) = Modules::find('constants', $this->_module, 'config/', $this->_module_location); 412 | 413 | /* module constants file */ 414 | if ($path != FALSE) { 415 | include_once $path.$file.EXT; 416 | } 417 | 418 | list($path, $file) = Modules::find('autoload', $this->_module, 'config/', $this->_module_location); 419 | 420 | /* module autoload file */ 421 | if ($path != FALSE) { 422 | $autoload = array_merge(Modules::load_file($file, $path, 'autoload'), $autoload); 423 | } 424 | } 425 | 426 | /* nothing to do */ 427 | if (count($autoload) == 0) return; 428 | 429 | /* autoload package paths */ 430 | if (isset($autoload['packages'])) { 431 | foreach ($autoload['packages'] as $package_path) { 432 | $this->add_package_path($package_path); 433 | } 434 | } 435 | 436 | /* autoload config */ 437 | if (isset($autoload['config'])) { 438 | foreach ($autoload['config'] as $config) { 439 | $this->config($config); 440 | } 441 | } 442 | 443 | /* autoload helpers, plugins, languages */ 444 | foreach (array('helper', 'plugin', 'language') as $type) { 445 | if (isset($autoload[$type])){ 446 | foreach ($autoload[$type] as $item) { 447 | $this->$type($item); 448 | } 449 | } 450 | } 451 | 452 | /* autoload database & libraries */ 453 | if (isset($autoload['libraries'])) { 454 | if (in_array('database', $autoload['libraries'])) { 455 | /* autoload database */ 456 | if ( ! $db = CI::$APP->config->item('database')) { 457 | $db['params'] = 'default'; 458 | $db['active_record'] = TRUE; 459 | } 460 | $this->database($db['params'], FALSE, $db['active_record']); 461 | $autoload['libraries'] = array_diff($autoload['libraries'], array('database')); 462 | } 463 | 464 | /* autoload libraries */ 465 | foreach ($autoload['libraries'] as $library) { 466 | $this->library($library); 467 | } 468 | } 469 | 470 | /* autoload models */ 471 | if (isset($autoload['model'])) { 472 | foreach ($autoload['model'] as $model => $alias) { 473 | (is_numeric($model)) ? $this->model($alias) : $this->model($model, $alias); 474 | } 475 | } 476 | 477 | /* autoload module controllers */ 478 | if (isset($autoload['modules'])) { 479 | foreach ($autoload['modules'] as $controller) { 480 | ($controller != $this->_module) AND $this->module($controller); 481 | } 482 | } 483 | } 484 | } 485 | 486 | /** load the CI class for Modular Separation **/ 487 | (class_exists('CI', FALSE)) OR require dirname(__FILE__).'/Ci.php'; -------------------------------------------------------------------------------- /third_party/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/third_party/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 | * This is a forked version of the original Modular Extensions - HMVC library to 49 | * support better module routing, and speed optimizations. These additional 50 | * changes were made by: 51 | * 52 | * @author Brian Wozeniak 53 | * @copyright Copyright (c) 1998-2013, Unmelted, LLC 54 | **/ 55 | class Modules 56 | { 57 | public static $routes, $registry, $locations; 58 | 59 | /** 60 | * Run a module controller method 61 | * Output from module is buffered and returned. 62 | **/ 63 | public static function run($module) { 64 | 65 | $method = 'index'; 66 | 67 | if(($pos = strrpos($module, '/')) != FALSE) { 68 | $method = substr($module, $pos + 1); 69 | $module = substr($module, 0, $pos); 70 | } 71 | 72 | if($class = self::load($module)) { 73 | 74 | if (method_exists($class, $method)) { 75 | ob_start(); 76 | $args = func_get_args(); 77 | $output = call_user_func_array(array($class, $method), array_slice($args, 1)); 78 | $buffer = ob_get_clean(); 79 | return ($output != NULL) ? $output : $buffer; 80 | } 81 | } 82 | 83 | log_message('error', "Module controller failed to run: {$module}/{$method}"); 84 | } 85 | 86 | /** Load a module controller **/ 87 | public static function load($module) { 88 | 89 | (is_array($module)) ? list($module, $params) = each($module) : $params = NULL; 90 | 91 | /* get the requested controller class name */ 92 | $alias = strtolower(basename($module)); 93 | 94 | /* create or return an existing controller from the registry */ 95 | if ( ! isset(self::$registry[$alias])) { 96 | 97 | /* find the controller */ 98 | list($class) = CI::$APP->router->locate(explode('/', $module)); 99 | 100 | /* controller cannot be located */ 101 | if (empty($class)) return; 102 | 103 | /* set the module directory */ 104 | $path = APPPATH.'controllers/'.CI::$APP->router->fetch_directory(); 105 | 106 | /* load the controller class */ 107 | $class = $class.CI::$APP->config->item('controller_suffix'); 108 | self::load_file($class, $path); 109 | 110 | /* create and register the new controller */ 111 | $controller = ucfirst($class); 112 | self::$registry[$alias] = new $controller($params); 113 | } 114 | 115 | return self::$registry[$alias]; 116 | } 117 | 118 | /** Library base class autoload **/ 119 | public static function autoload($class) { 120 | 121 | /* don't autoload CI_ prefixed classes or those using the config subclass_prefix */ 122 | if (strstr($class, 'CI_') OR strstr($class, config_item('subclass_prefix'))) return; 123 | 124 | /* autoload Modular Extensions MX core classes */ 125 | if (strstr($class, 'MX_') AND is_file($location = dirname(__FILE__).'/'.substr($class, 3).EXT)) { 126 | include_once $location; 127 | return; 128 | } 129 | 130 | /* autoload core classes */ 131 | if(is_file($location = APPPATH.'core/'.$class.EXT)) { 132 | include_once $location; 133 | return; 134 | } 135 | 136 | /* autoload library classes */ 137 | if(is_file($location = APPPATH.'libraries/'.$class.EXT)) { 138 | include_once $location; 139 | return; 140 | } 141 | } 142 | 143 | /** Load a module file **/ 144 | public static function load_file($file, $path, $type = 'other', $result = TRUE) { 145 | 146 | $file = str_replace(EXT, '', $file); 147 | $location = $path.$file.EXT; 148 | 149 | if ($type === 'other') { 150 | if (class_exists($file, FALSE)) { 151 | log_message('debug', "**File already loaded: {$location}"); 152 | return $result; 153 | } 154 | include_once $location; 155 | } else { 156 | 157 | /* load config or language array */ 158 | include $location; 159 | 160 | if ( ! isset($$type) OR ! is_array($$type)) 161 | show_error("{$location} does not contain a valid {$type} array"); 162 | 163 | $result = $$type; 164 | } 165 | log_message('debug', "File loaded: {$location}"); 166 | return $result; 167 | } 168 | 169 | /** 170 | * Find a file 171 | * Scans for files located within modules directories. 172 | * Also scans application directories for models, plugins and views. 173 | * Returns the first result found, not all results 174 | * 175 | * @param string $file 176 | * @param string $module 177 | * @param string $base 178 | * @param string $location 179 | * @return array Returns the location, filename, and TRUE if it is an extension to a helper 180 | */ 181 | public static function find($file, $module, $base, $location = '') { 182 | 183 | $segments = explode('/', $file); 184 | 185 | $file = array_pop($segments); 186 | $file_ext = (pathinfo($file, PATHINFO_EXTENSION)) ? $file : $file.EXT; 187 | 188 | $path = ltrim(implode('/', $segments).'/', '/'); 189 | $modules = array(); 190 | 191 | // If we have multiple segments, we don't need location as user may want location outside that area 192 | if($path) { 193 | $location = ''; 194 | } 195 | 196 | // We want to execute the below first under the assumption that the $file contains 197 | // the module as well in the segment, and if so is assumed to be most likely the 198 | // correct location, and this limits how much searching we do as if found it 199 | // immediately returns that result instead of searching through both 200 | if ( ! empty($segments)) { 201 | $modules[array_shift($segments)] = ltrim(implode('/', $segments).'/','/'); 202 | } 203 | 204 | // Is this file being overridden in an application directory? If so use that 205 | if($base == 'views/' || $base == 'models/' || $base == 'plugins/') { 206 | if(is_file(APPPATH.$base.$module.'/'.$path.$file_ext)) { 207 | return array(APPPATH.$base.$module.'/'.$path, $file, FALSE); 208 | } 209 | } 210 | 211 | // If $module arg exists, then add as a place to search with $file as full path 212 | // as it would then be assumed to have no module in that first arg then 213 | if ($module && !isset($modules[$module])) { 214 | $modules[$module] = $path; 215 | } 216 | //One last area to search if modules is still empty, use file as the module name 217 | elseif(empty($modules)) { 218 | $modules[$file] = $path; 219 | } 220 | 221 | // Check this first as this is likely the correct area 222 | if($location) { 223 | foreach($modules as $module => $subpath) { 224 | $fullpath = $location.$module.'/'.$base.$subpath; 225 | 226 | if ($base == 'libraries/' AND is_file($fullpath.ucfirst($file_ext))) 227 | return array($fullpath, ucfirst($file), FALSE); 228 | 229 | // Is there a possible helper extension here? 230 | if($base == 'helpers/' && is_file($fullpath.config_item('subclass_prefix').$file_ext)) { 231 | return array($fullpath, $file, TRUE); 232 | } 233 | 234 | //log_message('debug', "Checking to see if $fullpath$file_ext exists: ". ((is_file($fullpath.$file_ext)) ? '**yes**' : 'no' )); 235 | if (is_file($fullpath.$file_ext)) return array($fullpath, $file, FALSE); 236 | } 237 | } 238 | else { 239 | //Go through loop if necessary 240 | foreach($modules as $module => $subpath) { 241 | $directories = CI::$APP->router->module_map($module); 242 | 243 | //If no directories are returned for the module above, then search all 244 | if(empty($directories)) { 245 | $directories = CI::$APP->router->module_map(); 246 | } 247 | 248 | foreach($directories as $module => $locations) { 249 | foreach($locations as $location) { 250 | $fullpath = $location.$module.'/'.$base.$subpath; 251 | 252 | if ($base == 'libraries/' AND is_file($fullpath.ucfirst($file_ext))) { 253 | return array($fullpath, ucfirst($file), FALSE); 254 | } 255 | 256 | // Is there a possible helper extension here? 257 | if($base == 'helpers/' && is_file($fullpath.config_item('subclass_prefix').$file_ext)) { 258 | return array($fullpath, $file, TRUE); 259 | } 260 | 261 | //log_message('debug', "Checking to see if $fullpath$file_ext exists: ". ((is_file($fullpath.$file_ext)) ? '**yes**' : 'no' )); 262 | if (is_file($fullpath.$file_ext)) return array($fullpath, $file, FALSE); 263 | } 264 | } 265 | } 266 | } 267 | 268 | return array(FALSE, $file, FALSE); 269 | } 270 | 271 | /** Parse module routes **/ 272 | public static function parse_routes($module, $uri, $locations = array()) { 273 | 274 | /* load the route file and merge routes if more than one location is read */ 275 | if ( ! isset(self::$routes[$module])) { 276 | self::$routes[$module] = array(); 277 | 278 | if(empty($locations)) { 279 | if (list($path) = self::find('routes', $module, 'config/') AND $path) { 280 | self::$routes[$module] = self::load_file('routes', $path, 'route'); 281 | } 282 | } 283 | else { 284 | foreach($locations as $module => $locations) { 285 | foreach($locations as $location) { 286 | if (list($path) = self::find('routes', $module, 'config/', $location) AND $path) { 287 | self::$routes[$module] = array_merge(self::$routes[$module], self::load_file('routes', $path, 'route')); 288 | } 289 | } 290 | } 291 | } 292 | } 293 | 294 | if ( ! isset(self::$routes[$module])) return; 295 | 296 | // Does our URI actually have a trailing slash? 297 | $URI =& load_class('URI', 'core'); 298 | if($URI->has_trailing_slash()) { 299 | $uri .= '/'; 300 | } 301 | 302 | /* parse module routes */ 303 | foreach (self::$routes[$module] as $key => $val) { 304 | //log_message('debug', "-- Found route rule: $key -> " . (($val) ? $val : '[Removed this default route]')); 305 | 306 | $key = str_replace(array(':any', ':num'), array('.*[^\/]{1}', '[0-9]+'), $key); 307 | 308 | if (preg_match('#^'.$key.'$#', $uri)) { 309 | if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) { 310 | $val = preg_replace('#^'.$key.'$#', $val, $uri); 311 | } 312 | 313 | if(array_shift(explode("/", $val))) { 314 | log_message('debug', "**** Found matching route '$uri' in module '$module', controller '" . array_shift(explode("/", $val)) . "', method '" . array_pop(explode("/", $val)) . "'"); 315 | } 316 | return array(explode('/', $module.'/'.$val)); 317 | } 318 | } 319 | } 320 | } -------------------------------------------------------------------------------- /third_party/MX/Router.php: -------------------------------------------------------------------------------- 1 | module; 54 | } 55 | 56 | public function fetch_location() { 57 | return $this->location; 58 | } 59 | 60 | /** 61 | * A simple function which maps out exactly what path a module is located. It will 62 | * also cache the results to avoid extra work and allows a specific module to be 63 | * looked up or all modules and their locations returned. 64 | * 65 | * @param string $module The module to lookup, leave blank to return all 66 | * @return array A list of all module paths or filtered to a specified module 67 | */ 68 | public function module_map($module = false) { 69 | 70 | // If we have already done this, use the cached results 71 | if(!empty($this->map)) { 72 | 73 | // If a directory which holds modules is specified, only return those 74 | if($module) { 75 | if(!isset($this->map[$module])) { 76 | return array(); 77 | } 78 | return array($module => $this->map[$module]); 79 | } 80 | 81 | // Otherwise return the full associative array of modules and their locations 82 | return $this->map; 83 | } 84 | // Since no cached results exist, lets do the work! 85 | else { 86 | // Go through each directory that contains modules 87 | foreach (Modules::$locations as $location => $offset) { 88 | 89 | // Get all modules(folders) in the folder that holds modules 90 | $directories = array(); 91 | if ($fp = @opendir($location)) { 92 | while (FALSE !== ($file = readdir($fp))) { 93 | if (trim($file, '.') && $file[0] != '.' && @is_dir($location . $file)) { 94 | $this->map[$file][] = $location; 95 | } 96 | } 97 | } 98 | } 99 | 100 | // Everything is cached, now return the results by running func again 101 | if(!empty($this->map)) { 102 | return $this->module_map($module); 103 | } 104 | 105 | // No modules? Return empty array 106 | return array(); 107 | } 108 | } 109 | 110 | public function _validate_request($segments) { 111 | 112 | $BM =& load_class('Benchmark', 'core'); 113 | $benchmark = 'uri_routing:_locate_( ' . implode("::", $segments) . ' )'; 114 | $BM->mark($benchmark . '_start'); 115 | 116 | $result = FALSE; 117 | 118 | if (count($segments) == 0) { 119 | $result = $segments; 120 | } 121 | /* locate module controller */ 122 | elseif ($located = $this->locate($segments)) { 123 | $result = $located; 124 | } 125 | /* use a default 404_override controller */ 126 | elseif (isset($this->routes['404_override']) AND $this->routes['404_override']) { 127 | $segments = explode('/', $this->routes['404_override']); 128 | if ($located = $this->locate($segments)) { 129 | $result = $located; 130 | } 131 | } 132 | 133 | $BM->mark($benchmark . '_end'); 134 | 135 | /* no controller found */ 136 | if(!$result) { 137 | show_404($this->uri->uri_string); 138 | } 139 | 140 | return $result; 141 | } 142 | 143 | /** 144 | * Parse Routes 145 | * 146 | * This function matches any routes that may exist in the config/routes.php file 147 | * against the URI to determine if the class/method need to be remapped. 148 | * 149 | * It has been extended to be strict with trailing slashes. 150 | * 151 | * @access private 152 | * @return void 153 | */ 154 | function _parse_routes() 155 | { 156 | // Turn the segment array into a URI string 157 | $uri = implode('/', $this->uri->segments); 158 | 159 | // Does our URI actually have a trailing slash? 160 | if($this->uri->has_trailing_slash()) { 161 | $uri .= '/'; 162 | } 163 | 164 | // Is there a literal match? If so we're done 165 | if (isset($this->routes[$uri])) 166 | { 167 | return $this->_set_request(explode('/', $this->routes[$uri])); 168 | } 169 | 170 | // Loop through the route array looking for wild-cards 171 | foreach ($this->routes as $key => $val) 172 | { 173 | // Convert wild-cards to RegEx 174 | $key = str_replace(':any', '.*[^\/]{1}', str_replace(':num', '[0-9]+', $key)); 175 | 176 | // Does the RegEx match? 177 | if (preg_match('#^'.$key.'$#', $uri)) 178 | { 179 | // Do we have a back-reference? 180 | if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) 181 | { 182 | $val = preg_replace('#^'.$key.'$#', $val, $uri); 183 | } 184 | 185 | return $this->_set_request(explode('/', $val)); 186 | } 187 | } 188 | 189 | // If we got this far it means we didn't encounter a 190 | // matching route so we'll set the site default route 191 | $this->_set_request($this->uri->segments); 192 | } 193 | 194 | /** Locate the controller **/ 195 | public function locate($segments) { 196 | 197 | $this->module = ''; 198 | $this->directory = ''; 199 | $this->location = ''; 200 | $ext = $this->config->item('controller_suffix').EXT; 201 | $routed = implode("/", $segments); 202 | 203 | /* use module route if available and use exact module location if exists */ 204 | if (isset($segments[0]) && $this->module_map($segments[0]) AND list($routes) = Modules::parse_routes($segments[0], $routed, $this->module_map($segments[0]))) { 205 | $segments = $routes; 206 | } 207 | // Otherwise go through all module routes and stop at the first match if any (lower precedence) 208 | elseif(isset($segments[0])) { 209 | //log_message('debug', "Scanning for first match in all modules' routing files with the URI segment: " . $routed); 210 | 211 | //Get all of the module names and locations 212 | $directories = $this->module_map(); 213 | 214 | foreach($directories as $module => $locations) { 215 | if(list($routes) = Modules::parse_routes($module, $routed, array($module => $locations))) { 216 | $segments = $routes; 217 | 218 | //Since a route is found, no need to keep looking, break out of loop 219 | break; 220 | } 221 | } 222 | } 223 | 224 | // Let's see where the request originated ie, from a module or router, (is there a better way?) 225 | // If it comes from a router that assumes it is being loaded via a public website address, if it comes from 226 | // a module then that means it was called from somewhere internally. Tests indicate this is almost a free 227 | // task, benchmarks show 0.0000 seconds to complete, so not even traceable. 228 | $trace = debug_backtrace(); 229 | $module_source = (isset($trace[1]['class']) && $trace[1]['class'] == 'Modules' && isset($trace[1]['function']) && $trace[1]['function'] == 'load'); 230 | 231 | // Should all modules be unaccessable unless a route explicitly matches? 232 | if($this->remove_default_routes && !$module_source && empty($routes) && !$this->_validate_route($routed)) { 233 | return; 234 | } 235 | 236 | // Let's make sure all segments are lower case. It is possible in routing files that uppercase letters are 237 | // used. This ensures everything gets translated to lowercase. The main reason for doing this is that on 238 | // Windows machines since files will match any case, it may work there, but on Linux machines it must match 239 | // the exact case. This could cause differences between development machines and production machines which 240 | // would allow a route to work fine on Windows, but not on Linux, and may be hard to trace the cause. By 241 | // adding this you should always ensure all controllers are in lowercase. 242 | $segments = array_map('strtolower', $segments); 243 | 244 | /* get the segments array elements */ 245 | list($module, $directory, $controller) = array_pad($segments, 3, NULL); 246 | 247 | /* check modules */ 248 | $directories = $this->module_map($module); 249 | 250 | foreach ($directories as $module => $locations) { 251 | foreach($locations as $location) { 252 | $offset = Modules::$locations[$location]; 253 | 254 | /* module exists? */ 255 | if (is_dir($source = $location.$module.'/controllers/')) { 256 | $this->module = $module; 257 | $this->directory = $offset.$module.'/controllers/'; 258 | $this->location = $location; 259 | 260 | /* module sub-controller exists? */ 261 | if($directory AND is_file($source.$directory.$ext)) { 262 | return array_slice($segments, 1); 263 | } 264 | 265 | /* module sub-directory exists? */ 266 | if($directory AND is_dir($source.$directory.'/')) { 267 | 268 | $source = $source.$directory.'/'; 269 | $this->directory .= $directory.'/'; 270 | 271 | /* module sub-directory controller exists? */ 272 | if(is_file($source.$directory.$ext)) { 273 | return array_slice($segments, 1); 274 | } 275 | 276 | /* module sub-directory sub-controller exists? */ 277 | if($controller AND is_file($source.$controller.$ext)) { 278 | return array_slice($segments, 2); 279 | } 280 | } 281 | 282 | /* module controller exists? */ 283 | if(is_file($source.$module.$ext)) { 284 | return $segments; 285 | } 286 | } 287 | } 288 | } 289 | 290 | /* application controller exists? */ 291 | if (is_file(APPPATH.'controllers/'.$module.$ext)) { 292 | return $segments; 293 | } 294 | 295 | /* application sub-directory controller exists? */ 296 | if($directory AND is_file(APPPATH.'controllers/'.$module.'/'.$directory.$ext)) { 297 | $this->directory = $module.'/'; 298 | return array_slice($segments, 1); 299 | } 300 | 301 | /* application sub-directory default controller exists? */ 302 | if (is_file(APPPATH.'controllers/'.$module.'/'.$this->default_controller.$ext)) { 303 | $this->directory = $module.'/'; 304 | return array($this->default_controller); 305 | } 306 | } 307 | 308 | public function set_class($class) { 309 | $this->class = $class.$this->config->item('controller_suffix'); 310 | } 311 | 312 | /** 313 | * Simply returns if our URI had a trailing slash or not 314 | * 315 | * @return boolean TRUE if URI has trailing slash, FALSE if not 316 | */ 317 | protected function has_trailing_slash() { 318 | return $this->uri->has_trailing_slash(); 319 | } 320 | 321 | /** 322 | * Takes a translated route, parses wildcards, and returns TRUE if anything matches as being valid 323 | * 324 | * @param string $routed 325 | */ 326 | private function _validate_route($routed) { 327 | 328 | // Break down all the segments of our translated route 329 | $routed_segments = explode('/', $routed); 330 | 331 | // Do we have a match with our default controller? 332 | if ($this->default_controller == $routed || $this->default_controller == $routed_segments[0]) { 333 | return TRUE; 334 | } 335 | 336 | // Turn the URI segment array into a URI string 337 | $uri = implode('/', $this->uri->segments); 338 | 339 | // Go through each route and see if it matches with wildcards 340 | foreach ($this->routes as $key => $val) { 341 | $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key)); 342 | 343 | if (preg_match('#^' . $key . '$#', $uri)) { 344 | return TRUE; 345 | } 346 | } 347 | 348 | // Finally do we have a match with a 404 override? 349 | if(isset($this->routes['404_override']) AND $this->routes['404_override'] == $routed) { 350 | return TRUE; 351 | } 352 | 353 | return FALSE; 354 | } 355 | } -------------------------------------------------------------------------------- /third_party/MX/URI.php: -------------------------------------------------------------------------------- 1 | 56 | */ 57 | class MX_URI extends CI_URI { 58 | 59 | /** 60 | * Simply tracks if the URI had a trailing slash before it gets stripped by CodeIgniter 61 | * 62 | * @var boolean 63 | */ 64 | private $has_trailing_slash = FALSE; 65 | 66 | /** 67 | * Get the URI String 68 | * 69 | * This function has to be re-declared to be able to extend _detect_uri because _detect_uri is a private method 70 | * in the original URI class. Would have been easier if it was protected. Nothing has been changed in this 71 | * method otherwise. Its possible that we will still need to extend this function as well since we only account 72 | * for _detect_uri which works for most situations. May need to work PATH_INFO and QUERY_STRING if that is used 73 | * to form the URI string. 74 | * 75 | * @access private 76 | * @return string 77 | */ 78 | function _fetch_uri_string() 79 | { 80 | if (strtoupper($this->config->item('uri_protocol')) == 'AUTO') 81 | { 82 | // Is the request coming from the command line? 83 | if (php_sapi_name() == 'cli' or defined('STDIN')) 84 | { 85 | $this->_set_uri_string($this->_parse_cli_args()); 86 | return; 87 | } 88 | 89 | // Let's try the REQUEST_URI first, this will work in most situations 90 | if ($uri = $this->_detect_uri()) 91 | { 92 | $this->_set_uri_string($uri); 93 | return; 94 | } 95 | 96 | // Is there a PATH_INFO variable? 97 | // Note: some servers seem to have trouble with getenv() so we'll test it two ways 98 | $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO'); 99 | if (trim($path, '/') != '' && $path != "/".SELF) 100 | { 101 | $this->_set_uri_string($path); 102 | return; 103 | } 104 | 105 | // No PATH_INFO?... What about QUERY_STRING? 106 | $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); 107 | if (trim($path, '/') != '') 108 | { 109 | $this->_set_uri_string($path); 110 | return; 111 | } 112 | 113 | // As a last ditch effort lets try using the $_GET array 114 | if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') 115 | { 116 | $this->_set_uri_string(key($_GET)); 117 | return; 118 | } 119 | 120 | // We've exhausted all our options... 121 | $this->uri_string = ''; 122 | return; 123 | } 124 | 125 | $uri = strtoupper($this->config->item('uri_protocol')); 126 | 127 | if ($uri == 'REQUEST_URI') 128 | { 129 | $this->_set_uri_string($this->_detect_uri()); 130 | return; 131 | } 132 | elseif ($uri == 'CLI') 133 | { 134 | $this->_set_uri_string($this->_parse_cli_args()); 135 | return; 136 | } 137 | 138 | $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri); 139 | $this->_set_uri_string($path); 140 | } 141 | 142 | /** 143 | * Detects the URI 144 | * 145 | * This function will detect the URI automatically and fix the query string if necessary. This extended version 146 | * will also update whether or not a trailing slash was used in the URI. 147 | * 148 | * @access private 149 | * @return string 150 | */ 151 | private function _detect_uri() 152 | { 153 | if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME'])) 154 | { 155 | return ''; 156 | } 157 | 158 | $uri = $_SERVER['REQUEST_URI']; 159 | if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) 160 | { 161 | $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME'])); 162 | } 163 | elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) 164 | { 165 | $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); 166 | } 167 | 168 | // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct 169 | // URI is found, and also fixes the QUERY_STRING server var and $_GET array. 170 | if (strncmp($uri, '?/', 2) === 0) 171 | { 172 | $uri = substr($uri, 2); 173 | } 174 | $parts = preg_split('#\?#i', $uri, 2); 175 | $uri = $parts[0]; 176 | if (isset($parts[1])) 177 | { 178 | $_SERVER['QUERY_STRING'] = $parts[1]; 179 | parse_str($_SERVER['QUERY_STRING'], $_GET); 180 | } 181 | else 182 | { 183 | $_SERVER['QUERY_STRING'] = ''; 184 | $_GET = array(); 185 | } 186 | 187 | if ($uri == '/' || empty($uri)) 188 | { 189 | return '/'; 190 | } 191 | 192 | $uri = parse_url($uri, PHP_URL_PATH); 193 | 194 | // Does a trailing slash exist? 195 | if(preg_match('/\/$/', $uri)) 196 | { 197 | $this->has_trailing_slash = TRUE; 198 | } 199 | 200 | // Do some final cleaning of the URI and return it 201 | return str_replace(array('//', '../'), '/', trim($uri, '/')); 202 | } 203 | 204 | /** 205 | * Parse cli arguments 206 | * 207 | * Take each command line argument and assume it is a URI segment. 208 | * 209 | * Nothing changed here from the original CI _parse_cli_args. Unfortunately we have to add this because it is a 210 | * private method, and CLI requests will not function properly without it. 211 | * 212 | * @access private 213 | * @return string 214 | */ 215 | private function _parse_cli_args() 216 | { 217 | $args = array_slice($_SERVER['argv'], 1); 218 | 219 | return $args ? '/' . implode('/', $args) : ''; 220 | } 221 | 222 | /** 223 | * Returns whether or not the URI loaded with trailing slash 224 | * 225 | * @access public 226 | * @return string 227 | */ 228 | public function has_trailing_slash() 229 | { 230 | return $this->has_trailing_slash; 231 | } 232 | 233 | /** 234 | * Returns the URI string exactly as it really is, with or without a trailing slash 235 | * 236 | * @return string 237 | */ 238 | public function exact_uri_string() { 239 | return $this->uri_string() . ($this->has_trailing_slash() ? '/' : ''); 240 | } 241 | } 242 | 243 | 244 | /* End of file $filename$ */ 245 | /* Location: $location$$filename$ */ --------------------------------------------------------------------------------