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\Object
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->scheme = (string) $value;
125: return $this;
126: }
127:
128:
129: 130: 131: 132:
133: public function getScheme()
134: {
135: return $this->scheme;
136: }
137:
138:
139: 140: 141: 142: 143:
144: public function setUser($value)
145: {
146: $this->user = (string) $value;
147: return $this;
148: }
149:
150:
151: 152: 153: 154:
155: public function getUser()
156: {
157: return $this->user;
158: }
159:
160:
161: 162: 163: 164: 165:
166: public function setPassword($value)
167: {
168: $this->pass = (string) $value;
169: return $this;
170: }
171:
172:
173: 174: 175: 176:
177: public function getPassword()
178: {
179: return $this->pass;
180: }
181:
182:
183: 184: 185: 186: 187:
188: public function setHost($value)
189: {
190: $this->host = (string) $value;
191: return $this;
192: }
193:
194:
195: 196: 197: 198:
199: public function getHost()
200: {
201: return $this->host;
202: }
203:
204:
205: 206: 207: 208: 209:
210: public function setPort($value)
211: {
212: $this->port = (int) $value;
213: return $this;
214: }
215:
216:
217: 218: 219: 220:
221: public function getPort()
222: {
223: return $this->port;
224: }
225:
226:
227: 228: 229: 230: 231:
232: public function setPath($value)
233: {
234: $this->path = (string) $value;
235: return $this;
236: }
237:
238:
239: 240: 241: 242:
243: public function getPath()
244: {
245: return $this->path;
246: }
247:
248:
249: 250: 251: 252: 253:
254: public function setQuery($value)
255: {
256: $this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
257: return $this;
258: }
259:
260:
261: 262: 263: 264: 265:
266: public function appendQuery($value)
267: {
268: $value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
269: $this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value;
270: return $this;
271: }
272:
273:
274: 275: 276: 277:
278: public function getQuery()
279: {
280: return $this->query;
281: }
282:
283:
284: 285: 286: 287: 288:
289: public function getQueryParameter($name, $default = NULL)
290: {
291: parse_str($this->query, $params);
292: return isset($params[$name]) ? $params[$name] : $default;
293: }
294:
295:
296: 297: 298: 299: 300:
301: public function setQueryParameter($name, $value)
302: {
303: parse_str($this->query, $params);
304: if ($value === NULL) {
305: unset($params[$name]);
306: } else {
307: $params[$name] = $value;
308: }
309: $this->setQuery($params);
310: return $this;
311: }
312:
313:
314: 315: 316: 317: 318:
319: public function setFragment($value)
320: {
321: $this->fragment = (string) $value;
322: return $this;
323: }
324:
325:
326: 327: 328: 329:
330: public function getFragment()
331: {
332: return $this->fragment;
333: }
334:
335:
336: 337: 338: 339:
340: public function getAbsoluteUrl()
341: {
342: return $this->getHostUrl() . $this->path
343: . ($this->query === '' ? '' : '?' . $this->query)
344: . ($this->fragment === '' ? '' : '#' . $this->fragment);
345: }
346:
347:
348: 349: 350: 351:
352: public function getAuthority()
353: {
354: $authority = $this->host;
355: if ($this->port && (!isset(self::$defaultPorts[$this->scheme]) || $this->port !== self::$defaultPorts[$this->scheme])) {
356: $authority .= ':' . $this->port;
357: }
358:
359: if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') {
360: $authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority;
361: }
362:
363: return $authority;
364: }
365:
366:
367: 368: 369: 370:
371: public function getHostUrl()
372: {
373: return ($this->scheme ? $this->scheme . ':' : '') . '//' . $this->getAuthority();
374: }
375:
376:
377: 378: 379: 380:
381: public function getBasePath()
382: {
383: $pos = strrpos($this->path, '/');
384: return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1);
385: }
386:
387:
388: 389: 390: 391:
392: public function getBaseUrl()
393: {
394: return $this->getHostUrl() . $this->getBasePath();
395: }
396:
397:
398: 399: 400: 401:
402: public function getRelativeUrl()
403: {
404: return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl()));
405: }
406:
407:
408: 409: 410: 411: 412:
413: public function isEqual($url)
414: {
415: $url = new self($url);
416: parse_str($url->query, $query);
417: ksort($query);
418: parse_str($this->query, $query2);
419: ksort($query2);
420: $http = in_array($this->scheme, array('http', 'https'), TRUE);
421: return $url->scheme === $this->scheme
422: && !strcasecmp(rawurldecode($url->host), rawurldecode($this->host))
423: && $url->port === $this->port
424: && ($http || rawurldecode($url->user) === rawurldecode($this->user))
425: && ($http || rawurldecode($url->pass) === rawurldecode($this->pass))
426: && self::unescape($url->path, '%/') === self::unescape($this->path, '%/')
427: && $query === $query2
428: && rawurldecode($url->fragment) === rawurldecode($this->fragment);
429: }
430:
431:
432: 433: 434: 435:
436: public function canonicalize()
437: {
438: $this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/#?');
439: $this->host = strtolower(rawurldecode($this->host));
440: $this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+#');
441: return $this;
442: }
443:
444:
445: 446: 447:
448: public function __toString()
449: {
450: return $this->getAbsoluteUrl();
451: }
452:
453:
454: 455: 456: 457: 458: 459:
460: public static function unescape($s, $reserved = '%;/?:@&=+$,')
461: {
462:
463:
464:
465: if ($reserved !== '') {
466: $s = preg_replace_callback(
467: '#%(' . substr(chunk_split(bin2hex($reserved), 2, '|'), 0, -1) . ')#i',
468: function ($m) { return '%25' . strtoupper($m[1]); },
469: $s
470: );
471: }
472: return rawurldecode($s);
473: }
474:
475: }
476: