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