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