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