PageRenderTime 70ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/application/core/Router.php

https://gitlab.com/fredec/ionizecms-1.0.8.x
PHP | 757 lines | 467 code | 117 blank | 173 comment | 68 complexity | aa055913f3cdf906c0f21720d5f726f3 MD5 | raw file
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /*
  3. * Created by Martin Wernståhl on 2009-04-29.
  4. * Copyright (c) 2009 Martin Wernståhl.
  5. * All rights reserved.
  6. */
  7. require_once 'finder/finder.php';
  8. require_once strtr(dirname(__FILE__), '\\', '/') .'/../helpers/trace_helper.php';
  9. /**
  10. *
  11. */
  12. class CI_Router
  13. {
  14. public $config;
  15. public $routes = array();
  16. public $error_routes = array();
  17. public $uri_protocol = 'auto';
  18. public $default_controller = NULL;
  19. public $scaffolding_request = FALSE; // Must be set to FALSE
  20. /*
  21. * The name of the module currently in.
  22. */
  23. public $module = FALSE;
  24. /*
  25. * Path to the module.
  26. */
  27. public $module_path = FALSE;
  28. /*
  29. * Class and controller file name
  30. */
  31. public $class = '';
  32. /*
  33. * Method to be called
  34. */
  35. public $method = 'index';
  36. /*
  37. * Directory with the controller in.
  38. */
  39. public $directory = '';
  40. /*
  41. * Language key to use.
  42. */
  43. public $lang_key = '';
  44. /*
  45. * Raw detected key.
  46. */
  47. private $raw_key = '';
  48. /*
  49. * List of languages which can be used.
  50. *
  51. * Format: array(lang_abbreviation => human_readable_english)
  52. */
  53. public $lang_dict = array();
  54. public $lang_online = array();
  55. function __construct()
  56. {
  57. // Load the config class
  58. $this->config = load_class('Config');
  59. // Loads the Ionizes language config file. This file is created by the lang admin section of Ionize
  60. $this->config->load('language');
  61. $this->config->load('ionize');
  62. $this->uri = load_class('URI');
  63. // default language abbreviation
  64. $this->lang_key = $this->config->item('default_lang_code');
  65. // all available website languages
  66. $this->lang_dict = $this->config->item('available_languages');
  67. $this->lang_online = $this->config->item('online_languages');
  68. $this->_calc_modpath_apppath_diff();
  69. $this->_set_routing();
  70. // If maintenance mode, don't go further
  71. if (config_item('maintenance') == 1 && $this->uri->segment(1) != config_item('admin_url'))
  72. {
  73. if ( ! in_array($_SERVER['REMOTE_ADDR'], config_item('maintenance_ips')))
  74. {
  75. $content = 'Website in maintenance';
  76. if (file_exists(FCPATH.'maintenance.html'))
  77. {
  78. $content = file_get_contents(FCPATH.'maintenance.html');
  79. }
  80. elseif (file_exists(APPPATH.'views/core/maintenance.php'))
  81. {
  82. $path = APPPATH.'views/core/maintenance.php';
  83. ob_start();
  84. if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
  85. {
  86. echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($path))));
  87. }
  88. else
  89. {
  90. include($path);
  91. }
  92. ob_end_flush();
  93. die();
  94. }
  95. echo $content;
  96. die();
  97. }
  98. }
  99. log_message('debug', "Router Class Initialized");
  100. }
  101. /**
  102. * Calculates the path difference between APPPATH and MODPATH, needed to be
  103. * able to call controllers in modules.
  104. *
  105. * The $this->modpath_diff is to be appended to the APPPATH constant, then
  106. * that path will point to the modules dir.
  107. *
  108. * @author Martin Wernståhl <m4rw3r@gmail.com>
  109. */
  110. public function _calc_modpath_apppath_diff()
  111. {
  112. // Clean and split the paths
  113. $apppath = explode('/', trim(strtr(APPPATH, '\\', '/'), '/'));
  114. $modpath = explode('/', trim(strtr(MODPATH, '\\', '/'), '/'));
  115. $i = 0;
  116. $ac = count($apppath);
  117. $mc = count($modpath);
  118. for(; $i < $ac && $i < $mc; $i++)
  119. {
  120. if($apppath[$i] != $modpath[$i])
  121. {
  122. break;
  123. }
  124. }
  125. // Assemble the difference
  126. $this->modpath_diff = str_repeat('..'.DIRECTORY_SEPARATOR, $ac - $i).implode(DIRECTORY_SEPARATOR, array_slice($modpath, $i));
  127. }
  128. // ------------------------------------------------------------------------
  129. /**
  130. * Sets the routes.
  131. *
  132. * @return void
  133. */
  134. public function _set_routing()
  135. {
  136. if($this->config->item('enable_query_strings') && isset($_GET[$this->config->item('controller_trigger')]))
  137. {
  138. // controller
  139. $this->class = trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')]));
  140. // module
  141. if(isset($_GET[$this->config->item('module_trigger')]))
  142. {
  143. $this->module = trim($this->uri->_filter_uri($_GET[$this->config->item('module_trigger')]));
  144. // check if it is installed
  145. include APPPATH . 'config/modules.php';
  146. if(in_array($this->module, array_keys($modules)) && ( ! in_array($this->module, $disable_controller)))
  147. {
  148. // get path
  149. $this->module_path = $modules[$this->module];
  150. }
  151. else
  152. {
  153. // no module with that name exists
  154. $this->module = FALSE;
  155. }
  156. }
  157. // method
  158. if(isset($_GET[$this->config->item('function_trigger')]))
  159. {
  160. $this->method = trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')]));
  161. }
  162. // language
  163. if(isset($_GET[$this->config->item('language_trigger')]) &&
  164. $key = $this->valid_lang_key($_GET[$this->config->item('language_trigger')]))
  165. {
  166. $this->lang_key = $key;
  167. $this->apply_language();
  168. }
  169. else
  170. {
  171. // Lang detection : Cookie, Browser
  172. $this->detect_language();
  173. }
  174. }
  175. else
  176. {
  177. // Fetch the complete URI string
  178. $this->uri->_fetch_uri_string();
  179. // use the default controller
  180. if(empty($this->uri->uri_string))
  181. $this->uri->uri_string = '';
  182. $this->uri->_remove_url_suffix();
  183. // clean the uri and explode it
  184. $this->explode_segments($this->uri->uri_string);
  185. $this->raw_key = current($this->uri->segments);
  186. // Language key : check if we have a valid language key there
  187. if($key = $this->validate_lang_key($this->raw_key))
  188. {
  189. if (count($this->uri->segments) == 1 && $key == $this->config->item('default_lang_code'))
  190. $this->redirect_home_to_base_url();
  191. $this->lang_key = $key;
  192. $this->apply_language();
  193. // remove the language key
  194. array_shift($this->uri->segments);
  195. if(empty($this->uri->segments))
  196. {
  197. $this->uri->uri_string = '';
  198. }
  199. else
  200. {
  201. // remove the language key from the uri_string
  202. $this->uri->uri_string = strstr($this->uri->uri_string, '/');
  203. }
  204. }
  205. else
  206. {
  207. // Lang detection : Cookie, Browser
  208. $this->detect_language();
  209. // Home Page : Redirect to detected lang ?
  210. if ($this->raw_key == '')
  211. {
  212. $this->redirect_home_to_lang_url();
  213. }
  214. }
  215. // END Language key
  216. // get the generated module file and see if we can match to a module
  217. include APPPATH . 'config/modules.php';
  218. // get the first segment, to match to the modules
  219. $rmodule = current($this->uri->segments);
  220. // do we have a module with that name?
  221. if(in_array($rmodule, array_keys($modules)) && ( ! in_array($rmodule, $disable_controller)))
  222. {
  223. // yes, remove it and store it as a module, also remove the module name from the uri
  224. $this->module_path = $modules[array_shift($this->uri->segments)];
  225. $this->module = $rmodule;
  226. }
  227. // do we have an aliased controller?
  228. elseif(in_array($rmodule, array_keys($aliases)))
  229. {
  230. // yes, get module and controller, remove alias from the uri
  231. list($this->module_path, $c) = each($aliases[array_shift($this->uri->segments)]);
  232. $this->module = array_search($this->module_path, $modules);
  233. // add the controller back to it, so the match_to_routes() and find controller works as they should
  234. $this->uri->segments = array_merge(array($c), $this->uri->segments);
  235. }
  236. if($this->module_path)
  237. {
  238. // we have module, add the module to the cascade
  239. Finder::$paths = array_merge(array(MODPATH . $this->module_path . '/'), Finder::$paths);
  240. }
  241. $this->uri->segments = $this->match_to_routes($this->uri->segments);
  242. $this->find_controller($this->uri->segments);
  243. // add the module first so it is correct
  244. if($this->module_path)
  245. {
  246. $this->uri->segments = array_merge(array($this->module), $this->uri->segments);
  247. }
  248. // make them start with 1
  249. $this->uri->_reindex_segments();
  250. }
  251. }
  252. // ------------------------------------------------------------------------
  253. /**
  254. * A mashup of the two URI methods _explode_segments() and _filter_uri()
  255. *
  256. * @param string
  257. * @return void
  258. */
  259. public function explode_segments($str)
  260. {
  261. $str = preg_replace("|/*(.+?)/*$|", "\\1", $str);
  262. if( ($str != '' && $this->config->item('permitted_uri_chars') != '')
  263. && (! preg_match("|^[{$this->config->item('permitted_uri_chars')}/]+$|i", $str)))
  264. {
  265. header('HTTP/1.1 400 Bad Request');
  266. show_error('The URI you submitted has disallowed characters.');
  267. }
  268. // Convert programmatic characters to entities
  269. $bad = array('$', '(', ')', '%28', '%29');
  270. $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
  271. $str = str_replace($bad, $good, $str);
  272. // remove segments
  273. foreach(explode('/', $str) as $val)
  274. {
  275. // Filter segments for security
  276. $val = trim($val);
  277. if($val != '')
  278. $this->uri->segments[] = $val;
  279. }
  280. }
  281. // ------------------------------------------------------------------------
  282. /**
  283. * Tries to match the segments to a route.
  284. * @param array
  285. * @return array
  286. */
  287. public function match_to_routes($segments)
  288. {
  289. // get the routes, reverse so we load the module's routes.php last - overwriting the others
  290. $files = array_reverse(Finder::find_file('routes', 'config', FALSE, TRUE));
  291. foreach($files as $f)
  292. {
  293. include($f);
  294. // merge the route : module's route overwrite other routes
  295. $this->routes = ( ! isset($route) OR ! is_array($route)) ? $this->routes : array_merge($this->routes, $route);
  296. unset($route);
  297. }
  298. // we may have an empty route here, because the first segment could have been a module
  299. if(empty($segments))
  300. {
  301. $default = empty($this->routes['default_controller']) ? FALSE : $this->routes['default_controller'];
  302. if( ! $default)
  303. {
  304. show_error("Unable to determine what should be displayed. A default route has not been specified in the module '$this->module' routing file.");
  305. }
  306. // $segments = explode('/', $default);
  307. }
  308. // FROM CodeIgniter, slightly modified
  309. // Turn the segment array into a URI string
  310. $uri = implode('/', $this->uri->segments);
  311. // Is there a literal match? If so we're done
  312. if(isset($this->routes[$uri]))
  313. {
  314. return explode('/', $this->routes[$uri]);
  315. }
  316. // Loop through the route array looking for wild-cards
  317. foreach($this->routes as $key => $val)
  318. {
  319. // Convert wild-cards to RegEx
  320. $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));
  321. // Does the RegEx match?
  322. if(preg_match('#^'.$key.'$#', $uri))
  323. {
  324. // Do we have a back-reference?
  325. if(strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE)
  326. {
  327. $val = preg_replace('#^'.$key.'$#', $val, $uri);
  328. }
  329. return explode('/', $val);
  330. }
  331. }
  332. return $this->uri->segments;
  333. }
  334. // ------------------------------------------------------------------------
  335. /**
  336. * Finds the correct controller and creates the rerouted segments.
  337. * @param array
  338. * @return void
  339. */
  340. public function find_controller($segments)
  341. {
  342. $params = array();
  343. if( ! $this->module_path)
  344. {
  345. $path = APPPATH;
  346. }
  347. else
  348. {
  349. $path = MODPATH . $this->module_path . '/';
  350. }
  351. while( ! empty($segments))
  352. {
  353. if(file_exists($path . 'controllers/' . implode('/', $segments) . EXT))
  354. {
  355. $this->class = array_pop($segments);
  356. $this->directory = implode('/', $segments);
  357. // set the method if we have one
  358. if( ! empty($params))
  359. {
  360. while($params[0] =='')
  361. array_shift($params);
  362. $this->method = array_shift($params);
  363. }
  364. // create the rerouted array, controller/method/params to avoid large changes to CI
  365. $this->uri->rsegments = array_merge(array($this->class, $this->method), $params);
  366. return;
  367. }
  368. // make a more general search
  369. array_unshift($params, array_pop($segments));
  370. }
  371. // Get the 404 override
  372. if ( ! empty($this->routes['404_override']))
  373. {
  374. $x = explode('/', $this->routes['404_override']);
  375. $method = isset($x[1]) ? $x[1] : 'index';
  376. $this->class = $x[0];
  377. $this->method = $method;
  378. $this->uri->rsegments = array($this->class, $this->method);
  379. return;
  380. }
  381. show_404(implode('/', $params));
  382. }
  383. // ------------------------------------------------------------------------
  384. /**
  385. * Loads the default route from the routes.php config.
  386. *
  387. * @return void
  388. */
  389. public function load_default_uri()
  390. {
  391. include APPPATH . 'config/routes.php';
  392. $default = empty($route['default_controller']) ? FALSE : $route['default_controller'];
  393. if( ! $default)
  394. {
  395. show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
  396. }
  397. $this->uri->uri_string = $default;
  398. }
  399. // ------------------------------------------------------------------------
  400. public function get_default_controller()
  401. {
  402. if (is_null($this->default_controller))
  403. {
  404. include APPPATH . 'config/routes.php';
  405. $this->default_controller = $route['default_controller'];
  406. }
  407. return $this->default_controller;
  408. }
  409. // ------------------------------------------------------------------------
  410. public function get_lang_key()
  411. {
  412. return $this->lang_key;
  413. }
  414. // ------------------------------------------------------------------------
  415. public function get_raw_key()
  416. {
  417. return $this->raw_key;
  418. }
  419. // ------------------------------------------------------------------------
  420. public function is_home()
  421. {
  422. return empty($this->raw_key);
  423. }
  424. // ------------------------------------------------------------------------
  425. /**
  426. * Set the default language regarding the URL (for admin)
  427. * and regarding the Cookie or the Browser's user's language
  428. *
  429. * @return void
  430. *
  431. */
  432. public function detect_language()
  433. {
  434. $segments = array_values($this->uri->segment_array());
  435. // Check for admin language file
  436. if ((isset($segments[0]) && $segments[0] == config_item('admin_url')) OR (isset($segments[1]) && $segments[1] == config_item('admin_url')) )
  437. {
  438. $this->lang_key = $this->config->item('default_admin_lang');
  439. }
  440. else
  441. {
  442. $selected_language = NULL;
  443. // Case of Home page with Cookie : The asked lang code is the default one
  444. $selected_language = ( ! empty($_COOKIE['ion_selected_language'])) ? $_COOKIE['ion_selected_language'] : NULL ;
  445. if( ! is_null($selected_language))
  446. {
  447. // Use lang preference from cookie
  448. $this->lang_key = $selected_language;
  449. }
  450. else
  451. {
  452. // Define user lang by browser preferences
  453. $browser_lang = (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) ? substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) : '';
  454. if(
  455. $browser_lang != ''
  456. && $browser_lang != $this->config->item('default_lang_code')
  457. && !empty($this->lang_dict[$browser_lang])
  458. && !empty($this->lang_online[$browser_lang])
  459. )
  460. $this->lang_key = $browser_lang;
  461. else
  462. $this->lang_key = $this->config->item('default_lang_code');
  463. }
  464. }
  465. $this->apply_language();
  466. }
  467. // ------------------------------------------------------------------------
  468. /**
  469. * Checks and redirect properly the Home page to the URL
  470. * containing the lang code, if needed
  471. *
  472. * Note :
  473. * Inside pages redirect could not work as $rawkey set by _set_routing can be another lang.
  474. *
  475. */
  476. public function redirect_home_to_lang_url()
  477. {
  478. if (
  479. count($this->lang_online) > 1 &&
  480. $this->lang_key != $this->config->item('default_lang_code')
  481. )
  482. {
  483. $url = config_item('base_url').$this->lang_key;
  484. log_message('debug', 'Router : Detected lang code : ' . $this->lang_key);
  485. log_message('debug', 'Router : Redirect to : '. $url);
  486. // 302 Found
  487. header('HTTP/1.1 301 Moved Permanently');
  488. header('Location: '.$url, TRUE, 301);
  489. }
  490. }
  491. public function redirect_home_to_base_url()
  492. {
  493. $url = config_item('base_url');
  494. // 302 Found
  495. header('HTTP/1.1 301 Moved Permanently');
  496. header('Location: '.$url, TRUE, 301);
  497. }
  498. /**
  499. * Validates a language key and returns the filtered variant.
  500. *
  501. * @param scalar
  502. * @return string|FALSE
  503. */
  504. public function validate_lang_key($key)
  505. {
  506. $key = strtolower(trim((String) $key, '/\\ '));
  507. $segments = array_values($this->uri->segment_array());
  508. // If installer warning, the users languages are detected !
  509. // Not important, but not so clean, should be correctly implemented !
  510. // Admin lang key
  511. if (
  512. (isset($segments[0]) && $segments[0] == config_item('admin_url'))
  513. OR (
  514. isset($segments[1])
  515. && $segments[1] == config_item('admin_url')
  516. )
  517. )
  518. {
  519. if (is_file(APPPATH.'language/'.$key.'/admin_lang'.EXT))
  520. {
  521. return $key;
  522. }
  523. else
  524. {
  525. log_message('debug', 'Router: The key "'.$key.'" is not a valid admin language key.');
  526. }
  527. }
  528. // User defined languages
  529. else if
  530. (
  531. ! empty($this->lang_dict[$key])
  532. && ! empty($this->lang_online[$key])
  533. )
  534. {
  535. return $key;
  536. }
  537. else
  538. {
  539. log_message('debug', 'Router: The key "'.$key.'" is not a valid language key.');
  540. }
  541. return FALSE;
  542. }
  543. // ------------------------------------------------------------------------
  544. /**
  545. * Sets the configuration to the detected language.
  546. *
  547. * @return void
  548. */
  549. public function apply_language()
  550. {
  551. log_message('debug', 'Router: Applying the language key "'.$this->lang_key.'".');
  552. $this->config->set_item('detected_lang_code', $this->lang_key);
  553. }
  554. // ------------------------------------------------------------------------
  555. public function fetch_controller_path()
  556. {
  557. if($this->module_path)
  558. {
  559. $path = MODPATH;
  560. }
  561. else
  562. {
  563. $path = APPPATH;
  564. }
  565. return $path . 'controllers/' . ($this->directory ? $this->directory . '/' : '') . $this->class . EXT;
  566. }
  567. // ------------------------------------------------------------------------
  568. public function fetch_module_uri_seg()
  569. {
  570. return $this->module;
  571. }
  572. // ------------------------------------------------------------------------
  573. public function fetch_class()
  574. {
  575. return $this->class;
  576. }
  577. // ------------------------------------------------------------------------
  578. public function fetch_method()
  579. {
  580. return $this->method;
  581. }
  582. // ------------------------------------------------------------------------
  583. /**
  584. * @deprecated Use get_lang_key() instead
  585. *
  586. * @return string
  587. */
  588. public function fetch_lang_key()
  589. {
  590. return $this->lang_key;
  591. }
  592. // ------------------------------------------------------------------------
  593. public function fetch_lang_name()
  594. {
  595. return $this->lang_dict[$this->lang_key];
  596. }
  597. // ------------------------------------------------------------------------
  598. public function fetch_directory()
  599. {
  600. if($this->module_path)
  601. {
  602. // First ".." is for the controllers folder, then we need to add that after the module path
  603. return '../'.$this->modpath_diff.'/'.$this->module_path.'/controllers/'.($this->directory ? $this->directory.'/' : '');
  604. }
  605. else
  606. {
  607. return ($this->directory ? $this->directory.'/' : '');
  608. }
  609. }
  610. }
  611. /* End of file MY_Router.php */
  612. /* Location: ./application/core/Router.php */