Namespaces

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • Ftp
  • Html
  • HttpContext
  • HttpRequest
  • HttpResponse
  • HttpUploadedFile
  • Session
  • SessionNamespace
  • Uri
  • UriScript
  • User

Interfaces

  • IHttpRequest
  • IHttpResponse
  • IUser

Exceptions

  • FtpException
  • Overview
  • Namespace
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette\Web;
 13: 
 14: use Nette,
 15:     Nette\String;
 16: 
 17: 
 18: 
 19: /**
 20:  * HttpRequest provides access scheme for request sent via HTTP.
 21:  *
 22:  * @author     David Grudl
 23:  *
 24:  * @property   UriScript $uri
 25:  * @property-read Uri $originalUri
 26:  * @property-read array $query
 27:  * @property-read array $post
 28:  * @property-read string $postRaw
 29:  * @property-read array $files
 30:  * @property-read array $cookies
 31:  * @property-read string $method
 32:  * @property-read array $headers
 33:  * @property-read Uri $referer
 34:  * @property-read string $remoteAddress
 35:  * @property-read string $remoteHost
 36:  * @property-read bool $secured
 37:  */
 38: class HttpRequest extends Nette\Object implements IHttpRequest
 39: {
 40:     /** @var array */
 41:     protected $query;
 42: 
 43:     /** @var array */
 44:     protected $post;
 45: 
 46:     /** @var array */
 47:     protected $files;
 48: 
 49:     /** @var array */
 50:     protected $cookies;
 51: 
 52:     /** @var UriScript {@link HttpRequest::getUri()} */
 53:     protected $uri;
 54: 
 55:     /** @var Uri  {@link HttpRequest::getOriginalUri()} */
 56:     protected $originalUri;
 57: 
 58:     /** @var array  {@link HttpRequest::getHeaders()} */
 59:     protected $headers;
 60: 
 61:     /** @var array */
 62:     protected $uriFilter = array(
 63:         PHP_URL_PATH => array('#/{2,}#' => '/'), // '%20' => ''
 64:         0 => array(), // '#[.,)]$#' => ''
 65:     );
 66: 
 67:     /** @var string */
 68:     protected $encoding;
 69: 
 70: 
 71: 
 72:     /********************* URI ****************d*g**/
 73: 
 74: 
 75: 
 76:     /**
 77:      * Returns URL object.
 78:      * @return UriScript
 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:      * Sets URL object.
 92:      * @param  UriScript
 93:      * @return HttpRequest  provides a fluent interface
 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:      * Returns URL object.
108:      * @return Uri
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:      * Sets request URI filter.
122:      * @param  string  pattern to search for
123:      * @param  string  string to replace
124:      * @param  int     PHP_URL_PATH or NULL
125:      * @return void
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:      * Returns request URI filter.
144:      * @return array
145:      */
146:     final public function getUriFilters()
147:     {
148:         return $this->uriFilter;
149:     }
150: 
151: 
152: 
153:     /**
154:      * Detects uri, base path and script path of the request.
155:      * @return void
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:         // host & port
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:         // path & query
185:         if (isset($_SERVER['REQUEST_URI'])) { // Apache, IIS 6.0
186:             $requestUri = $_SERVER['REQUEST_URI'];
187: 
188:         } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 (PHP as CGI ?)
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:         // normalized uri
209:         $uri->canonicalize();
210:         $uri->path = String::fixEncoding($uri->path);
211: 
212:         // detect base URI-path - inspired by Zend Framework (c) Zend Technologies USA Inc. (http://www.zend.com), new BSD license
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']; // 1and1 shared hosting compatibility
224: 
225:         } elseif (isset($_SERVER['PHP_SELF'], $_SERVER['SCRIPT_FILENAME'])) {
226:             // Backtrack up the script_filename to find the portion matching php_self
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:         // Does the scriptPath have anything in common with the request_uri?
240:         if (strncmp($uri->path, $scriptPath, strlen($scriptPath)) === 0) {
241:             // whole $scriptPath in URL
242:             $uri->scriptPath = $scriptPath;
243: 
244:         } elseif (strncmp($uri->path, $scriptPath, strrpos($scriptPath, '/') + 1) === 0) {
245:             // directory portion of $scriptPath in URL
246:             $uri->scriptPath = substr($scriptPath, 0, strrpos($scriptPath, '/') + 1);
247: 
248:         } elseif (strpos($uri->path, basename($scriptPath)) === FALSE) {
249:             // no match whatsoever; set it blank
250:             $uri->scriptPath = '/';
251: 
252:         } elseif ((strlen($uri->path) >= strlen($scriptPath))
253:             && ((FALSE !== ($pos = strpos($uri->path, $scriptPath))) && ($pos !== 0))) {
254:             // If using mod_rewrite or ISAPI_Rewrite strip the script filename
255:             // out of scriptPath. $pos !== 0 makes sure it is not matching a value
256:             // from PATH_INFO or QUERY_STRING
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:     /********************* query, post, files & cookies ****************d*g**/
269: 
270: 
271: 
272:     /**
273:      * Returns variable provided to the script via URL query ($_GET).
274:      * If no key is passed, returns the entire array.
275:      * @param  string key
276:      * @param  mixed  default value
277:      * @return mixed
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:      * Returns variable provided to the script via POST method ($_POST).
300:      * If no key is passed, returns the entire array.
301:      * @param  string key
302:      * @param  mixed  default value
303:      * @return mixed
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:      * Returns HTTP POST data in raw format (only for "application/x-www-form-urlencoded").
326:      * @return string
327:      */
328:     public function getPostRaw()
329:     {
330:         return file_get_contents('php://input');
331:     }
332: 
333: 
334: 
335:     /**
336:      * Returns uploaded file.
337:      * @param  string key (or more keys)
338:      * @return HttpUploadedFile
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:      * Returns uploaded files.
353:      * @return array
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:      * Returns variable provided to the script via HTTP cookies.
368:      * @param  string key
369:      * @param  mixed  default value
370:      * @return mixed
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:      * Returns variables provided to the script via HTTP cookies.
393:      * @return array
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:      * Recursively converts and checks encoding.
408:      * @param  array
409:      * @param  string
410:      * @return HttpRequest  provides a fluent interface
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; // reinitialization required
417:         }
418:         return $this;
419:     }
420: 
421: 
422: 
423:     /**
424:      * Initializes $this->query, $this->files, $this->cookies and $this->files arrays
425:      * @return void
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:         // remove fucking quotes and check (and optionally convert) encoding
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:                         // invalid key -> ignore
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:         // structure $files and create HttpUploadedFile objects
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:     /********************* method & headers ****************d*g**/
532: 
533: 
534: 
535:     /**
536:      * Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive.
537:      * @return string
538:      */
539:     public function getMethod()
540:     {
541:         return isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL;
542:     }
543: 
544: 
545: 
546:     /**
547:      * Checks if the request method is the given one.
548:      * @param  string
549:      * @return bool
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:      * Checks if the request method is POST.
560:      * @return bool
561:      */
562:     public function isPost()
563:     {
564:         return $this->isMethod('POST');
565:     }
566: 
567: 
568: 
569:     /**
570:      * Return the value of the HTTP header. Pass the header name as the
571:      * plain, HTTP-specified header name (e.g. 'Accept-Encoding').
572:      * @param  string
573:      * @param  mixed
574:      * @return mixed
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:      * Returns all HTTP headers.
591:      * @return array
592:      */
593:     public function getHeaders()
594:     {
595:         if ($this->headers === NULL) {
596:             // lazy initialization
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:      * Returns referrer.
618:      * @return Uri|NULL
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:      * Is the request is sent via secure channel (https).
630:      * @return bool
631:      */
632:     public function isSecured()
633:     {
634:         return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off');
635:     }
636: 
637: 
638: 
639:     /**
640:      * Is AJAX request?
641:      * @return bool
642:      */
643:     public function isAjax()
644:     {
645:         return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
646:     }
647: 
648: 
649: 
650:     /**
651:      * Returns the IP address of the remote client.
652:      * @return string
653:      */
654:     public function getRemoteAddress()
655:     {
656:         return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL;
657:     }
658: 
659: 
660: 
661:     /**
662:      * Returns the host of the remote client.
663:      * @return string
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:      * Parse Accept-Language header and returns prefered language.
681:      * @param  array   Supported languages
682:      * @return string
683:      */
684:     public function detectLanguage(array $langs)
685:     {
686:         $header = $this->getHeader('accept-language');
687:         if (!$header) return NULL;
688: 
689:         $s = strtolower($header);  // case insensitive
690:         $s = strtr($s, '_', '-');  // cs_CZ means cs-CZ
691:         rsort($langs);             // first more specific
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: 
Nette Framework 0.9.7 API documentation generated by ApiGen 2.3.0