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