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