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