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