|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of the Symfony package. |
|
5 * |
|
6 * (c) Fabien Potencier <fabien@symfony.com> |
|
7 * |
|
8 * For the full copyright and license information, please view the LICENSE |
|
9 * file that was distributed with this source code. |
|
10 */ |
|
11 |
|
12 namespace Symfony\Component\HttpFoundation; |
|
13 |
|
14 use Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage; |
|
15 |
|
16 /** |
|
17 * Request represents an HTTP request. |
|
18 * |
|
19 * @author Fabien Potencier <fabien@symfony.com> |
|
20 * |
|
21 * @api |
|
22 */ |
|
23 class Request |
|
24 { |
|
25 static protected $trustProxy = false; |
|
26 |
|
27 /** |
|
28 * @var \Symfony\Component\HttpFoundation\ParameterBag |
|
29 * |
|
30 * @api |
|
31 */ |
|
32 public $attributes; |
|
33 |
|
34 /** |
|
35 * @var \Symfony\Component\HttpFoundation\ParameterBag |
|
36 * |
|
37 * @api |
|
38 */ |
|
39 public $request; |
|
40 |
|
41 /** |
|
42 * @var \Symfony\Component\HttpFoundation\ParameterBag |
|
43 * |
|
44 * @api |
|
45 */ |
|
46 public $query; |
|
47 |
|
48 /** |
|
49 * @var \Symfony\Component\HttpFoundation\ParameterBag |
|
50 * |
|
51 * @api |
|
52 */ |
|
53 public $server; |
|
54 |
|
55 /** |
|
56 * @var \Symfony\Component\HttpFoundation\ParameterBag |
|
57 * |
|
58 * @api |
|
59 */ |
|
60 public $files; |
|
61 |
|
62 /** |
|
63 * @var \Symfony\Component\HttpFoundation\ParameterBag |
|
64 * |
|
65 * @api |
|
66 */ |
|
67 public $cookies; |
|
68 |
|
69 /** |
|
70 * @var \Symfony\Component\HttpFoundation\HeaderBag |
|
71 * |
|
72 * @api |
|
73 */ |
|
74 public $headers; |
|
75 |
|
76 protected $content; |
|
77 protected $languages; |
|
78 protected $charsets; |
|
79 protected $acceptableContentTypes; |
|
80 protected $pathInfo; |
|
81 protected $requestUri; |
|
82 protected $baseUrl; |
|
83 protected $basePath; |
|
84 protected $method; |
|
85 protected $format; |
|
86 protected $session; |
|
87 |
|
88 static protected $formats; |
|
89 |
|
90 /** |
|
91 * Constructor. |
|
92 * |
|
93 * @param array $query The GET parameters |
|
94 * @param array $request The POST parameters |
|
95 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) |
|
96 * @param array $cookies The COOKIE parameters |
|
97 * @param array $files The FILES parameters |
|
98 * @param array $server The SERVER parameters |
|
99 * @param string $content The raw body data |
|
100 * |
|
101 * @api |
|
102 */ |
|
103 public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) |
|
104 { |
|
105 $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); |
|
106 } |
|
107 |
|
108 /** |
|
109 * Sets the parameters for this request. |
|
110 * |
|
111 * This method also re-initializes all properties. |
|
112 * |
|
113 * @param array $query The GET parameters |
|
114 * @param array $request The POST parameters |
|
115 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) |
|
116 * @param array $cookies The COOKIE parameters |
|
117 * @param array $files The FILES parameters |
|
118 * @param array $server The SERVER parameters |
|
119 * @param string $content The raw body data |
|
120 * |
|
121 * @api |
|
122 */ |
|
123 public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) |
|
124 { |
|
125 $this->request = new ParameterBag($request); |
|
126 $this->query = new ParameterBag($query); |
|
127 $this->attributes = new ParameterBag($attributes); |
|
128 $this->cookies = new ParameterBag($cookies); |
|
129 $this->files = new FileBag($files); |
|
130 $this->server = new ServerBag($server); |
|
131 $this->headers = new HeaderBag($this->server->getHeaders()); |
|
132 |
|
133 $this->content = $content; |
|
134 $this->languages = null; |
|
135 $this->charsets = null; |
|
136 $this->acceptableContentTypes = null; |
|
137 $this->pathInfo = null; |
|
138 $this->requestUri = null; |
|
139 $this->baseUrl = null; |
|
140 $this->basePath = null; |
|
141 $this->method = null; |
|
142 $this->format = null; |
|
143 } |
|
144 |
|
145 /** |
|
146 * Creates a new request with values from PHP's super globals. |
|
147 * |
|
148 * @return Request A new request |
|
149 * |
|
150 * @api |
|
151 */ |
|
152 static public function createFromGlobals() |
|
153 { |
|
154 $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); |
|
155 |
|
156 if (0 === strpos($request->server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') |
|
157 && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE')) |
|
158 ) { |
|
159 parse_str($request->getContent(), $data); |
|
160 $request->request = new ParameterBag($data); |
|
161 } |
|
162 |
|
163 return $request; |
|
164 } |
|
165 |
|
166 /** |
|
167 * Creates a Request based on a given URI and configuration. |
|
168 * |
|
169 * @param string $uri The URI |
|
170 * @param string $method The HTTP method |
|
171 * @param array $parameters The request (GET) or query (POST) parameters |
|
172 * @param array $cookies The request cookies ($_COOKIE) |
|
173 * @param array $files The request files ($_FILES) |
|
174 * @param array $server The server parameters ($_SERVER) |
|
175 * @param string $content The raw body data |
|
176 * |
|
177 * @return Request A Request instance |
|
178 * |
|
179 * @api |
|
180 */ |
|
181 static public function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) |
|
182 { |
|
183 $defaults = array( |
|
184 'SERVER_NAME' => 'localhost', |
|
185 'SERVER_PORT' => 80, |
|
186 'HTTP_HOST' => 'localhost', |
|
187 'HTTP_USER_AGENT' => 'Symfony/2.X', |
|
188 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', |
|
189 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', |
|
190 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', |
|
191 'REMOTE_ADDR' => '127.0.0.1', |
|
192 'SCRIPT_NAME' => '', |
|
193 'SCRIPT_FILENAME' => '', |
|
194 'SERVER_PROTOCOL' => 'HTTP/1.1', |
|
195 'REQUEST_TIME' => time(), |
|
196 ); |
|
197 |
|
198 $components = parse_url($uri); |
|
199 if (isset($components['host'])) { |
|
200 $defaults['SERVER_NAME'] = $components['host']; |
|
201 $defaults['HTTP_HOST'] = $components['host']; |
|
202 } |
|
203 |
|
204 if (isset($components['scheme'])) { |
|
205 if ('https' === $components['scheme']) { |
|
206 $defaults['HTTPS'] = 'on'; |
|
207 $defaults['SERVER_PORT'] = 443; |
|
208 } |
|
209 } |
|
210 |
|
211 if (isset($components['port'])) { |
|
212 $defaults['SERVER_PORT'] = $components['port']; |
|
213 $defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port']; |
|
214 } |
|
215 |
|
216 if (!isset($components['path'])) { |
|
217 $components['path'] = ''; |
|
218 } |
|
219 |
|
220 if (in_array(strtoupper($method), array('POST', 'PUT', 'DELETE'))) { |
|
221 $request = $parameters; |
|
222 $query = array(); |
|
223 $defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; |
|
224 } else { |
|
225 $request = array(); |
|
226 $query = $parameters; |
|
227 if (false !== $pos = strpos($uri, '?')) { |
|
228 $qs = substr($uri, $pos + 1); |
|
229 parse_str($qs, $params); |
|
230 |
|
231 $query = array_merge($params, $query); |
|
232 } |
|
233 } |
|
234 |
|
235 $queryString = isset($components['query']) ? html_entity_decode($components['query']) : ''; |
|
236 parse_str($queryString, $qs); |
|
237 if (is_array($qs)) { |
|
238 $query = array_replace($qs, $query); |
|
239 } |
|
240 |
|
241 $uri = $components['path'].($queryString ? '?'.$queryString : ''); |
|
242 |
|
243 $server = array_replace($defaults, $server, array( |
|
244 'REQUEST_METHOD' => strtoupper($method), |
|
245 'PATH_INFO' => '', |
|
246 'REQUEST_URI' => $uri, |
|
247 'QUERY_STRING' => $queryString, |
|
248 )); |
|
249 |
|
250 return new static($query, $request, array(), $cookies, $files, $server, $content); |
|
251 } |
|
252 |
|
253 /** |
|
254 * Clones a request and overrides some of its parameters. |
|
255 * |
|
256 * @param array $query The GET parameters |
|
257 * @param array $request The POST parameters |
|
258 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) |
|
259 * @param array $cookies The COOKIE parameters |
|
260 * @param array $files The FILES parameters |
|
261 * @param array $server The SERVER parameters |
|
262 * |
|
263 * @api |
|
264 */ |
|
265 public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) |
|
266 { |
|
267 $dup = clone $this; |
|
268 if ($query !== null) { |
|
269 $dup->query = new ParameterBag($query); |
|
270 } |
|
271 if ($request !== null) { |
|
272 $dup->request = new ParameterBag($request); |
|
273 } |
|
274 if ($attributes !== null) { |
|
275 $dup->attributes = new ParameterBag($attributes); |
|
276 } |
|
277 if ($cookies !== null) { |
|
278 $dup->cookies = new ParameterBag($cookies); |
|
279 } |
|
280 if ($files !== null) { |
|
281 $dup->files = new FileBag($files); |
|
282 } |
|
283 if ($server !== null) { |
|
284 $dup->server = new ServerBag($server); |
|
285 $dup->headers = new HeaderBag($dup->server->getHeaders()); |
|
286 } |
|
287 $this->languages = null; |
|
288 $this->charsets = null; |
|
289 $this->acceptableContentTypes = null; |
|
290 $this->pathInfo = null; |
|
291 $this->requestUri = null; |
|
292 $this->baseUrl = null; |
|
293 $this->basePath = null; |
|
294 $this->method = null; |
|
295 $this->format = null; |
|
296 |
|
297 return $dup; |
|
298 } |
|
299 |
|
300 /** |
|
301 * Clones the current request. |
|
302 * |
|
303 * Note that the session is not cloned as duplicated requests |
|
304 * are most of the time sub-requests of the main one. |
|
305 */ |
|
306 public function __clone() |
|
307 { |
|
308 $this->query = clone $this->query; |
|
309 $this->request = clone $this->request; |
|
310 $this->attributes = clone $this->attributes; |
|
311 $this->cookies = clone $this->cookies; |
|
312 $this->files = clone $this->files; |
|
313 $this->server = clone $this->server; |
|
314 $this->headers = clone $this->headers; |
|
315 } |
|
316 |
|
317 /** |
|
318 * Returns the request as a string. |
|
319 * |
|
320 * @return string The request |
|
321 */ |
|
322 public function __toString() |
|
323 { |
|
324 return |
|
325 sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". |
|
326 $this->headers."\r\n". |
|
327 $this->getContent(); |
|
328 } |
|
329 |
|
330 /** |
|
331 * Overrides the PHP global variables according to this request instance. |
|
332 * |
|
333 * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE, and $_FILES. |
|
334 * |
|
335 * @api |
|
336 */ |
|
337 public function overrideGlobals() |
|
338 { |
|
339 $_GET = $this->query->all(); |
|
340 $_POST = $this->request->all(); |
|
341 $_SERVER = $this->server->all(); |
|
342 $_COOKIE = $this->cookies->all(); |
|
343 // FIXME: populate $_FILES |
|
344 |
|
345 foreach ($this->headers->all() as $key => $value) { |
|
346 $key = strtoupper(str_replace('-', '_', $key)); |
|
347 if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { |
|
348 $_SERVER[$key] = implode(', ', $value); |
|
349 } else { |
|
350 $_SERVER['HTTP_'.$key] = implode(', ', $value); |
|
351 } |
|
352 } |
|
353 |
|
354 // FIXME: should read variables_order and request_order |
|
355 // to know which globals to merge and in which order |
|
356 $_REQUEST = array_merge($_GET, $_POST); |
|
357 } |
|
358 |
|
359 /** |
|
360 * Trusts $_SERVER entries coming from proxies. |
|
361 * |
|
362 * You should only call this method if your application |
|
363 * is hosted behind a reverse proxy that you manage. |
|
364 * |
|
365 * @api |
|
366 */ |
|
367 static public function trustProxyData() |
|
368 { |
|
369 self::$trustProxy = true; |
|
370 } |
|
371 |
|
372 /** |
|
373 * Gets a "parameter" value. |
|
374 * |
|
375 * This method is mainly useful for libraries that want to provide some flexibility. |
|
376 * |
|
377 * Order of precedence: GET, PATH, POST, COOKIE |
|
378 * Avoid using this method in controllers: |
|
379 * * slow |
|
380 * * prefer to get from a "named" source |
|
381 * |
|
382 * @param string $key the key |
|
383 * @param mixed $default the default value |
|
384 * @param type $deep is parameter deep in multidimensional array |
|
385 * |
|
386 * @return mixed |
|
387 */ |
|
388 public function get($key, $default = null, $deep = false) |
|
389 { |
|
390 return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep); |
|
391 } |
|
392 |
|
393 /** |
|
394 * Gets the Session. |
|
395 * |
|
396 * @return Session|null The session |
|
397 * |
|
398 * @api |
|
399 */ |
|
400 public function getSession() |
|
401 { |
|
402 return $this->session; |
|
403 } |
|
404 |
|
405 /** |
|
406 * Whether the request contains a Session which was started in one of the |
|
407 * previous requests. |
|
408 * |
|
409 * @return boolean |
|
410 * |
|
411 * @api |
|
412 */ |
|
413 public function hasPreviousSession() |
|
414 { |
|
415 // the check for $this->session avoids malicious users trying to fake a session cookie with proper name |
|
416 return $this->cookies->has(session_name()) && null !== $this->session; |
|
417 } |
|
418 |
|
419 /** |
|
420 * Whether the request contains a Session object. |
|
421 * |
|
422 * @return boolean |
|
423 * |
|
424 * @api |
|
425 */ |
|
426 public function hasSession() |
|
427 { |
|
428 return null !== $this->session; |
|
429 } |
|
430 |
|
431 /** |
|
432 * Sets the Session. |
|
433 * |
|
434 * @param Session $session The Session |
|
435 * |
|
436 * @api |
|
437 */ |
|
438 public function setSession(Session $session) |
|
439 { |
|
440 $this->session = $session; |
|
441 } |
|
442 |
|
443 /** |
|
444 * Returns the client IP address. |
|
445 * |
|
446 * @param Boolean $proxy Whether the current request has been made behind a proxy or not |
|
447 * |
|
448 * @return string The client IP address |
|
449 * |
|
450 * @api |
|
451 */ |
|
452 public function getClientIp($proxy = false) |
|
453 { |
|
454 if ($proxy) { |
|
455 if ($this->server->has('HTTP_CLIENT_IP')) { |
|
456 return $this->server->get('HTTP_CLIENT_IP'); |
|
457 } elseif (self::$trustProxy && $this->server->has('HTTP_X_FORWARDED_FOR')) { |
|
458 return $this->server->get('HTTP_X_FORWARDED_FOR'); |
|
459 } |
|
460 } |
|
461 |
|
462 return $this->server->get('REMOTE_ADDR'); |
|
463 } |
|
464 |
|
465 /** |
|
466 * Returns current script name. |
|
467 * |
|
468 * @return string |
|
469 * |
|
470 * @api |
|
471 */ |
|
472 public function getScriptName() |
|
473 { |
|
474 return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', '')); |
|
475 } |
|
476 |
|
477 /** |
|
478 * Returns the path being requested relative to the executed script. |
|
479 * |
|
480 * The path info always starts with a /. |
|
481 * |
|
482 * Suppose this request is instantiated from /mysite on localhost: |
|
483 * |
|
484 * * http://localhost/mysite returns an empty string |
|
485 * * http://localhost/mysite/about returns '/about' |
|
486 * * http://localhost/mysite/about?var=1 returns '/about' |
|
487 * |
|
488 * @return string |
|
489 * |
|
490 * @api |
|
491 */ |
|
492 public function getPathInfo() |
|
493 { |
|
494 if (null === $this->pathInfo) { |
|
495 $this->pathInfo = $this->preparePathInfo(); |
|
496 } |
|
497 |
|
498 return $this->pathInfo; |
|
499 } |
|
500 |
|
501 /** |
|
502 * Returns the root path from which this request is executed. |
|
503 * |
|
504 * Suppose that an index.php file instantiates this request object: |
|
505 * |
|
506 * * http://localhost/index.php returns an empty string |
|
507 * * http://localhost/index.php/page returns an empty string |
|
508 * * http://localhost/web/index.php return '/web' |
|
509 * |
|
510 * @return string |
|
511 * |
|
512 * @api |
|
513 */ |
|
514 public function getBasePath() |
|
515 { |
|
516 if (null === $this->basePath) { |
|
517 $this->basePath = $this->prepareBasePath(); |
|
518 } |
|
519 |
|
520 return $this->basePath; |
|
521 } |
|
522 |
|
523 /** |
|
524 * Returns the root url from which this request is executed. |
|
525 * |
|
526 * The base URL never ends with a /. |
|
527 * |
|
528 * This is similar to getBasePath(), except that it also includes the |
|
529 * script filename (e.g. index.php) if one exists. |
|
530 * |
|
531 * @return string |
|
532 * |
|
533 * @api |
|
534 */ |
|
535 public function getBaseUrl() |
|
536 { |
|
537 if (null === $this->baseUrl) { |
|
538 $this->baseUrl = $this->prepareBaseUrl(); |
|
539 } |
|
540 |
|
541 return $this->baseUrl; |
|
542 } |
|
543 |
|
544 /** |
|
545 * Gets the request's scheme. |
|
546 * |
|
547 * @return string |
|
548 * |
|
549 * @api |
|
550 */ |
|
551 public function getScheme() |
|
552 { |
|
553 return $this->isSecure() ? 'https' : 'http'; |
|
554 } |
|
555 |
|
556 /** |
|
557 * Returns the port on which the request is made. |
|
558 * |
|
559 * @return string |
|
560 * |
|
561 * @api |
|
562 */ |
|
563 public function getPort() |
|
564 { |
|
565 return $this->headers->get('X-Forwarded-Port') ?: $this->server->get('SERVER_PORT'); |
|
566 } |
|
567 |
|
568 /** |
|
569 * Returns the HTTP host being requested. |
|
570 * |
|
571 * The port name will be appended to the host if it's non-standard. |
|
572 * |
|
573 * @return string |
|
574 * |
|
575 * @api |
|
576 */ |
|
577 public function getHttpHost() |
|
578 { |
|
579 $scheme = $this->getScheme(); |
|
580 $port = $this->getPort(); |
|
581 |
|
582 if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) { |
|
583 return $this->getHost(); |
|
584 } |
|
585 |
|
586 return $this->getHost().':'.$port; |
|
587 } |
|
588 |
|
589 /** |
|
590 * Returns the requested URI. |
|
591 * |
|
592 * @return string |
|
593 * |
|
594 * @api |
|
595 */ |
|
596 public function getRequestUri() |
|
597 { |
|
598 if (null === $this->requestUri) { |
|
599 $this->requestUri = $this->prepareRequestUri(); |
|
600 } |
|
601 |
|
602 return $this->requestUri; |
|
603 } |
|
604 |
|
605 /** |
|
606 * Generates a normalized URI for the Request. |
|
607 * |
|
608 * @return string A normalized URI for the Request |
|
609 * |
|
610 * @see getQueryString() |
|
611 * |
|
612 * @api |
|
613 */ |
|
614 public function getUri() |
|
615 { |
|
616 $qs = $this->getQueryString(); |
|
617 if (null !== $qs) { |
|
618 $qs = '?'.$qs; |
|
619 } |
|
620 |
|
621 return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs; |
|
622 } |
|
623 |
|
624 /** |
|
625 * Generates a normalized URI for the given path. |
|
626 * |
|
627 * @param string $path A path to use instead of the current one |
|
628 * |
|
629 * @return string The normalized URI for the path |
|
630 * |
|
631 * @api |
|
632 */ |
|
633 public function getUriForPath($path) |
|
634 { |
|
635 return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$path; |
|
636 } |
|
637 |
|
638 /** |
|
639 * Generates the normalized query string for the Request. |
|
640 * |
|
641 * It builds a normalized query string, where keys/value pairs are alphabetized |
|
642 * and have consistent escaping. |
|
643 * |
|
644 * @return string A normalized query string for the Request |
|
645 * |
|
646 * @api |
|
647 */ |
|
648 public function getQueryString() |
|
649 { |
|
650 if (!$qs = $this->server->get('QUERY_STRING')) { |
|
651 return null; |
|
652 } |
|
653 |
|
654 $parts = array(); |
|
655 $order = array(); |
|
656 |
|
657 foreach (explode('&', $qs) as $segment) { |
|
658 if (false === strpos($segment, '=')) { |
|
659 $parts[] = $segment; |
|
660 $order[] = $segment; |
|
661 } else { |
|
662 $tmp = explode('=', rawurldecode($segment), 2); |
|
663 $parts[] = rawurlencode($tmp[0]).'='.rawurlencode($tmp[1]); |
|
664 $order[] = $tmp[0]; |
|
665 } |
|
666 } |
|
667 array_multisort($order, SORT_ASC, $parts); |
|
668 |
|
669 return implode('&', $parts); |
|
670 } |
|
671 |
|
672 /** |
|
673 * Checks whether the request is secure or not. |
|
674 * |
|
675 * @return Boolean |
|
676 * |
|
677 * @api |
|
678 */ |
|
679 public function isSecure() |
|
680 { |
|
681 return ( |
|
682 (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1) |
|
683 || |
|
684 (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1) |
|
685 || |
|
686 (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https') |
|
687 ); |
|
688 } |
|
689 |
|
690 /** |
|
691 * Returns the host name. |
|
692 * |
|
693 * @return string |
|
694 * |
|
695 * @api |
|
696 */ |
|
697 public function getHost() |
|
698 { |
|
699 if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) { |
|
700 $elements = explode(',', $host); |
|
701 |
|
702 $host = trim($elements[count($elements) - 1]); |
|
703 } else { |
|
704 if (!$host = $this->headers->get('HOST')) { |
|
705 if (!$host = $this->server->get('SERVER_NAME')) { |
|
706 $host = $this->server->get('SERVER_ADDR', ''); |
|
707 } |
|
708 } |
|
709 } |
|
710 |
|
711 // Remove port number from host |
|
712 $host = preg_replace('/:\d+$/', '', $host); |
|
713 |
|
714 return trim($host); |
|
715 } |
|
716 |
|
717 /** |
|
718 * Sets the request method. |
|
719 * |
|
720 * @param string $method |
|
721 * |
|
722 * @api |
|
723 */ |
|
724 public function setMethod($method) |
|
725 { |
|
726 $this->method = null; |
|
727 $this->server->set('REQUEST_METHOD', $method); |
|
728 } |
|
729 |
|
730 /** |
|
731 * Gets the request method. |
|
732 * |
|
733 * The method is always an uppercased string. |
|
734 * |
|
735 * @return string The request method |
|
736 * |
|
737 * @api |
|
738 */ |
|
739 public function getMethod() |
|
740 { |
|
741 if (null === $this->method) { |
|
742 $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); |
|
743 if ('POST' === $this->method) { |
|
744 $this->method = strtoupper($this->headers->get('X-HTTP-METHOD-OVERRIDE', $this->request->get('_method', 'POST'))); |
|
745 } |
|
746 } |
|
747 |
|
748 return $this->method; |
|
749 } |
|
750 |
|
751 /** |
|
752 * Gets the mime type associated with the format. |
|
753 * |
|
754 * @param string $format The format |
|
755 * |
|
756 * @return string The associated mime type (null if not found) |
|
757 * |
|
758 * @api |
|
759 */ |
|
760 public function getMimeType($format) |
|
761 { |
|
762 if (null === static::$formats) { |
|
763 static::initializeFormats(); |
|
764 } |
|
765 |
|
766 return isset(static::$formats[$format]) ? static::$formats[$format][0] : null; |
|
767 } |
|
768 |
|
769 /** |
|
770 * Gets the format associated with the mime type. |
|
771 * |
|
772 * @param string $mimeType The associated mime type |
|
773 * |
|
774 * @return string The format (null if not found) |
|
775 * |
|
776 * @api |
|
777 */ |
|
778 public function getFormat($mimeType) |
|
779 { |
|
780 if (false !== $pos = strpos($mimeType, ';')) { |
|
781 $mimeType = substr($mimeType, 0, $pos); |
|
782 } |
|
783 |
|
784 if (null === static::$formats) { |
|
785 static::initializeFormats(); |
|
786 } |
|
787 |
|
788 foreach (static::$formats as $format => $mimeTypes) { |
|
789 if (in_array($mimeType, (array) $mimeTypes)) { |
|
790 return $format; |
|
791 } |
|
792 } |
|
793 |
|
794 return null; |
|
795 } |
|
796 |
|
797 /** |
|
798 * Associates a format with mime types. |
|
799 * |
|
800 * @param string $format The format |
|
801 * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type) |
|
802 * |
|
803 * @api |
|
804 */ |
|
805 public function setFormat($format, $mimeTypes) |
|
806 { |
|
807 if (null === static::$formats) { |
|
808 static::initializeFormats(); |
|
809 } |
|
810 |
|
811 static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); |
|
812 } |
|
813 |
|
814 /** |
|
815 * Gets the request format. |
|
816 * |
|
817 * Here is the process to determine the format: |
|
818 * |
|
819 * * format defined by the user (with setRequestFormat()) |
|
820 * * _format request parameter |
|
821 * * $default |
|
822 * |
|
823 * @param string $default The default format |
|
824 * |
|
825 * @return string The request format |
|
826 * |
|
827 * @api |
|
828 */ |
|
829 public function getRequestFormat($default = 'html') |
|
830 { |
|
831 if (null === $this->format) { |
|
832 $this->format = $this->get('_format', $default); |
|
833 } |
|
834 |
|
835 return $this->format; |
|
836 } |
|
837 |
|
838 /** |
|
839 * Sets the request format. |
|
840 * |
|
841 * @param string $format The request format. |
|
842 * |
|
843 * @api |
|
844 */ |
|
845 public function setRequestFormat($format) |
|
846 { |
|
847 $this->format = $format; |
|
848 } |
|
849 |
|
850 /** |
|
851 * Checks whether the method is safe or not. |
|
852 * |
|
853 * @return Boolean |
|
854 * |
|
855 * @api |
|
856 */ |
|
857 public function isMethodSafe() |
|
858 { |
|
859 return in_array($this->getMethod(), array('GET', 'HEAD')); |
|
860 } |
|
861 |
|
862 /** |
|
863 * Returns the request body content. |
|
864 * |
|
865 * @param Boolean $asResource If true, a resource will be returned |
|
866 * |
|
867 * @return string|resource The request body content or a resource to read the body stream. |
|
868 */ |
|
869 public function getContent($asResource = false) |
|
870 { |
|
871 if (false === $this->content || (true === $asResource && null !== $this->content)) { |
|
872 throw new \LogicException('getContent() can only be called once when using the resource return type.'); |
|
873 } |
|
874 |
|
875 if (true === $asResource) { |
|
876 $this->content = false; |
|
877 |
|
878 return fopen('php://input', 'rb'); |
|
879 } |
|
880 |
|
881 if (null === $this->content) { |
|
882 $this->content = file_get_contents('php://input'); |
|
883 } |
|
884 |
|
885 return $this->content; |
|
886 } |
|
887 |
|
888 /** |
|
889 * Gets the Etags. |
|
890 * |
|
891 * @return array The entity tags |
|
892 */ |
|
893 public function getETags() |
|
894 { |
|
895 return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY); |
|
896 } |
|
897 |
|
898 public function isNoCache() |
|
899 { |
|
900 return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma'); |
|
901 } |
|
902 |
|
903 /** |
|
904 * Returns the preferred language. |
|
905 * |
|
906 * @param array $locales An array of ordered available locales |
|
907 * |
|
908 * @return string The preferred locale |
|
909 * |
|
910 * @api |
|
911 */ |
|
912 public function getPreferredLanguage(array $locales = null) |
|
913 { |
|
914 $preferredLanguages = $this->getLanguages(); |
|
915 |
|
916 if (null === $locales) { |
|
917 return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null; |
|
918 } |
|
919 |
|
920 if (!$preferredLanguages) { |
|
921 return $locales[0]; |
|
922 } |
|
923 |
|
924 $preferredLanguages = array_values(array_intersect($preferredLanguages, $locales)); |
|
925 |
|
926 return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0]; |
|
927 } |
|
928 |
|
929 /** |
|
930 * Gets a list of languages acceptable by the client browser. |
|
931 * |
|
932 * @return array Languages ordered in the user browser preferences |
|
933 * |
|
934 * @api |
|
935 */ |
|
936 public function getLanguages() |
|
937 { |
|
938 if (null !== $this->languages) { |
|
939 return $this->languages; |
|
940 } |
|
941 |
|
942 $languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language')); |
|
943 $this->languages = array(); |
|
944 foreach ($languages as $lang => $q) { |
|
945 if (strstr($lang, '-')) { |
|
946 $codes = explode('-', $lang); |
|
947 if ($codes[0] == 'i') { |
|
948 // Language not listed in ISO 639 that are not variants |
|
949 // of any listed language, which can be registered with the |
|
950 // i-prefix, such as i-cherokee |
|
951 if (count($codes) > 1) { |
|
952 $lang = $codes[1]; |
|
953 } |
|
954 } else { |
|
955 for ($i = 0, $max = count($codes); $i < $max; $i++) { |
|
956 if ($i == 0) { |
|
957 $lang = strtolower($codes[0]); |
|
958 } else { |
|
959 $lang .= '_'.strtoupper($codes[$i]); |
|
960 } |
|
961 } |
|
962 } |
|
963 } |
|
964 |
|
965 $this->languages[] = $lang; |
|
966 } |
|
967 |
|
968 return $this->languages; |
|
969 } |
|
970 |
|
971 /** |
|
972 * Gets a list of charsets acceptable by the client browser. |
|
973 * |
|
974 * @return array List of charsets in preferable order |
|
975 * |
|
976 * @api |
|
977 */ |
|
978 public function getCharsets() |
|
979 { |
|
980 if (null !== $this->charsets) { |
|
981 return $this->charsets; |
|
982 } |
|
983 |
|
984 return $this->charsets = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept-Charset'))); |
|
985 } |
|
986 |
|
987 /** |
|
988 * Gets a list of content types acceptable by the client browser |
|
989 * |
|
990 * @return array List of content types in preferable order |
|
991 * |
|
992 * @api |
|
993 */ |
|
994 public function getAcceptableContentTypes() |
|
995 { |
|
996 if (null !== $this->acceptableContentTypes) { |
|
997 return $this->acceptableContentTypes; |
|
998 } |
|
999 |
|
1000 return $this->acceptableContentTypes = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept'))); |
|
1001 } |
|
1002 |
|
1003 /** |
|
1004 * Returns true if the request is a XMLHttpRequest. |
|
1005 * |
|
1006 * It works if your JavaScript library set an X-Requested-With HTTP header. |
|
1007 * It is known to work with Prototype, Mootools, jQuery. |
|
1008 * |
|
1009 * @return Boolean true if the request is an XMLHttpRequest, false otherwise |
|
1010 * |
|
1011 * @api |
|
1012 */ |
|
1013 public function isXmlHttpRequest() |
|
1014 { |
|
1015 return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); |
|
1016 } |
|
1017 |
|
1018 /** |
|
1019 * Splits an Accept-* HTTP header. |
|
1020 * |
|
1021 * @param string $header Header to split |
|
1022 */ |
|
1023 public function splitHttpAcceptHeader($header) |
|
1024 { |
|
1025 if (!$header) { |
|
1026 return array(); |
|
1027 } |
|
1028 |
|
1029 $values = array(); |
|
1030 foreach (array_filter(explode(',', $header)) as $value) { |
|
1031 // Cut off any q-value that might come after a semi-colon |
|
1032 if ($pos = strpos($value, ';')) { |
|
1033 $q = (float) trim(substr($value, strpos($value, '=') + 1)); |
|
1034 $value = trim(substr($value, 0, $pos)); |
|
1035 } else { |
|
1036 $q = 1; |
|
1037 } |
|
1038 |
|
1039 if (0 < $q) { |
|
1040 $values[trim($value)] = $q; |
|
1041 } |
|
1042 } |
|
1043 |
|
1044 arsort($values); |
|
1045 reset($values); |
|
1046 |
|
1047 return $values; |
|
1048 } |
|
1049 |
|
1050 /* |
|
1051 * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24) |
|
1052 * |
|
1053 * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd). |
|
1054 * |
|
1055 * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
1056 */ |
|
1057 |
|
1058 protected function prepareRequestUri() |
|
1059 { |
|
1060 $requestUri = ''; |
|
1061 |
|
1062 if ($this->headers->has('X_REWRITE_URL')) { |
|
1063 // check this first so IIS will catch |
|
1064 $requestUri = $this->headers->get('X_REWRITE_URL'); |
|
1065 } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') { |
|
1066 // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem) |
|
1067 $requestUri = $this->server->get('UNENCODED_URL'); |
|
1068 } elseif ($this->server->has('REQUEST_URI')) { |
|
1069 $requestUri = $this->server->get('REQUEST_URI'); |
|
1070 // HTTP proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path |
|
1071 $schemeAndHttpHost = $this->getScheme().'://'.$this->getHttpHost(); |
|
1072 if (strpos($requestUri, $schemeAndHttpHost) === 0) { |
|
1073 $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); |
|
1074 } |
|
1075 } elseif ($this->server->has('ORIG_PATH_INFO')) { |
|
1076 // IIS 5.0, PHP as CGI |
|
1077 $requestUri = $this->server->get('ORIG_PATH_INFO'); |
|
1078 if ($this->server->get('QUERY_STRING')) { |
|
1079 $requestUri .= '?'.$this->server->get('QUERY_STRING'); |
|
1080 } |
|
1081 } |
|
1082 |
|
1083 return $requestUri; |
|
1084 } |
|
1085 |
|
1086 protected function prepareBaseUrl() |
|
1087 { |
|
1088 $filename = basename($this->server->get('SCRIPT_FILENAME')); |
|
1089 |
|
1090 if (basename($this->server->get('SCRIPT_NAME')) === $filename) { |
|
1091 $baseUrl = $this->server->get('SCRIPT_NAME'); |
|
1092 } elseif (basename($this->server->get('PHP_SELF')) === $filename) { |
|
1093 $baseUrl = $this->server->get('PHP_SELF'); |
|
1094 } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) { |
|
1095 $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility |
|
1096 } else { |
|
1097 // Backtrack up the script_filename to find the portion matching |
|
1098 // php_self |
|
1099 $path = $this->server->get('PHP_SELF', ''); |
|
1100 $file = $this->server->get('SCRIPT_FILENAME', ''); |
|
1101 $segs = explode('/', trim($file, '/')); |
|
1102 $segs = array_reverse($segs); |
|
1103 $index = 0; |
|
1104 $last = count($segs); |
|
1105 $baseUrl = ''; |
|
1106 do { |
|
1107 $seg = $segs[$index]; |
|
1108 $baseUrl = '/'.$seg.$baseUrl; |
|
1109 ++$index; |
|
1110 } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos)); |
|
1111 } |
|
1112 |
|
1113 // Does the baseUrl have anything in common with the request_uri? |
|
1114 $requestUri = $this->getRequestUri(); |
|
1115 |
|
1116 if ($baseUrl && 0 === strpos($requestUri, $baseUrl)) { |
|
1117 // full $baseUrl matches |
|
1118 return $baseUrl; |
|
1119 } |
|
1120 |
|
1121 if ($baseUrl && 0 === strpos($requestUri, dirname($baseUrl))) { |
|
1122 // directory portion of $baseUrl matches |
|
1123 return rtrim(dirname($baseUrl), '/'); |
|
1124 } |
|
1125 |
|
1126 $truncatedRequestUri = $requestUri; |
|
1127 if (($pos = strpos($requestUri, '?')) !== false) { |
|
1128 $truncatedRequestUri = substr($requestUri, 0, $pos); |
|
1129 } |
|
1130 |
|
1131 $basename = basename($baseUrl); |
|
1132 if (empty($basename) || !strpos($truncatedRequestUri, $basename)) { |
|
1133 // no match whatsoever; set it blank |
|
1134 return ''; |
|
1135 } |
|
1136 |
|
1137 // If using mod_rewrite or ISAPI_Rewrite strip the script filename |
|
1138 // out of baseUrl. $pos !== 0 makes sure it is not matching a value |
|
1139 // from PATH_INFO or QUERY_STRING |
|
1140 if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) { |
|
1141 $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); |
|
1142 } |
|
1143 |
|
1144 return rtrim($baseUrl, '/'); |
|
1145 } |
|
1146 |
|
1147 /** |
|
1148 * Prepares base path. |
|
1149 * |
|
1150 * @return string base path |
|
1151 */ |
|
1152 protected function prepareBasePath() |
|
1153 { |
|
1154 $filename = basename($this->server->get('SCRIPT_FILENAME')); |
|
1155 $baseUrl = $this->getBaseUrl(); |
|
1156 if (empty($baseUrl)) { |
|
1157 return ''; |
|
1158 } |
|
1159 |
|
1160 if (basename($baseUrl) === $filename) { |
|
1161 $basePath = dirname($baseUrl); |
|
1162 } else { |
|
1163 $basePath = $baseUrl; |
|
1164 } |
|
1165 |
|
1166 if ('\\' === DIRECTORY_SEPARATOR) { |
|
1167 $basePath = str_replace('\\', '/', $basePath); |
|
1168 } |
|
1169 |
|
1170 return rtrim($basePath, '/'); |
|
1171 } |
|
1172 |
|
1173 /** |
|
1174 * Prepares path info. |
|
1175 * |
|
1176 * @return string path info |
|
1177 */ |
|
1178 protected function preparePathInfo() |
|
1179 { |
|
1180 $baseUrl = $this->getBaseUrl(); |
|
1181 |
|
1182 if (null === ($requestUri = $this->getRequestUri())) { |
|
1183 return '/'; |
|
1184 } |
|
1185 |
|
1186 $pathInfo = '/'; |
|
1187 |
|
1188 // Remove the query string from REQUEST_URI |
|
1189 if ($pos = strpos($requestUri, '?')) { |
|
1190 $requestUri = substr($requestUri, 0, $pos); |
|
1191 } |
|
1192 |
|
1193 if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) { |
|
1194 // If substr() returns false then PATH_INFO is set to an empty string |
|
1195 return '/'; |
|
1196 } elseif (null === $baseUrl) { |
|
1197 return $requestUri; |
|
1198 } |
|
1199 |
|
1200 return (string) $pathInfo; |
|
1201 } |
|
1202 |
|
1203 /** |
|
1204 * Initializes HTTP request formats. |
|
1205 */ |
|
1206 static protected function initializeFormats() |
|
1207 { |
|
1208 static::$formats = array( |
|
1209 'html' => array('text/html', 'application/xhtml+xml'), |
|
1210 'txt' => array('text/plain'), |
|
1211 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), |
|
1212 'css' => array('text/css'), |
|
1213 'json' => array('application/json', 'application/x-json'), |
|
1214 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), |
|
1215 'rdf' => array('application/rdf+xml'), |
|
1216 'atom' => array('application/atom+xml'), |
|
1217 ); |
|
1218 } |
|
1219 } |