PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Http/Url.php

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