PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Nette/Web/Uri.php

https://github.com/DocX/nette
PHP | 483 lines | 198 code | 108 blank | 177 comment | 19 complexity | 14924aadb3f58570ffd8ec7ec5790be8 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license" that is bundled
  8. * with this package in the file license.txt.
  9. *
  10. * For more information please see http://nettephp.com
  11. *
  12. * @copyright Copyright (c) 2004, 2009 David Grudl
  13. * @license http://nettephp.com/license Nette license
  14. * @link http://nettephp.com
  15. * @category Nette
  16. * @package Nette\Web
  17. */
  18. /*namespace Nette\Web;*/
  19. require_once dirname(__FILE__) . '/../FreezableObject.php';
  20. /**
  21. * URI Syntax (RFC 3986).
  22. *
  23. * <pre>
  24. * http://user:password@nettephp.com:8042/en/manual.html?name=param#fragment
  25. * \__/^^^\_____________________________/\_____________/^\________/^\______/
  26. * | | | | |
  27. * scheme authority path query fragment
  28. * </pre>
  29. *
  30. * - authority: [user[:password]@]host[:port]
  31. * - hostUri: http://user:password@nettephp.com:8042
  32. *
  33. * @author David Grudl
  34. * @copyright Copyright (c) 2004, 2009 David Grudl
  35. * @package Nette\Web
  36. *
  37. * @property string $scheme
  38. * @property string $user
  39. * @property string $password
  40. * @property string $host
  41. * @property string $port
  42. * @property string $path
  43. * @property string $query
  44. * @property string $fragment
  45. * @property-read string $absoluteUri
  46. * @property-read string $authority
  47. * @property-read string $hostUri
  48. */
  49. class Uri extends /*Nette\*/FreezableObject
  50. {
  51. /** @var array */
  52. public static $defaultPorts = array(
  53. 'http' => 80,
  54. 'https' => 443,
  55. 'ftp' => 21,
  56. 'news' => 119,
  57. 'nntp' => 119,
  58. );
  59. /** @var string */
  60. private $scheme = '';
  61. /** @var string */
  62. private $user = '';
  63. /** @var string */
  64. private $pass = '';
  65. /** @var string */
  66. private $host = '';
  67. /** @var int */
  68. private $port = NULL;
  69. /** @var string */
  70. private $path = '';
  71. /** @var string */
  72. private $query = '';
  73. /** @var string */
  74. private $fragment = '';
  75. /**
  76. * @param string URL
  77. * @throws InvalidArgumentException
  78. */
  79. public function __construct($uri = NULL)
  80. {
  81. if (is_string($uri)) {
  82. $parts = @parse_url($uri); // intentionally @
  83. if ($parts === FALSE) {
  84. throw new /*\*/InvalidArgumentException("Malformed or unsupported URI '$uri'.");
  85. }
  86. foreach ($parts as $key => $val) {
  87. $this->$key = $val;
  88. }
  89. if (!$this->port && isset(self::$defaultPorts[$this->scheme])) {
  90. $this->port = self::$defaultPorts[$this->scheme];
  91. }
  92. } elseif ($uri instanceof self) {
  93. foreach ($uri as $key => $val) {
  94. $this->$key = $val;
  95. }
  96. }
  97. }
  98. /**
  99. * Sets the scheme part of URI.
  100. * @param string
  101. * @return Uri provides a fluent interface
  102. */
  103. public function setScheme($value)
  104. {
  105. $this->updating();
  106. $this->scheme = (string) $value;
  107. return $this;
  108. }
  109. /**
  110. * Returns the scheme part of URI.
  111. * @return string
  112. */
  113. public function getScheme()
  114. {
  115. return $this->scheme;
  116. }
  117. /**
  118. * Sets the user name part of URI.
  119. * @param string
  120. * @return Uri provides a fluent interface
  121. */
  122. public function setUser($value)
  123. {
  124. $this->updating();
  125. $this->user = (string) $value;
  126. return $this;
  127. }
  128. /**
  129. * Returns the user name part of URI.
  130. * @return string
  131. */
  132. public function getUser()
  133. {
  134. return $this->user;
  135. }
  136. /**
  137. * Sets the password part of URI.
  138. * @param string
  139. * @return Uri provides a fluent interface
  140. */
  141. public function setPassword($value)
  142. {
  143. $this->updating();
  144. $this->pass = (string) $value;
  145. return $this;
  146. }
  147. /**
  148. * Returns the password part of URI.
  149. * @return string
  150. */
  151. public function getPassword()
  152. {
  153. return $this->pass;
  154. }
  155. /**
  156. * @deprecated
  157. */
  158. public function setPass($value)
  159. {
  160. $this->setPassword($value);
  161. }
  162. /**
  163. * @deprecated
  164. */
  165. public function getPass()
  166. {
  167. return $this->pass;
  168. }
  169. /**
  170. * Sets the host part of URI.
  171. * @param string
  172. * @return Uri provides a fluent interface
  173. */
  174. public function setHost($value)
  175. {
  176. $this->updating();
  177. $this->host = (string) $value;
  178. return $this;
  179. }
  180. /**
  181. * Returns the host part of URI.
  182. * @return string
  183. */
  184. public function getHost()
  185. {
  186. return $this->host;
  187. }
  188. /**
  189. * Sets the port part of URI.
  190. * @param string
  191. * @return Uri provides a fluent interface
  192. */
  193. public function setPort($value)
  194. {
  195. $this->updating();
  196. $this->port = (int) $value;
  197. return $this;
  198. }
  199. /**
  200. * Returns the port part of URI.
  201. * @return string
  202. */
  203. public function getPort()
  204. {
  205. return $this->port;
  206. }
  207. /**
  208. * Sets the path part of URI.
  209. * @param string
  210. * @return Uri provides a fluent interface
  211. */
  212. public function setPath($value)
  213. {
  214. $this->updating();
  215. $this->path = (string) $value;
  216. return $this;
  217. }
  218. /**
  219. * Returns the path part of URI.
  220. * @return string
  221. */
  222. public function getPath()
  223. {
  224. return $this->path;
  225. }
  226. /**
  227. * Sets the query part of URI.
  228. * @param string|array
  229. * @return Uri provides a fluent interface
  230. */
  231. public function setQuery($value)
  232. {
  233. $this->updating();
  234. $this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
  235. return $this;
  236. }
  237. /**
  238. * Appends the query part of URI.
  239. * @param string|array
  240. * @return void
  241. */
  242. public function appendQuery($value)
  243. {
  244. $this->updating();
  245. $value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
  246. $this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value;
  247. }
  248. /**
  249. * Returns the query part of URI.
  250. * @return string
  251. */
  252. public function getQuery()
  253. {
  254. return $this->query;
  255. }
  256. /**
  257. * Sets the fragment part of URI.
  258. * @param string
  259. * @return Uri provides a fluent interface
  260. */
  261. public function setFragment($value)
  262. {
  263. $this->updating();
  264. $this->fragment = (string) $value;
  265. return $this;
  266. }
  267. /**
  268. * Returns the fragment part of URI.
  269. * @return string
  270. */
  271. public function getFragment()
  272. {
  273. return $this->fragment;
  274. }
  275. /**
  276. * Returns the entire URI including query string and fragment.
  277. * @return string
  278. */
  279. public function getAbsoluteUri()
  280. {
  281. return $this->scheme . '://' . $this->getAuthority() . $this->path
  282. . ($this->query === '' ? '' : '?' . $this->query)
  283. . ($this->fragment === '' ? '' : '#' . $this->fragment);
  284. }
  285. /**
  286. * Returns the [user[:pass]@]host[:port] part of URI.
  287. * @return string
  288. */
  289. public function getAuthority()
  290. {
  291. $authority = $this->host;
  292. if ($this->port && isset(self::$defaultPorts[$this->scheme]) && $this->port !== self::$defaultPorts[$this->scheme]) {
  293. $authority .= ':' . $this->port;
  294. }
  295. if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') {
  296. $authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority;
  297. }
  298. return $authority;
  299. }
  300. /**
  301. * Returns the scheme and authority part of URI.
  302. * @return string
  303. */
  304. public function getHostUri()
  305. {
  306. return $this->scheme . '://' . $this->getAuthority();
  307. }
  308. /**
  309. * URI comparsion (this object must be in canonical form).
  310. * @param string
  311. * @return bool
  312. */
  313. public function isEqual($uri)
  314. {
  315. // compare host + path
  316. $part = self::unescape(strtok($uri, '?#'), '%/');
  317. if (strncmp($part, '//', 2) === 0) { // absolute URI without scheme
  318. if ($part !== '//' . $this->getAuthority() . $this->path) return FALSE;
  319. } elseif (strncmp($part, '/', 1) === 0) { // absolute path
  320. if ($part !== $this->path) return FALSE;
  321. } else {
  322. if ($part !== $this->scheme . '://' . $this->getAuthority() . $this->path) return FALSE;
  323. }
  324. // compare query strings
  325. $part = (string) strtok('?#');
  326. if ($part !== '') {
  327. $tmp = preg_split('#[&;]#', self::unescape(strtr($part, '+', ' '), '%&;=+'));
  328. sort($tmp);
  329. $part = implode('&', $tmp);
  330. }
  331. return $part === $this->query;
  332. }
  333. /**
  334. * Transform to canonical form.
  335. * @return void
  336. */
  337. public function canonicalize()
  338. {
  339. $this->updating();
  340. $this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/');
  341. $this->host = strtolower(rawurldecode($this->host));
  342. if ($this->query !== '') {
  343. $tmp = preg_split('#[&;]#', self::unescape(strtr($this->query, '+', ' '), '%&;=+'));
  344. sort($tmp);
  345. $this->query = implode('&', $tmp);
  346. }
  347. }
  348. /**
  349. * @return string
  350. */
  351. public function __toString()
  352. {
  353. return $this->getAbsoluteUri();
  354. }
  355. /**
  356. * Similar to rawurldecode, but preserve reserved chars encoded.
  357. * @param string to decode
  358. * @param string reserved characters
  359. * @return string
  360. */
  361. public static function unescape($s, $reserved = '%;/?:@&=+$,')
  362. {
  363. // reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
  364. // within a path segment, the characters "/", ";", "=", "?" are reserved
  365. // within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved.
  366. preg_match_all('#(?<=%)[a-f0-9][a-f0-9]#i', $s, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
  367. foreach (array_reverse($matches) as $match) {
  368. $ch = chr(hexdec($match[0][0]));
  369. if (strpos($reserved, $ch) === FALSE) {
  370. $s = substr_replace($s, $ch, $match[0][1] - 1, 3);
  371. }
  372. }
  373. return $s;
  374. }
  375. }