PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Nette/Web/Uri.php

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