1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Http;
13:
14: use Nette;
15:
16:
17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:
53: class Url extends Nette\FreezableObject
54: {
55:
56: public static $defaultPorts = array(
57: 'http' => 80,
58: 'https' => 443,
59: 'ftp' => 21,
60: 'news' => 119,
61: 'nntp' => 119,
62: );
63:
64:
65: private $scheme = '';
66:
67:
68: private $user = '';
69:
70:
71: private $pass = '';
72:
73:
74: private $host = '';
75:
76:
77: private $port = NULL;
78:
79:
80: private $path = '';
81:
82:
83: private $query = '';
84:
85:
86: private $fragment = '';
87:
88:
89: 90: 91: 92:
93: public function __construct($url = NULL)
94: {
95: if (is_string($url)) {
96: $parts = @parse_url($url);
97: if ($parts === FALSE) {
98: throw new Nette\InvalidArgumentException("Malformed or unsupported URI '$url'.");
99: }
100:
101: foreach ($parts as $key => $val) {
102: $this->$key = $val;
103: }
104:
105: if (!$this->port && isset(self::$defaultPorts[$this->scheme])) {
106: $this->port = self::$defaultPorts[$this->scheme];
107: }
108:
109: if ($this->path === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
110: $this->path = '/';
111: }
112:
113: } elseif ($url instanceof self) {
114: foreach ($this as $key => $val) {
115: $this->$key = $url->$key;
116: }
117: }
118: }
119:
120:
121: 122: 123: 124: 125:
126: public function setScheme($value)
127: {
128: $this->updating();
129: $this->scheme = (string) $value;
130: return $this;
131: }
132:
133:
134: 135: 136: 137:
138: public function getScheme()
139: {
140: return $this->scheme;
141: }
142:
143:
144: 145: 146: 147: 148:
149: public function setUser($value)
150: {
151: $this->updating();
152: $this->user = (string) $value;
153: return $this;
154: }
155:
156:
157: 158: 159: 160:
161: public function getUser()
162: {
163: return $this->user;
164: }
165:
166:
167: 168: 169: 170: 171:
172: public function setPassword($value)
173: {
174: $this->updating();
175: $this->pass = (string) $value;
176: return $this;
177: }
178:
179:
180: 181: 182: 183:
184: public function getPassword()
185: {
186: return $this->pass;
187: }
188:
189:
190: 191: 192: 193: 194:
195: public function setHost($value)
196: {
197: $this->updating();
198: $this->host = (string) $value;
199: return $this;
200: }
201:
202:
203: 204: 205: 206:
207: public function getHost()
208: {
209: return $this->host;
210: }
211:
212:
213: 214: 215: 216: 217:
218: public function setPort($value)
219: {
220: $this->updating();
221: $this->port = (int) $value;
222: return $this;
223: }
224:
225:
226: 227: 228: 229:
230: public function getPort()
231: {
232: return $this->port;
233: }
234:
235:
236: 237: 238: 239: 240:
241: public function setPath($value)
242: {
243: $this->updating();
244: $this->path = (string) $value;
245: return $this;
246: }
247:
248:
249: 250: 251: 252:
253: public function getPath()
254: {
255: return $this->path;
256: }
257:
258:
259: 260: 261: 262: 263:
264: public function setQuery($value)
265: {
266: $this->updating();
267: $this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
268: return $this;
269: }
270:
271:
272: 273: 274: 275: 276:
277: public function appendQuery($value)
278: {
279: $this->updating();
280: $value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
281: $this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value;
282: }
283:
284:
285: 286: 287: 288:
289: public function getQuery()
290: {
291: return $this->query;
292: }
293:
294:
295: 296: 297: 298: 299:
300: public function setFragment($value)
301: {
302: $this->updating();
303: $this->fragment = (string) $value;
304: return $this;
305: }
306:
307:
308: 309: 310: 311:
312: public function getFragment()
313: {
314: return $this->fragment;
315: }
316:
317:
318: 319: 320: 321:
322: public function getAbsoluteUrl()
323: {
324: return $this->getHostUrl() . $this->path
325: . ($this->query === '' ? '' : '?' . $this->query)
326: . ($this->fragment === '' ? '' : '#' . $this->fragment);
327: }
328:
329:
330: 331: 332: 333:
334: public function getAuthority()
335: {
336: $authority = $this->host;
337: if ($this->port && (!isset(self::$defaultPorts[$this->scheme]) || $this->port !== self::$defaultPorts[$this->scheme])) {
338: $authority .= ':' . $this->port;
339: }
340:
341: if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') {
342: $authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority;
343: }
344:
345: return $authority;
346: }
347:
348:
349: 350: 351: 352:
353: public function getHostUrl()
354: {
355: return ($this->scheme ? $this->scheme . ':' : '') . '//' . $this->getAuthority();
356: }
357:
358:
359: 360: 361: 362:
363: public function getBasePath()
364: {
365: $pos = strrpos($this->path, '/');
366: return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1);
367: }
368:
369:
370: 371: 372: 373:
374: public function getBaseUrl()
375: {
376: return $this->getHostUrl() . $this->getBasePath();
377: }
378:
379:
380: 381: 382: 383:
384: public function getRelativeUrl()
385: {
386: return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl()));
387: }
388:
389:
390: 391: 392: 393: 394:
395: public function isEqual($url)
396: {
397:
398: $part = self::unescape(strtok($url, '?#'), '%/');
399: if (strncmp($part, '//', 2) === 0) {
400: if ($part !== '//' . $this->getAuthority() . $this->path) {
401: return FALSE;
402: }
403:
404: } elseif (strncmp($part, '/', 1) === 0) {
405: if ($part !== $this->path) {
406: return FALSE;
407: }
408:
409: } else {
410: if ($part !== $this->getHostUrl() . $this->path) {
411: return FALSE;
412: }
413: }
414:
415:
416: $part = preg_split('#[&;]#', self::unescape(strtr((string) strtok('?#'), '+', ' '), '%&;=+'));
417: sort($part);
418: $query = preg_split('#[&;]#', $this->query);
419: sort($query);
420: return $part === $query;
421: }
422:
423:
424: 425: 426: 427:
428: public function canonicalize()
429: {
430: $this->updating();
431: $this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/');
432: $this->host = strtolower(rawurldecode($this->host));
433: $this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+');
434: }
435:
436:
437: 438: 439:
440: public function __toString()
441: {
442: return $this->getAbsoluteUrl();
443: }
444:
445:
446: 447: 448: 449: 450: 451:
452: public static function unescape($s, $reserved = '%;/?:@&=+$,')
453: {
454:
455:
456:
457: preg_match_all('#(?<=%)[a-f0-9][a-f0-9]#i', $s, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
458: foreach (array_reverse($matches) as $match) {
459: $ch = chr(hexdec($match[0][0]));
460: if (strpos($reserved, $ch) === FALSE) {
461: $s = substr_replace($s, $ch, $match[0][1] - 1, 3);
462: }
463: }
464: return $s;
465: }
466:
467:
468:
469: function getRelativeUri()
470: {
471: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getRelativeUrl() instead.', E_USER_WARNING);
472: return $this->getRelativeUrl();
473: }
474:
475:
476: function getAbsoluteUri()
477: {
478: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getAbsoluteUrl() instead.', E_USER_WARNING);
479: return $this->getAbsoluteUrl();
480: }
481:
482:
483: function getHostUri()
484: {
485: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getHostUrl() instead.', E_USER_WARNING);
486: return $this->getHostUrl();
487: }
488:
489:
490: function getBaseUri()
491: {
492: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getBaseUrl() instead.', E_USER_WARNING);
493: return $this->getBaseUrl();
494: }
495:
496: }
497: