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: class HttpRequest extends Object implements IHttpRequest
35: {
36:
37: protected $query;
38:
39:
40: protected $post;
41:
42:
43: protected $files;
44:
45:
46: protected $cookies;
47:
48:
49: protected $uri;
50:
51:
52: protected $originalUri;
53:
54:
55: protected $headers;
56:
57:
58: protected $uriFilter = array(
59: PHP_URL_PATH => array('#/{2,}#' => '/'), 60: 0 => array(), 61: );
62:
63:
64: protected $encoding;
65:
66:
67:
68:
69:
70:
71:
72: 73: 74: 75:
76: final public function getUri()
77: {
78: if ($this->uri === NULL) {
79: $this->detectUri();
80: }
81: return $this->uri;
82: }
83:
84:
85:
86: 87: 88: 89: 90:
91: public function setUri(UriScript $uri)
92: {
93: $this->uri = clone $uri;
94: $this->query = NULL;
95: $this->uri->canonicalize();
96: $this->uri->freeze();
97: return $this;
98: }
99:
100:
101:
102: 103: 104: 105:
106: final public function getOriginalUri()
107: {
108: if ($this->originalUri === NULL) {
109: $this->detectUri();
110: }
111: return $this->originalUri;
112: }
113:
114:
115:
116: 117: 118: 119: 120: 121: 122:
123: public function addUriFilter($pattern, $replacement = '', $component = NULL)
124: {
125: $pattern = '#' . $pattern . '#';
126: $component = $component === PHP_URL_PATH ? PHP_URL_PATH : 0;
127:
128: if ($replacement === NULL) {
129: unset($this->uriFilter[$component][$pattern]);
130: } else {
131: $this->uriFilter[$component][$pattern] = $replacement;
132: }
133: $this->uri = NULL;
134: }
135:
136:
137:
138: 139: 140: 141:
142: final public function getUriFilters()
143: {
144: return $this->uriFilter;
145: }
146:
147:
148:
149: 150: 151: 152:
153: protected function detectUri()
154: {
155: $uri = $this->uri = new UriScript;
156: $uri->scheme = $this->isSecured() ? 'https' : 'http';
157: $uri->user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
158: $uri->password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
159:
160: 161: if (isset($_SERVER['HTTP_HOST'])) {
162: $pair = explode(':', $_SERVER['HTTP_HOST']);
163:
164: } elseif (isset($_SERVER['SERVER_NAME'])) {
165: $pair = explode(':', $_SERVER['SERVER_NAME']);
166:
167: } else {
168: $pair = array('');
169: }
170:
171: $uri->host = $pair[0];
172:
173: if (isset($pair[1])) {
174: $uri->port = (int) $pair[1];
175:
176: } elseif (isset($_SERVER['SERVER_PORT'])) {
177: $uri->port = (int) $_SERVER['SERVER_PORT'];
178: }
179:
180: 181: if (isset($_SERVER['REQUEST_URI'])) { 182: $requestUri = $_SERVER['REQUEST_URI'];
183:
184: } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { 185: $requestUri = $_SERVER['ORIG_PATH_INFO'];
186: if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') {
187: $requestUri .= '?' . $_SERVER['QUERY_STRING'];
188: }
189: } else {
190: $requestUri = '';
191: }
192:
193: $tmp = explode('?', $requestUri, 2);
194: $this->originalUri = new Uri($uri);
195: $this->originalUri->path = $tmp[0];
196: $this->originalUri->query = isset($tmp[1]) ? $tmp[1] : '';
197: $this->originalUri->freeze();
198:
199: $requestUri = String::replace($requestUri, $this->uriFilter[0]);
200: $tmp = explode('?', $requestUri, 2);
201: $uri->path = String::replace($tmp[0], $this->uriFilter[PHP_URL_PATH]);
202: $uri->query = isset($tmp[1]) ? $tmp[1] : '';
203:
204: 205: $uri->canonicalize();
206: $uri->path = String::fixEncoding($uri->path);
207:
208: 209: $filename = isset($_SERVER['SCRIPT_FILENAME']) ? basename($_SERVER['SCRIPT_FILENAME']) : NULL;
210: $scriptPath = '';
211:
212: if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $filename) {
213: $scriptPath = rtrim($_SERVER['SCRIPT_NAME'], '/');
214:
215: } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $filename) {
216: $scriptPath = $_SERVER['PHP_SELF'];
217:
218: } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
219: $scriptPath = $_SERVER['ORIG_SCRIPT_NAME']; 220:
221: } elseif (isset($_SERVER['PHP_SELF'], $_SERVER['SCRIPT_FILENAME'])) {
222: 223: $path = $_SERVER['PHP_SELF'];
224: $segs = explode('/', trim($_SERVER['SCRIPT_FILENAME'], '/'));
225: $segs = array_reverse($segs);
226: $index = 0;
227: $last = count($segs);
228: do {
229: $seg = $segs[$index];
230: $scriptPath = '/' . $seg . $scriptPath;
231: $index++;
232: } while (($last > $index) && (FALSE !== ($pos = strpos($path, $scriptPath))) && (0 != $pos));
233: }
234:
235: 236: if (strncmp($uri->path, $scriptPath, strlen($scriptPath)) === 0) {
237: 238: $uri->scriptPath = $scriptPath;
239:
240: } elseif (strncmp($uri->path, $scriptPath, strrpos($scriptPath, '/') + 1) === 0) {
241: 242: $uri->scriptPath = substr($scriptPath, 0, strrpos($scriptPath, '/') + 1);
243:
244: } elseif (strpos($uri->path, basename($scriptPath)) === FALSE) {
245: 246: $uri->scriptPath = '/';
247:
248: } elseif ((strlen($uri->path) >= strlen($scriptPath))
249: && ((FALSE !== ($pos = strpos($uri->path, $scriptPath))) && ($pos !== 0))) {
250: 251: 252: 253: $uri->scriptPath = substr($uri->path, 0, $pos + strlen($scriptPath));
254:
255: } else {
256: $uri->scriptPath = $scriptPath;
257: }
258:
259: $uri->freeze();
260: }
261:
262:
263:
264:
265:
266:
267:
268: 269: 270: 271: 272: 273: 274:
275: final public function getQuery($key = NULL, $default = NULL)
276: {
277: if ($this->query === NULL) {
278: $this->initialize();
279: }
280:
281: if (func_num_args() === 0) {
282: return $this->query;
283:
284: } elseif (isset($this->query[$key])) {
285: return $this->query[$key];
286:
287: } else {
288: return $default;
289: }
290: }
291:
292:
293:
294: 295: 296: 297: 298: 299: 300:
301: final public function getPost($key = NULL, $default = NULL)
302: {
303: if ($this->post === NULL) {
304: $this->initialize();
305: }
306:
307: if (func_num_args() === 0) {
308: return $this->post;
309:
310: } elseif (isset($this->post[$key])) {
311: return $this->post[$key];
312:
313: } else {
314: return $default;
315: }
316: }
317:
318:
319:
320: 321: 322: 323:
324: public function getPostRaw()
325: {
326: return file_get_contents('php://input');
327: }
328:
329:
330:
331: 332: 333: 334: 335:
336: final public function getFile($key)
337: {
338: if ($this->files === NULL) {
339: $this->initialize();
340: }
341: $args = func_get_args();
342: return ArrayTools::get($this->files, $args);
343: }
344:
345:
346:
347: 348: 349: 350:
351: final public function getFiles()
352: {
353: if ($this->files === NULL) {
354: $this->initialize();
355: }
356:
357: return $this->files;
358: }
359:
360:
361:
362: 363: 364: 365: 366: 367:
368: final public function getCookie($key, $default = NULL)
369: {
370: if ($this->cookies === NULL) {
371: $this->initialize();
372: }
373:
374: if (func_num_args() === 0) {
375: return $this->cookies;
376:
377: } elseif (isset($this->cookies[$key])) {
378: return $this->cookies[$key];
379:
380: } else {
381: return $default;
382: }
383: }
384:
385:
386:
387: 388: 389: 390:
391: final public function getCookies()
392: {
393: if ($this->cookies === NULL) {
394: $this->initialize();
395: }
396:
397: return $this->cookies;
398: }
399:
400:
401:
402: 403: 404: 405: 406: 407:
408: public function setEncoding($encoding)
409: {
410: if (strcasecmp($encoding, $this->encoding)) {
411: $this->encoding = $encoding;
412: $this->query = $this->post = $this->cookies = $this->files = NULL; 413: }
414: return $this;
415: }
416:
417:
418:
419: 420: 421: 422:
423: public function initialize()
424: {
425: $filter = (!in_array(ini_get("filter.default"), array("", "unsafe_raw")) || ini_get("filter.default_flags"));
426:
427: parse_str($this->getUri()->query, $this->query);
428: if (!$this->query) {
429: $this->query = $filter ? filter_input_array(INPUT_GET, FILTER_UNSAFE_RAW) : (empty($_GET) ? array() : $_GET);
430: }
431: $this->post = $filter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? array() : $_POST);
432: $this->cookies = $filter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? array() : $_COOKIE);
433:
434: $gpc = (bool) get_magic_quotes_gpc();
435: $enc = (bool) $this->encoding;
436: $old = error_reporting(error_reporting() ^ E_NOTICE);
437: $nonChars = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
438:
439:
440: 441: if ($gpc || $enc) {
442: $utf = strcasecmp($this->encoding, 'UTF-8') === 0;
443: $list = array(& $this->query, & $this->post, & $this->cookies);
444: while (list($key, $val) = each($list)) {
445: foreach ($val as $k => $v) {
446: unset($list[$key][$k]);
447:
448: if ($gpc) {
449: $k = stripslashes($k);
450: }
451:
452: if ($enc && is_string($k) && (preg_match($nonChars, $k) || preg_last_error())) {
453: 454:
455: } elseif (is_array($v)) {
456: $list[$key][$k] = $v;
457: $list[] = & $list[$key][$k];
458:
459: } else {
460: if ($gpc && !$filter) {
461: $v = stripSlashes($v);
462: }
463: if ($enc) {
464: if ($utf) {
465: $v = String::fixEncoding($v);
466:
467: } else {
468: if (!String::checkEncoding($v)) {
469: $v = iconv($this->encoding, 'UTF-8//IGNORE', $v);
470: }
471: $v = html_entity_decode($v, ENT_QUOTES, 'UTF-8');
472: }
473: $v = preg_replace($nonChars, '', $v);
474: }
475: $list[$key][$k] = $v;
476: }
477: }
478: }
479: unset($list, $key, $val, $k, $v);
480: }
481:
482:
483: 484: $this->files = array();
485: $list = array();
486: if (!empty($_FILES)) {
487: foreach ($_FILES as $k => $v) {
488: if ($enc && is_string($k) && (preg_match($nonChars, $k) || preg_last_error())) continue;
489: $v['@'] = & $this->files[$k];
490: $list[] = $v;
491: }
492: }
493:
494: while (list(, $v) = each($list)) {
495: if (!isset($v['name'])) {
496: continue;
497:
498: } elseif (!is_array($v['name'])) {
499: if ($gpc) {
500: $v['name'] = stripSlashes($v['name']);
501: }
502: if ($enc) {
503: $v['name'] = preg_replace($nonChars, '', String::fixEncoding($v['name']));
504: }
505: $v['@'] = new HttpUploadedFile($v);
506: continue;
507: }
508:
509: foreach ($v['name'] as $k => $foo) {
510: if ($enc && is_string($k) && (preg_match($nonChars, $k) || preg_last_error())) continue;
511: $list[] = array(
512: 'name' => $v['name'][$k],
513: 'type' => $v['type'][$k],
514: 'size' => $v['size'][$k],
515: 'tmp_name' => $v['tmp_name'][$k],
516: 'error' => $v['error'][$k],
517: '@' => & $v['@'][$k],
518: );
519: }
520: }
521:
522: error_reporting($old);
523: }
524:
525:
526:
527:
528:
529:
530:
531: 532: 533: 534:
535: public function getMethod()
536: {
537: return isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL;
538: }
539:
540:
541:
542: 543: 544: 545: 546:
547: public function isMethod($method)
548: {
549: return isset($_SERVER['REQUEST_METHOD']) ? strcasecmp($_SERVER['REQUEST_METHOD'], $method) === 0 : FALSE;
550: }
551:
552:
553:
554: 555: 556: 557:
558: public function isPost()
559: {
560: return $this->isMethod('POST');
561: }
562:
563:
564:
565: 566: 567: 568: 569: 570: 571:
572: final public function getHeader($header, $default = NULL)
573: {
574: $header = strtolower($header);
575: $headers = $this->getHeaders();
576: if (isset($headers[$header])) {
577: return $headers[$header];
578: } else {
579: return $default;
580: }
581: }
582:
583:
584:
585: 586: 587: 588:
589: public function getHeaders()
590: {
591: if ($this->headers === NULL) {
592: 593: if (function_exists('apache_request_headers')) {
594: $this->headers = array_change_key_case(apache_request_headers(), CASE_LOWER);
595: } else {
596: $this->headers = array();
597: foreach ($_SERVER as $k => $v) {
598: if (strncmp($k, 'HTTP_', 5) == 0) {
599: $k = substr($k, 5);
600: } elseif (strncmp($k, 'CONTENT_', 8)) {
601: continue;
602: }
603: $this->headers[ strtr(strtolower($k), '_', '-') ] = $v;
604: }
605: }
606: }
607: return $this->headers;
608: }
609:
610:
611:
612: 613: 614: 615:
616: final public function getReferer()
617: {
618: $uri = self::getHeader('referer');
619: return $uri ? new Uri($uri) : NULL;
620: }
621:
622:
623:
624: 625: 626: 627:
628: public function isSecured()
629: {
630: return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off');
631: }
632:
633:
634:
635: 636: 637: 638:
639: public function isAjax()
640: {
641: return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
642: }
643:
644:
645:
646: 647: 648: 649:
650: public function getRemoteAddress()
651: {
652: return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL;
653: }
654:
655:
656:
657: 658: 659: 660:
661: public function getRemoteHost()
662: {
663: if (!isset($_SERVER['REMOTE_HOST'])) {
664: if (!isset($_SERVER['REMOTE_ADDR'])) {
665: return NULL;
666: }
667: $_SERVER['REMOTE_HOST'] = getHostByAddr($_SERVER['REMOTE_ADDR']);
668: }
669:
670: return $_SERVER['REMOTE_HOST'];
671: }
672:
673:
674:
675: 676: 677: 678: 679:
680: public function detectLanguage(array $langs)
681: {
682: $header = $this->getHeader('accept-language');
683: if (!$header) return NULL;
684:
685: $s = strtolower($header); 686: $s = strtr($s, '_', '-'); 687: rsort($langs); 688: preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches);
689:
690: if (!$matches[0]) {
691: return NULL;
692: }
693:
694: $max = 0;
695: $lang = NULL;
696: foreach ($matches[1] as $key => $value) {
697: $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key];
698: if ($q > $max) {
699: $max = $q; $lang = $value;
700: }
701: }
702:
703: return $lang;
704: }
705:
706: }
707: