PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/system/core/URI.php

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