PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Test/Case/Library/Uri.php

http://github.com/CakeDC/oauth_lib
PHP | 475 lines | 268 code | 31 blank | 176 comment | 61 complexity | 7d0ddbf8a289f880fae34f2c6e426bbe MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright 2010, Cake Development Corporation (http://cakedc.com)
  4. *
  5. * Licensed under The MIT License
  6. * Redistributions of files must retain the above copyright notice.
  7. *
  8. * @copyright Copyright 2010, Cake Development Corporation (http://cakedc.com)
  9. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  10. */
  11. /**
  12. * Parses a URI, provides details and a full correctly composed URI, processes a relative URI redirect
  13. *
  14. * @package oauth_lib
  15. * @subpackage oauth_lib.tests.uri
  16. */
  17. class URI {
  18. /**
  19. * Path type REL, FULL
  20. *
  21. * @var string
  22. */
  23. public $type;
  24. /**
  25. * URI schema part
  26. *
  27. * @var string
  28. */
  29. public $scheme;
  30. /**
  31. * Host name
  32. *
  33. * @var string
  34. */
  35. public $host;
  36. /**
  37. * User (for basic HTTP authentication)
  38. *
  39. * @var string
  40. */
  41. public $user;
  42. /**
  43. * Password (for basic HTTP authentication)
  44. *
  45. * @var string
  46. */
  47. public $pass;
  48. /**
  49. * Path part
  50. *
  51. * @var string
  52. */
  53. public $path;
  54. /**
  55. * Directory part of the path (not part of RFC)
  56. *
  57. * @var string
  58. */
  59. public $dir;
  60. /**
  61. * Query string
  62. *
  63. * @var string
  64. */
  65. public $query;
  66. /**
  67. * Port to connect to (if not 80)
  68. *
  69. * @var string
  70. */
  71. public $port;
  72. /**
  73. * Port part including preceeding semicolon
  74. *
  75. * @var string
  76. */
  77. public $port_string;
  78. /**
  79. * The anchor (after #)
  80. *
  81. * @var string
  82. */
  83. public $fragment;
  84. /**
  85. * Full correct URI in the RFC-compliant format
  86. *
  87. * @var string
  88. */
  89. public $full;
  90. /**
  91. * Flag defines whether to process "/../", "/./", "//" and convert backslash to slashes
  92. *
  93. * @var bool
  94. */
  95. public $normalize_path = false;
  96. /**
  97. * Symbol used to mark a start of a query
  98. *
  99. */
  100. public $QUERY_DELIMITER = '?';
  101. /**
  102. * regex
  103. *
  104. * @param string $name
  105. * @return mixed
  106. */
  107. static function regex($name) {
  108. $patternAlpha = "a-zA-Z";
  109. $patternAlnum = "" . $patternAlpha . "\\d";
  110. $patternHex = "a-fA-F\\d";
  111. $patternEscaped = "%[" . $patternHex . "]{2}";
  112. $patternUnreserved = "-_.!~*'()" . $patternAlnum . "";
  113. $patternReserved = ";\/?:@&=+$,\\[\\]";
  114. $Escaped = '/' . $patternEscaped . '/';
  115. $Unsafe = "/[^" . $patternUnreserved . $patternReserved . "]/";
  116. if (isset(${$name})) {
  117. return ${$name};
  118. }
  119. return false;
  120. }
  121. /**
  122. * Constructor
  123. *
  124. * @param string $new_uri
  125. */
  126. public function __construct($new_uri = null) {
  127. if (!empty($new_uri)) {
  128. $this->process($new_uri);
  129. }
  130. }
  131. /**
  132. * Parse the URI, return false on error
  133. *
  134. * @param string $new_uri
  135. * @return bool
  136. */
  137. public function process($new_uri) {
  138. $this->type = 'FULL';
  139. //init variables, results of parse_url() may redefine them
  140. $this->scheme = '';
  141. $this->host = '';
  142. $this->user = '';
  143. $this->pass = '';
  144. $this->path = '';
  145. $this->dir = '';
  146. $this->query = '';
  147. $this->fragment = '';
  148. $this->full = '';
  149. $this->port = 80;
  150. $this->port_string = ':80';
  151. if (strpos($new_uri, '://') === false) {
  152. $local_uri = 'http://sample.com' . $new_uri;
  153. $new_uri = 'http://' . $new_uri;
  154. }
  155. $uri_array = @parse_url($new_uri);
  156. if (!$uri_array) {
  157. $uri_array = @parse_url($local_uri);
  158. if (!$uri_array) {
  159. return false;
  160. } else {
  161. $this->type = 'REL';
  162. $uri_array['host'] = '';
  163. }
  164. }
  165. if (empty($uri_array['scheme'])) {
  166. $uri_array['scheme'] = 'http://';
  167. } else {
  168. $uri_array['scheme'] = $uri_array['scheme'] . '://';
  169. }
  170. if (!empty($uri_array['user'])) {
  171. if (!empty($uri_array['pass'])) {
  172. $uri_array['pass'] = ':' . $uri_array['pass'] . '@';
  173. } else {
  174. $uri_array['user'] .= '@';
  175. $uri_array['pass'] = '';
  176. }
  177. } else {
  178. $uri_array['user'] = $uri_array['pass'] = '';
  179. }
  180. if (!empty($uri_array['port'])) {
  181. $uri_array['port_string'] = ':' . $uri_array['port'];
  182. } else {
  183. $uri_array['port'] = 80;
  184. $uri_array['port_string'] = '';
  185. }
  186. if (empty($uri_array['path']) || !trim($uri_array['path'])) {
  187. $uri_array['path'] = '/';
  188. }
  189. $uri_array['dir'] = $this->dirname($uri_array['path']);
  190. if (empty($uri_array['query'])) {
  191. $uri_array['query'] = '';
  192. } else {
  193. $uri_array['query'] = '?' . $uri_array['query'];
  194. }
  195. if (empty($uri_array['fragment'])) {
  196. $uri_array['fragment'] = '';
  197. } else {
  198. $uri_array['fragment'] = '#' . $uri_array['fragment'];
  199. }
  200. foreach($uri_array as $key => $value) {
  201. $this->$key = $value;
  202. }
  203. $this->get_full_uri();
  204. return true;
  205. }
  206. /**
  207. * Processes a new URI using details of a previous one
  208. *
  209. * @param string $new_url
  210. * @return bool
  211. */
  212. public function parse_http_redirect($new_url) {
  213. if (!$new_url || !is_string($new_url)) {
  214. return false;
  215. }
  216. if ($method_pos = strpos($new_url, '://')) {
  217. $method = substr($new_url, 0, $method_pos);
  218. if (!strcasecmp($method, 'http') || !strcasecmp($method, 'https')) {
  219. return $this->process($new_url);
  220. } else {
  221. return false;
  222. }
  223. }
  224. $param_pos = strpos($new_url, $this->QUERY_DELIMITER);
  225. if ($param_pos !== false) {
  226. $new_query = substr($new_url, $param_pos);
  227. $new_path = $param_pos ? substr($new_url, 0, $param_pos) : '';
  228. } else {
  229. $new_path = $new_url;
  230. $new_query = '';
  231. }
  232. if ($new_url[0] != '/') {
  233. $new_path = $this->dirname($this->path) . '/' . $new_path;
  234. }
  235. if ($this->normalize_path) {
  236. $new_path = preg_replace('~((\\\\+)|/){2,}~', '/', $new_path);
  237. if (strpos($new_path, '/../') !== false) {
  238. $path_array = explode('/', $new_path);
  239. foreach($path_array as $key => $value) {
  240. if ($value == '..') {
  241. if ($key>2) {
  242. unset($path_array[$key-1]);
  243. }
  244. unset($path_array[$key]);
  245. }
  246. }
  247. $new_path = implode('/', $path_array);
  248. }
  249. $new_path = str_replace('/./', '/', $new_path);
  250. }
  251. $this->path = $new_path;
  252. $this->query = $new_query;
  253. $this->get_full_uri();
  254. return true;
  255. }
  256. /**
  257. * Returns the directory part of the path (path parameter may include query string)
  258. *
  259. * @param string $path
  260. * @return string
  261. */
  262. public function dirname($path) {
  263. if (!$path) {
  264. return false;
  265. }
  266. $i = strpos($path, '?');
  267. $dir = $i ? substr($path, 0, $i) : $path;
  268. $i = strrpos($dir, '/');
  269. $dir = $i ? substr($dir, 0, $i) : '/';
  270. if (!($dir[0] == '/')) {
  271. $dir = '/' . $dir;
  272. }
  273. return $dir;
  274. }
  275. /**
  276. * (re)compile the full uri and return the string
  277. *
  278. * @return string
  279. */
  280. public function get_full_uri() {
  281. if ($this->type == 'REL') {
  282. $this->full = $this->localPath();
  283. }
  284. if ($this->type == 'FULL') {
  285. $this->full = $this->fullPath();
  286. }
  287. return $this->full;
  288. }
  289. /**
  290. * to string
  291. *
  292. * @return string
  293. */
  294. public function toString() {
  295. return $this->get_full_uri();
  296. }
  297. /**
  298. * to string
  299. *
  300. * @return void
  301. */
  302. public function __toString() {
  303. return $this->get_full_uri();
  304. }
  305. /**
  306. * full path
  307. *
  308. * @return string
  309. */
  310. public function fullPath() {
  311. $this->full = $this->scheme . $this->user . $this->pass . $this->host . $this->port_string . $this->path . $this->queryWithQ();
  312. return $this->full;
  313. }
  314. /**
  315. * server path
  316. *
  317. * @return string
  318. */
  319. public function serverPath() {
  320. $this->full = $this->scheme . $this->user . $this->pass . $this->host . $this->port_string . $this->path;
  321. return $this->server_path;
  322. }
  323. /**
  324. * local path
  325. *
  326. * @return string
  327. */
  328. public function localPath() {
  329. if ((substr($this->query, 0, 1) != '?') && strlen($this->query)>0) {
  330. $this->query = '?' . $this->query;
  331. }
  332. $this->local_path = $this->path . $this->query;
  333. return $this->local_path;
  334. }
  335. /**
  336. * path
  337. *
  338. * @return string
  339. */
  340. public function path() {
  341. if ($this->type == 'REL') {
  342. return $this->localPath();
  343. }
  344. if ($this->type == 'FULL') {
  345. return $this->serverPath();
  346. }
  347. return false;
  348. }
  349. /**
  350. * Checks if the requested host exists
  351. *
  352. * @return bool
  353. */
  354. public function checkHost() {
  355. if (!$this->host) {
  356. throw new URLException(120);
  357. }
  358. $regexp = '/^\d{2,3}(\.\d{1,3}){3}$/';
  359. if (!checkdnsrr($this->host, 'A') && !preg_match($regexp, $this->host)) {
  360. throw new URLException(120);
  361. }
  362. }
  363. /**
  364. * Unescapes the string.
  365. *
  366. * $enc_uri = URI::escape("http://example.com/?a=\11\15");
  367. * echo $enc_uri;
  368. * : "http://example.com/?a=%09%0D";
  369. *
  370. * echo URI::unescape($enc_uri);
  371. * echo URI::unescape("http://example.com/?a=%09%0D");
  372. * : "http://example.com/?a=\t\r"
  373. */
  374. public function unescape($str) {
  375. $escaped = URI::regex('Escaped');
  376. if (preg_match_all($escaped, $str, $matches) && count($matches) >0) {
  377. $str = preg_replace_callback($escaped, create_function('$matches', 'return chr(hexdec(substr($matches[0], 1)));'), $str);
  378. }
  379. return $str;
  380. }
  381. /**
  382. * Escapes the string, replacing all unsafe characters with codes.
  383. *
  384. * @param string $str, string to replaces in.
  385. * @param string $unsafe, Regexp that matches all symbols that must be replaced with codes.
  386. */
  387. public function escape($str, $unsafe = null) {
  388. if (!isset($unsafe)) {
  389. $unsafe = URI::regex('Unsafe');
  390. } else {
  391. $unsafe = "/" . $unsafe . "/";
  392. }
  393. if (preg_match_all($unsafe, $str, $matches) && count($matches) >0) {
  394. $str = preg_replace_callback($unsafe, create_function('$matches', '
  395. $replace = $matches[0];
  396. $result = "";
  397. for($i=0; $i<strlen($replace); $i++) {
  398. $result .= sprintf(\'%%%02X\', ord($replace[$i]));
  399. }
  400. return $result;'), $str);
  401. }
  402. return $str;
  403. }
  404. /**
  405. * Set host
  406. *
  407. * @param string $host
  408. * @param string $port
  409. * @return void
  410. */
  411. public function setHost($host, $port) {
  412. $this->host = $host;
  413. $this->port = $port;
  414. $this->type = 'FULL';
  415. }
  416. /**
  417. * query
  418. *
  419. * @return mixed
  420. */
  421. public function query() {
  422. if (substr($this->query, 0, 1) == '?') {
  423. return substr($this->query, 1);
  424. }
  425. return $this->query;
  426. }
  427. /**
  428. * queryWithQ
  429. *
  430. * @return string
  431. */
  432. public function queryWithQ() {
  433. if (strlen($this->query)>0) {
  434. if (substr($this->query, 0, 1) == '?') {
  435. return $this->query;
  436. }
  437. return $this->QUERY_DELIMITER . $this->query;
  438. } else {
  439. return "";
  440. }
  441. }
  442. }