URI.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP 5.1.6 or newer
  6. *
  7. * @package CodeIgniter
  8. * @author EllisLab Dev Team
  9. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
  10. * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
  11. * @license http://codeigniter.com/user_guide/license.html
  12. * @link http://codeigniter.com
  13. * @since Version 1.0
  14. * @filesource
  15. */
  16. // ------------------------------------------------------------------------
  17. /**
  18. * URI Class
  19. *
  20. * Parses URIs and determines routing
  21. *
  22. * @package CodeIgniter
  23. * @subpackage Libraries
  24. * @category URI
  25. * @author EllisLab Dev Team
  26. * @link http://codeigniter.com/user_guide/libraries/uri.html
  27. */
  28. class CI_URI {
  29. /**
  30. * List of cached uri segments
  31. *
  32. * @var array
  33. * @access public
  34. */
  35. var $keyval = array();
  36. /**
  37. * Current uri string
  38. *
  39. * @var string
  40. * @access public
  41. */
  42. var $uri_string;
  43. /**
  44. * List of uri segments
  45. *
  46. * @var array
  47. * @access public
  48. */
  49. var $segments = array();
  50. /**
  51. * Re-indexed list of uri segments
  52. * Starts at 1 instead of 0
  53. *
  54. * @var array
  55. * @access public
  56. */
  57. var $rsegments = array();
  58. /**
  59. * Constructor
  60. *
  61. * Simply globalizes the $RTR object. The front
  62. * loads the Router class early on so it's not available
  63. * normally as other classes are.
  64. *
  65. * @access public
  66. */
  67. function __construct()
  68. {
  69. $this->config =& load_class('Config', 'core');
  70. log_message('debug', "URI Class Initialized");
  71. }
  72. // --------------------------------------------------------------------
  73. /**
  74. * Get the URI String
  75. *
  76. * @access private
  77. * @return string
  78. */
  79. function _fetch_uri_string()
  80. {
  81. if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
  82. {
  83. // Is the request coming from the command line?
  84. if (php_sapi_name() == 'cli' or defined('STDIN'))
  85. {
  86. $this->_set_uri_string($this->_parse_cli_args());
  87. return;
  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. // Is there a PATH_INFO variable?
  96. // Note: some servers seem to have trouble with getenv() so we'll test it two ways
  97. $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
  98. if (trim($path, '/') != '' && $path != "/".SELF)
  99. {
  100. $this->_set_uri_string($path);
  101. return;
  102. }
  103. // No PATH_INFO?... What about QUERY_STRING?
  104. $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
  105. if (trim($path, '/') != '')
  106. {
  107. $this->_set_uri_string($path);
  108. return;
  109. }
  110. // As a last ditch effort lets try using the $_GET array
  111. if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
  112. {
  113. $this->_set_uri_string(key($_GET));
  114. return;
  115. }
  116. // We've exhausted all our options...
  117. $this->uri_string = '';
  118. return;
  119. }
  120. $uri = strtoupper($this->config->item('uri_protocol'));
  121. if ($uri == 'REQUEST_URI')
  122. {
  123. $this->_set_uri_string($this->_detect_uri());
  124. return;
  125. }
  126. elseif ($uri == 'CLI')
  127. {
  128. $this->_set_uri_string($this->_parse_cli_args());
  129. return;
  130. }
  131. $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
  132. $this->_set_uri_string($path);
  133. }
  134. // --------------------------------------------------------------------
  135. /**
  136. * Set the URI String
  137. *
  138. * @access public
  139. * @param string
  140. * @return string
  141. */
  142. function _set_uri_string($str)
  143. {
  144. // Filter out control characters
  145. $str = remove_invisible_characters($str, FALSE);
  146. // If the URI contains only a slash we'll kill it
  147. $this->uri_string = ($str == '/') ? '' : $str;
  148. }
  149. // --------------------------------------------------------------------
  150. /**
  151. * Detects the URI
  152. *
  153. * This function will detect the URI automatically and fix the query string
  154. * if necessary.
  155. *
  156. * @access private
  157. * @return string
  158. */
  159. private function _detect_uri()
  160. {
  161. if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME']))
  162. {
  163. return '';
  164. }
  165. $uri = $_SERVER['REQUEST_URI'];
  166. if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
  167. {
  168. $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
  169. }
  170. elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
  171. {
  172. $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
  173. }
  174. // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
  175. // URI is found, and also fixes the QUERY_STRING server var and $_GET array.
  176. if (strncmp($uri, '?/', 2) === 0)
  177. {
  178. $uri = substr($uri, 2);
  179. }
  180. $parts = preg_split('#\?#i', $uri, 2);
  181. $uri = $parts[0];
  182. if (isset($parts[1]))
  183. {
  184. $_SERVER['QUERY_STRING'] = $parts[1];
  185. parse_str($_SERVER['QUERY_STRING'], $_GET);
  186. }
  187. else
  188. {
  189. $_SERVER['QUERY_STRING'] = '';
  190. $_GET = array();
  191. }
  192. if ($uri == '/' || empty($uri))
  193. {
  194. return '/';
  195. }
  196. $uri = parse_url($uri, PHP_URL_PATH);
  197. // Do some final cleaning of the URI and return it
  198. return str_replace(array('//', '../'), '/', trim($uri, '/'));
  199. }
  200. // --------------------------------------------------------------------
  201. /**
  202. * Parse cli arguments
  203. *
  204. * Take each command line argument and assume it is a URI segment.
  205. *
  206. * @access private
  207. * @return string
  208. */
  209. private function _parse_cli_args()
  210. {
  211. $args = array_slice($_SERVER['argv'], 1);
  212. return $args ? '/' . implode('/', $args) : '';
  213. }
  214. // --------------------------------------------------------------------
  215. /**
  216. * Filter segments for malicious characters
  217. *
  218. * @access private
  219. * @param string
  220. * @return string
  221. */
  222. function _filter_uri($str)
  223. {
  224. if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
  225. {
  226. // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
  227. // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
  228. if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
  229. {
  230. show_error('The URI you submitted has disallowed characters.', 400);
  231. }
  232. }
  233. // Convert programatic characters to entities
  234. $bad = array('$', '(', ')', '%28', '%29');
  235. $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
  236. return str_replace($bad, $good, $str);
  237. }
  238. // --------------------------------------------------------------------
  239. /**
  240. * Remove the suffix from the URL if needed
  241. *
  242. * @access private
  243. * @return void
  244. */
  245. function _remove_url_suffix()
  246. {
  247. if ($this->config->item('url_suffix') != "")
  248. {
  249. $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
  250. }
  251. }
  252. // --------------------------------------------------------------------
  253. /**
  254. * Explode the URI Segments. The individual segments will
  255. * be stored in the $this->segments array.
  256. *
  257. * @access private
  258. * @return void
  259. */
  260. function _explode_segments()
  261. {
  262. foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
  263. {
  264. // Filter segments for security
  265. $val = trim($this->_filter_uri($val));
  266. if ($val != '')
  267. {
  268. // BY leefouce case insensitive url
  269. // $this->segments[] = $val;
  270. $this->segments[] = strtolower($val);
  271. }
  272. }
  273. }
  274. // --------------------------------------------------------------------
  275. /**
  276. * Re-index Segments
  277. *
  278. * This function re-indexes the $this->segment array so that it
  279. * starts at 1 rather than 0. Doing so makes it simpler to
  280. * use functions like $this->uri->segment(n) since there is
  281. * a 1:1 relationship between the segment array and the actual segments.
  282. *
  283. * @access private
  284. * @return void
  285. */
  286. function _reindex_segments()
  287. {
  288. array_unshift($this->segments, NULL);
  289. array_unshift($this->rsegments, NULL);
  290. unset($this->segments[0]);
  291. unset($this->rsegments[0]);
  292. }
  293. // --------------------------------------------------------------------
  294. /**
  295. * Fetch a URI Segment
  296. *
  297. * This function returns the URI segment based on the number provided.
  298. *
  299. * @access public
  300. * @param integer
  301. * @param bool
  302. * @return string
  303. */
  304. function segment($n, $no_result = FALSE)
  305. {
  306. return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
  307. }
  308. // --------------------------------------------------------------------
  309. /**
  310. * Fetch a URI "routed" Segment
  311. *
  312. * This function returns the re-routed URI segment (assuming routing rules are used)
  313. * based on the number provided. If there is no routing this function returns the
  314. * same result as $this->segment()
  315. *
  316. * @access public
  317. * @param integer
  318. * @param bool
  319. * @return string
  320. */
  321. function rsegment($n, $no_result = FALSE)
  322. {
  323. return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
  324. }
  325. // --------------------------------------------------------------------
  326. /**
  327. * Generate a key value pair from the URI string
  328. *
  329. * This function generates and associative array of URI data starting
  330. * at the supplied segment. For example, if this is your URI:
  331. *
  332. * example.com/user/search/name/joe/location/UK/gender/male
  333. *
  334. * You can use this function to generate an array with this prototype:
  335. *
  336. * array (
  337. * name => joe
  338. * location => UK
  339. * gender => male
  340. * )
  341. *
  342. * @access public
  343. * @param integer the starting segment number
  344. * @param array an array of default values
  345. * @return array
  346. */
  347. function uri_to_assoc($n = 3, $default = array())
  348. {
  349. return $this->_uri_to_assoc($n, $default, 'segment');
  350. }
  351. /**
  352. * Identical to above only it uses the re-routed segment array
  353. *
  354. * @access public
  355. * @param integer the starting segment number
  356. * @param array an array of default values
  357. * @return array
  358. *
  359. */
  360. function ruri_to_assoc($n = 3, $default = array())
  361. {
  362. return $this->_uri_to_assoc($n, $default, 'rsegment');
  363. }
  364. // --------------------------------------------------------------------
  365. /**
  366. * Generate a key value pair from the URI string or Re-routed URI string
  367. *
  368. * @access private
  369. * @param integer the starting segment number
  370. * @param array an array of default values
  371. * @param string which array we should use
  372. * @return array
  373. */
  374. function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
  375. {
  376. if ($which == 'segment')
  377. {
  378. $total_segments = 'total_segments';
  379. $segment_array = 'segment_array';
  380. }
  381. else
  382. {
  383. $total_segments = 'total_rsegments';
  384. $segment_array = 'rsegment_array';
  385. }
  386. if ( ! is_numeric($n))
  387. {
  388. return $default;
  389. }
  390. if (isset($this->keyval[$n]))
  391. {
  392. return $this->keyval[$n];
  393. }
  394. if ($this->$total_segments() < $n)
  395. {
  396. if (count($default) == 0)
  397. {
  398. return array();
  399. }
  400. $retval = array();
  401. foreach ($default as $val)
  402. {
  403. $retval[$val] = FALSE;
  404. }
  405. return $retval;
  406. }
  407. $segments = array_slice($this->$segment_array(), ($n - 1));
  408. $i = 0;
  409. $lastval = '';
  410. $retval = array();
  411. foreach ($segments as $seg)
  412. {
  413. if ($i % 2)
  414. {
  415. $retval[$lastval] = $seg;
  416. }
  417. else
  418. {
  419. $retval[$seg] = FALSE;
  420. $lastval = $seg;
  421. }
  422. $i++;
  423. }
  424. if (count($default) > 0)
  425. {
  426. foreach ($default as $val)
  427. {
  428. if ( ! array_key_exists($val, $retval))
  429. {
  430. $retval[$val] = FALSE;
  431. }
  432. }
  433. }
  434. // Cache the array for reuse
  435. $this->keyval[$n] = $retval;
  436. return $retval;
  437. }
  438. // --------------------------------------------------------------------
  439. /**
  440. * Generate a URI string from an associative array
  441. *
  442. *
  443. * @access public
  444. * @param array an associative array of key/values
  445. * @return array
  446. */
  447. function assoc_to_uri($array)
  448. {
  449. $temp = array();
  450. foreach ((array)$array as $key => $val)
  451. {
  452. $temp[] = $key;
  453. $temp[] = $val;
  454. }
  455. return implode('/', $temp);
  456. }
  457. // --------------------------------------------------------------------
  458. /**
  459. * Fetch a URI Segment and add a trailing slash
  460. *
  461. * @access public
  462. * @param integer
  463. * @param string
  464. * @return string
  465. */
  466. function slash_segment($n, $where = 'trailing')
  467. {
  468. return $this->_slash_segment($n, $where, 'segment');
  469. }
  470. // --------------------------------------------------------------------
  471. /**
  472. * Fetch a URI Segment and add a trailing slash
  473. *
  474. * @access public
  475. * @param integer
  476. * @param string
  477. * @return string
  478. */
  479. function slash_rsegment($n, $where = 'trailing')
  480. {
  481. return $this->_slash_segment($n, $where, 'rsegment');
  482. }
  483. // --------------------------------------------------------------------
  484. /**
  485. * Fetch a URI Segment and add a trailing slash - helper function
  486. *
  487. * @access private
  488. * @param integer
  489. * @param string
  490. * @param string
  491. * @return string
  492. */
  493. function _slash_segment($n, $where = 'trailing', $which = 'segment')
  494. {
  495. $leading = '/';
  496. $trailing = '/';
  497. if ($where == 'trailing')
  498. {
  499. $leading = '';
  500. }
  501. elseif ($where == 'leading')
  502. {
  503. $trailing = '';
  504. }
  505. return $leading.$this->$which($n).$trailing;
  506. }
  507. // --------------------------------------------------------------------
  508. /**
  509. * Segment Array
  510. *
  511. * @access public
  512. * @return array
  513. */
  514. function segment_array()
  515. {
  516. return $this->segments;
  517. }
  518. // --------------------------------------------------------------------
  519. /**
  520. * Routed Segment Array
  521. *
  522. * @access public
  523. * @return array
  524. */
  525. function rsegment_array()
  526. {
  527. return $this->rsegments;
  528. }
  529. // --------------------------------------------------------------------
  530. /**
  531. * Total number of segments
  532. *
  533. * @access public
  534. * @return integer
  535. */
  536. function total_segments()
  537. {
  538. return count($this->segments);
  539. }
  540. // --------------------------------------------------------------------
  541. /**
  542. * Total number of routed segments
  543. *
  544. * @access public
  545. * @return integer
  546. */
  547. function total_rsegments()
  548. {
  549. return count($this->rsegments);
  550. }
  551. // --------------------------------------------------------------------
  552. /**
  553. * Fetch the entire URI string
  554. *
  555. * @access public
  556. * @return string
  557. */
  558. function uri_string()
  559. {
  560. return $this->uri_string;
  561. }
  562. // --------------------------------------------------------------------
  563. /**
  564. * Fetch the entire Re-routed URI string
  565. *
  566. * @access public
  567. * @return string
  568. */
  569. function ruri_string()
  570. {
  571. return '/'.implode('/', $this->rsegment_array());
  572. }
  573. }
  574. // END URI Class
  575. /* End of file URI.php */
  576. /* Location: ./system/core/URI.php */