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