Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
15403 manish.sha 1
<?php
2
/**
3
 * CakeRequest
4
 *
5
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
6
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13
 * @link          http://cakephp.org CakePHP(tm) Project
14
 * @since         CakePHP(tm) v 2.0
15
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
16
 */
17
 
18
App::uses('Hash', 'Utility');
19
 
20
/**
21
 * A class that helps wrap Request information and particulars about a single request.
22
 * Provides methods commonly used to introspect on the request headers and request body.
23
 *
24
 * Has both an Array and Object interface. You can access framework parameters using indexes:
25
 *
26
 * `$request['controller']` or `$request->controller`.
27
 *
28
 * @package       Cake.Network
29
 */
30
class CakeRequest implements ArrayAccess {
31
 
32
/**
33
 * Array of parameters parsed from the URL.
34
 *
35
 * @var array
36
 */
37
	public $params = array(
38
		'plugin' => null,
39
		'controller' => null,
40
		'action' => null,
41
		'named' => array(),
42
		'pass' => array(),
43
	);
44
 
45
/**
46
 * Array of POST data. Will contain form data as well as uploaded files.
47
 * Inputs prefixed with 'data' will have the data prefix removed. If there is
48
 * overlap between an input prefixed with data and one without, the 'data' prefixed
49
 * value will take precedence.
50
 *
51
 * @var array
52
 */
53
	public $data = array();
54
 
55
/**
56
 * Array of querystring arguments
57
 *
58
 * @var array
59
 */
60
	public $query = array();
61
 
62
/**
63
 * The URL string used for the request.
64
 *
65
 * @var string
66
 */
67
	public $url;
68
 
69
/**
70
 * Base URL path.
71
 *
72
 * @var string
73
 */
74
	public $base = false;
75
 
76
/**
77
 * webroot path segment for the request.
78
 *
79
 * @var string
80
 */
81
	public $webroot = '/';
82
 
83
/**
84
 * The full address to the current request
85
 *
86
 * @var string
87
 */
88
	public $here = null;
89
 
90
/**
91
 * The built in detectors used with `is()` can be modified with `addDetector()`.
92
 *
93
 * There are several ways to specify a detector, see CakeRequest::addDetector() for the
94
 * various formats and ways to define detectors.
95
 *
96
 * @var array
97
 */
98
	protected $_detectors = array(
99
		'get' => array('env' => 'REQUEST_METHOD', 'value' => 'GET'),
100
		'post' => array('env' => 'REQUEST_METHOD', 'value' => 'POST'),
101
		'put' => array('env' => 'REQUEST_METHOD', 'value' => 'PUT'),
102
		'delete' => array('env' => 'REQUEST_METHOD', 'value' => 'DELETE'),
103
		'head' => array('env' => 'REQUEST_METHOD', 'value' => 'HEAD'),
104
		'options' => array('env' => 'REQUEST_METHOD', 'value' => 'OPTIONS'),
105
		'ssl' => array('env' => 'HTTPS', 'value' => 1),
106
		'ajax' => array('env' => 'HTTP_X_REQUESTED_WITH', 'value' => 'XMLHttpRequest'),
107
		'flash' => array('env' => 'HTTP_USER_AGENT', 'pattern' => '/^(Shockwave|Adobe) Flash/'),
108
		'mobile' => array('env' => 'HTTP_USER_AGENT', 'options' => array(
109
			'Android', 'AvantGo', 'BB10', 'BlackBerry', 'DoCoMo', 'Fennec', 'iPod', 'iPhone', 'iPad',
110
			'J2ME', 'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'Opera Mobi', 'PalmOS', 'PalmSource',
111
			'portalmmm', 'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\\.Browser',
112
			'webOS', 'Windows CE', 'Windows Phone OS', 'Xiino'
113
		)),
114
		'requested' => array('param' => 'requested', 'value' => 1)
115
	);
116
 
117
/**
118
 * Copy of php://input. Since this stream can only be read once in most SAPI's
119
 * keep a copy of it so users don't need to know about that detail.
120
 *
121
 * @var string
122
 */
123
	protected $_input = '';
124
 
125
/**
126
 * Constructor
127
 *
128
 * @param string $url Trimmed URL string to use. Should not contain the application base path.
129
 * @param bool $parseEnvironment Set to false to not auto parse the environment. ie. GET, POST and FILES.
130
 */
131
	public function __construct($url = null, $parseEnvironment = true) {
132
		$this->_base();
133
		if (empty($url)) {
134
			$url = $this->_url();
135
		}
136
		if ($url[0] === '/') {
137
			$url = substr($url, 1);
138
		}
139
		$this->url = $url;
140
 
141
		if ($parseEnvironment) {
142
			$this->_processPost();
143
			$this->_processGet();
144
			$this->_processFiles();
145
		}
146
		$this->here = $this->base . '/' . $this->url;
147
	}
148
 
149
/**
150
 * process the post data and set what is there into the object.
151
 * processed data is available at `$this->data`
152
 *
153
 * Will merge POST vars prefixed with `data`, and ones without
154
 * into a single array. Variables prefixed with `data` will overwrite those without.
155
 *
156
 * If you have mixed POST values be careful not to make any top level keys numeric
157
 * containing arrays. Hash::merge() is used to merge data, and it has possibly
158
 * unexpected behavior in this situation.
159
 *
160
 * @return void
161
 */
162
	protected function _processPost() {
163
		if ($_POST) {
164
			$this->data = $_POST;
165
		} elseif (($this->is('put') || $this->is('delete')) &&
166
			strpos(env('CONTENT_TYPE'), 'application/x-www-form-urlencoded') === 0
167
		) {
168
				$data = $this->_readInput();
169
				parse_str($data, $this->data);
170
		}
171
		if (ini_get('magic_quotes_gpc') === '1') {
172
			$this->data = stripslashes_deep($this->data);
173
		}
174
		if (env('HTTP_X_HTTP_METHOD_OVERRIDE')) {
175
			$this->data['_method'] = env('HTTP_X_HTTP_METHOD_OVERRIDE');
176
		}
177
		$isArray = is_array($this->data);
178
		if ($isArray && isset($this->data['_method'])) {
179
			if (!empty($_SERVER)) {
180
				$_SERVER['REQUEST_METHOD'] = $this->data['_method'];
181
			} else {
182
				$_ENV['REQUEST_METHOD'] = $this->data['_method'];
183
			}
184
			unset($this->data['_method']);
185
		}
186
		if ($isArray && isset($this->data['data'])) {
187
			$data = $this->data['data'];
188
			if (count($this->data) <= 1) {
189
				$this->data = $data;
190
			} else {
191
				unset($this->data['data']);
192
				$this->data = Hash::merge($this->data, $data);
193
			}
194
		}
195
	}
196
 
197
/**
198
 * Process the GET parameters and move things into the object.
199
 *
200
 * @return void
201
 */
202
	protected function _processGet() {
203
		if (ini_get('magic_quotes_gpc') === '1') {
204
			$query = stripslashes_deep($_GET);
205
		} else {
206
			$query = $_GET;
207
		}
208
 
209
		$unsetUrl = '/' . str_replace(array('.', ' '), '_', urldecode($this->url));
210
		unset($query[$unsetUrl]);
211
		unset($query[$this->base . $unsetUrl]);
212
		if (strpos($this->url, '?') !== false) {
213
			list(, $querystr) = explode('?', $this->url);
214
			parse_str($querystr, $queryArgs);
215
			$query += $queryArgs;
216
		}
217
		if (isset($this->params['url'])) {
218
			$query = array_merge($this->params['url'], $query);
219
		}
220
		$this->query = $query;
221
	}
222
 
223
/**
224
 * Get the request uri. Looks in PATH_INFO first, as this is the exact value we need prepared
225
 * by PHP. Following that, REQUEST_URI, PHP_SELF, HTTP_X_REWRITE_URL and argv are checked in that order.
226
 * Each of these server variables have the base path, and query strings stripped off
227
 *
228
 * @return string URI The CakePHP request path that is being accessed.
229
 */
230
	protected function _url() {
231
		if (!empty($_SERVER['PATH_INFO'])) {
232
			return $_SERVER['PATH_INFO'];
233
		} elseif (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '://') === false) {
234
			$uri = $_SERVER['REQUEST_URI'];
235
		} elseif (isset($_SERVER['REQUEST_URI'])) {
236
			$qPosition = strpos($_SERVER['REQUEST_URI'], '?');
237
			if ($qPosition !== false && strpos($_SERVER['REQUEST_URI'], '://') > $qPosition) {
238
				$uri = $_SERVER['REQUEST_URI'];
239
			} else {
240
				$uri = substr($_SERVER['REQUEST_URI'], strlen(Configure::read('App.fullBaseUrl')));
241
			}
242
		} elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) {
243
			$uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']);
244
		} elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
245
			$uri = $_SERVER['HTTP_X_REWRITE_URL'];
246
		} elseif ($var = env('argv')) {
247
			$uri = $var[0];
248
		}
249
 
250
		$base = $this->base;
251
 
252
		if (strlen($base) > 0 && strpos($uri, $base) === 0) {
253
			$uri = substr($uri, strlen($base));
254
		}
255
		if (strpos($uri, '?') !== false) {
256
			list($uri) = explode('?', $uri, 2);
257
		}
258
		if (empty($uri) || $uri === '/' || $uri === '//' || $uri === '/index.php') {
259
			$uri = '/';
260
		}
261
		$endsWithIndex = '/webroot/index.php';
262
		$endsWithLength = strlen($endsWithIndex);
263
		if (strlen($uri) >= $endsWithLength &&
264
			substr($uri, -$endsWithLength) === $endsWithIndex
265
		) {
266
			$uri = '/';
267
		}
268
		return $uri;
269
	}
270
 
271
/**
272
 * Returns a base URL and sets the proper webroot
273
 *
274
 * If CakePHP is called with index.php in the URL even though
275
 * URL Rewriting is activated (and thus not needed) it swallows
276
 * the unnecessary part from $base to prevent issue #3318.
277
 *
278
 * @return string Base URL
279
 * @link https://cakephp.lighthouseapp.com/projects/42648-cakephp/tickets/3318
280
 */
281
	protected function _base() {
282
		$dir = $webroot = null;
283
		$config = Configure::read('App');
284
		extract($config);
285
 
286
		if (!isset($base)) {
287
			$base = $this->base;
288
		}
289
		if ($base !== false) {
290
			$this->webroot = $base . '/';
291
			return $this->base = $base;
292
		}
293
 
294
		if (!$baseUrl) {
295
			$base = dirname(env('PHP_SELF'));
296
 
297
			$indexPos = strpos($base, '/webroot/index.php');
298
			if ($indexPos !== false) {
299
				$base = substr($base, 0, $indexPos) . '/webroot';
300
			}
301
			if ($webroot === 'webroot' && $webroot === basename($base)) {
302
				$base = dirname($base);
303
			}
304
			if ($dir === 'app' && $dir === basename($base)) {
305
				$base = dirname($base);
306
			}
307
 
308
			if ($base === DS || $base === '.') {
309
				$base = '';
310
			}
311
			$base = implode('/', array_map('rawurlencode', explode('/', $base)));
312
			$this->webroot = $base . '/';
313
 
314
			return $this->base = $base;
315
		}
316
 
317
		$file = '/' . basename($baseUrl);
318
		$base = dirname($baseUrl);
319
 
320
		if ($base === DS || $base === '.') {
321
			$base = '';
322
		}
323
		$this->webroot = $base . '/';
324
 
325
		$docRoot = env('DOCUMENT_ROOT');
326
		$docRootContainsWebroot = strpos($docRoot, $dir . DS . $webroot);
327
 
328
		if (!empty($base) || !$docRootContainsWebroot) {
329
			if (strpos($this->webroot, '/' . $dir . '/') === false) {
330
				$this->webroot .= $dir . '/';
331
			}
332
			if (strpos($this->webroot, '/' . $webroot . '/') === false) {
333
				$this->webroot .= $webroot . '/';
334
			}
335
		}
336
		return $this->base = $base . $file;
337
	}
338
 
339
/**
340
 * Process $_FILES and move things into the object.
341
 *
342
 * @return void
343
 */
344
	protected function _processFiles() {
345
		if (isset($_FILES) && is_array($_FILES)) {
346
			foreach ($_FILES as $name => $data) {
347
				if ($name !== 'data') {
348
					$this->params['form'][$name] = $data;
349
				}
350
			}
351
		}
352
 
353
		if (isset($_FILES['data'])) {
354
			foreach ($_FILES['data'] as $key => $data) {
355
				$this->_processFileData('', $data, $key);
356
			}
357
		}
358
	}
359
 
360
/**
361
 * Recursively walks the FILES array restructuring the data
362
 * into something sane and useable.
363
 *
364
 * @param string $path The dot separated path to insert $data into.
365
 * @param array $data The data to traverse/insert.
366
 * @param string $field The terminal field name, which is the top level key in $_FILES.
367
 * @return void
368
 */
369
	protected function _processFileData($path, $data, $field) {
370
		foreach ($data as $key => $fields) {
371
			$newPath = $key;
372
			if (strlen($path) > 0) {
373
				$newPath = $path . '.' . $key;
374
			}
375
			if (is_array($fields)) {
376
				$this->_processFileData($newPath, $fields, $field);
377
			} else {
378
				$newPath .= '.' . $field;
379
				$this->data = Hash::insert($this->data, $newPath, $fields);
380
			}
381
		}
382
	}
383
 
384
/**
385
 * Get the IP the client is using, or says they are using.
386
 *
387
 * @param bool $safe Use safe = false when you think the user might manipulate their HTTP_CLIENT_IP
388
 *   header. Setting $safe = false will also look at HTTP_X_FORWARDED_FOR
389
 * @return string The client IP.
390
 */
391
	public function clientIp($safe = true) {
392
		if (!$safe && env('HTTP_X_FORWARDED_FOR')) {
393
			$ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
394
		} else {
395
			if (env('HTTP_CLIENT_IP')) {
396
				$ipaddr = env('HTTP_CLIENT_IP');
397
			} else {
398
				$ipaddr = env('REMOTE_ADDR');
399
			}
400
		}
401
 
402
		if (env('HTTP_CLIENTADDRESS')) {
403
			$tmpipaddr = env('HTTP_CLIENTADDRESS');
404
 
405
			if (!empty($tmpipaddr)) {
406
				$ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
407
			}
408
		}
409
		return trim($ipaddr);
410
	}
411
 
412
/**
413
 * Returns the referer that referred this request.
414
 *
415
 * @param bool $local Attempt to return a local address. Local addresses do not contain hostnames.
416
 * @return string The referring address for this request.
417
 */
418
	public function referer($local = false) {
419
		$ref = env('HTTP_REFERER');
420
 
421
		$base = Configure::read('App.fullBaseUrl') . $this->webroot;
422
		if (!empty($ref) && !empty($base)) {
423
			if ($local && strpos($ref, $base) === 0) {
424
				$ref = substr($ref, strlen($base));
425
				if ($ref[0] !== '/') {
426
					$ref = '/' . $ref;
427
				}
428
				return $ref;
429
			} elseif (!$local) {
430
				return $ref;
431
			}
432
		}
433
		return '/';
434
	}
435
 
436
/**
437
 * Missing method handler, handles wrapping older style isAjax() type methods
438
 *
439
 * @param string $name The method called
440
 * @param array $params Array of parameters for the method call
441
 * @return mixed
442
 * @throws CakeException when an invalid method is called.
443
 */
444
	public function __call($name, $params) {
445
		if (strpos($name, 'is') === 0) {
446
			$type = strtolower(substr($name, 2));
447
			return $this->is($type);
448
		}
449
		throw new CakeException(__d('cake_dev', 'Method %s does not exist', $name));
450
	}
451
 
452
/**
453
 * Magic get method allows access to parsed routing parameters directly on the object.
454
 *
455
 * Allows access to `$this->params['controller']` via `$this->controller`
456
 *
457
 * @param string $name The property being accessed.
458
 * @return mixed Either the value of the parameter or null.
459
 */
460
	public function __get($name) {
461
		if (isset($this->params[$name])) {
462
			return $this->params[$name];
463
		}
464
		return null;
465
	}
466
 
467
/**
468
 * Magic isset method allows isset/empty checks
469
 * on routing parameters.
470
 *
471
 * @param string $name The property being accessed.
472
 * @return bool Existence
473
 */
474
	public function __isset($name) {
475
		return isset($this->params[$name]);
476
	}
477
 
478
/**
479
 * Check whether or not a Request is a certain type.
480
 *
481
 * Uses the built in detection rules as well as additional rules
482
 * defined with CakeRequest::addDetector(). Any detector can be called
483
 * as `is($type)` or `is$Type()`.
484
 *
485
 * @param string|array $type The type of request you want to check. If an array
486
 *   this method will return true if the request matches any type.
487
 * @return bool Whether or not the request is the type you are checking.
488
 */
489
	public function is($type) {
490
		if (is_array($type)) {
491
			$result = array_map(array($this, 'is'), $type);
492
			return count(array_filter($result)) > 0;
493
		}
494
		$type = strtolower($type);
495
		if (!isset($this->_detectors[$type])) {
496
			return false;
497
		}
498
		$detect = $this->_detectors[$type];
499
		if (isset($detect['env'])) {
500
			if (isset($detect['value'])) {
501
				return env($detect['env']) == $detect['value'];
502
			}
503
			if (isset($detect['pattern'])) {
504
				return (bool)preg_match($detect['pattern'], env($detect['env']));
505
			}
506
			if (isset($detect['options'])) {
507
				$pattern = '/' . implode('|', $detect['options']) . '/i';
508
				return (bool)preg_match($pattern, env($detect['env']));
509
			}
510
		}
511
		if (isset($detect['param'])) {
512
			$key = $detect['param'];
513
			if (isset($detect['value'])) {
514
				$value = $detect['value'];
515
				return isset($this->params[$key]) ? $this->params[$key] == $value : false;
516
			}
517
			if (isset($detect['options'])) {
518
				return isset($this->params[$key]) ? in_array($this->params[$key], $detect['options']) : false;
519
			}
520
		}
521
		if (isset($detect['callback']) && is_callable($detect['callback'])) {
522
			return call_user_func($detect['callback'], $this);
523
		}
524
		return false;
525
	}
526
 
527
/**
528
 * Check that a request matches all the given types.
529
 *
530
 * Allows you to test multiple types and union the results.
531
 * See CakeRequest::is() for how to add additional types and the
532
 * built-in types.
533
 *
534
 * @param array $types The types to check.
535
 * @return bool Success.
536
 * @see CakeRequest::is()
537
 */
538
	public function isAll(array $types) {
539
		$result = array_filter(array_map(array($this, 'is'), $types));
540
		return count($result) === count($types);
541
	}
542
 
543
/**
544
 * Add a new detector to the list of detectors that a request can use.
545
 * There are several different formats and types of detectors that can be set.
546
 *
547
 * ### Environment value comparison
548
 *
549
 * An environment value comparison, compares a value fetched from `env()` to a known value
550
 * the environment value is equality checked against the provided value.
551
 *
552
 * e.g `addDetector('post', array('env' => 'REQUEST_METHOD', 'value' => 'POST'))`
553
 *
554
 * ### Pattern value comparison
555
 *
556
 * Pattern value comparison allows you to compare a value fetched from `env()` to a regular expression.
557
 *
558
 * e.g `addDetector('iphone', array('env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'));`
559
 *
560
 * ### Option based comparison
561
 *
562
 * Option based comparisons use a list of options to create a regular expression. Subsequent calls
563
 * to add an already defined options detector will merge the options.
564
 *
565
 * e.g `addDetector('mobile', array('env' => 'HTTP_USER_AGENT', 'options' => array('Fennec')));`
566
 *
567
 * ### Callback detectors
568
 *
569
 * Callback detectors allow you to provide a 'callback' type to handle the check. The callback will
570
 * receive the request object as its only parameter.
571
 *
572
 * e.g `addDetector('custom', array('callback' => array('SomeClass', 'somemethod')));`
573
 *
574
 * ### Request parameter detectors
575
 *
576
 * Allows for custom detectors on the request parameters.
577
 *
578
 * e.g `addDetector('requested', array('param' => 'requested', 'value' => 1)`
579
 *
580
 * You can also make parameter detectors that accept multiple values
581
 * using the `options` key. This is useful when you want to check
582
 * if a request parameter is in a list of options.
583
 *
584
 * `addDetector('extension', array('param' => 'ext', 'options' => array('pdf', 'csv'))`
585
 *
586
 * @param string $name The name of the detector.
587
 * @param array $options The options for the detector definition. See above.
588
 * @return void
589
 */
590
	public function addDetector($name, $options) {
591
		$name = strtolower($name);
592
		if (isset($this->_detectors[$name]) && isset($options['options'])) {
593
			$options = Hash::merge($this->_detectors[$name], $options);
594
		}
595
		$this->_detectors[$name] = $options;
596
	}
597
 
598
/**
599
 * Add parameters to the request's parsed parameter set. This will overwrite any existing parameters.
600
 * This modifies the parameters available through `$request->params`.
601
 *
602
 * @param array $params Array of parameters to merge in
603
 * @return $this
604
 */
605
	public function addParams($params) {
606
		$this->params = array_merge($this->params, (array)$params);
607
		return $this;
608
	}
609
 
610
/**
611
 * Add paths to the requests' paths vars. This will overwrite any existing paths.
612
 * Provides an easy way to modify, here, webroot and base.
613
 *
614
 * @param array $paths Array of paths to merge in
615
 * @return $this
616
 */
617
	public function addPaths($paths) {
618
		foreach (array('webroot', 'here', 'base') as $element) {
619
			if (isset($paths[$element])) {
620
				$this->{$element} = $paths[$element];
621
			}
622
		}
623
		return $this;
624
	}
625
 
626
/**
627
 * Get the value of the current requests URL. Will include named parameters and querystring arguments.
628
 *
629
 * @param bool $base Include the base path, set to false to trim the base path off.
630
 * @return string the current request URL including query string args.
631
 */
632
	public function here($base = true) {
633
		$url = $this->here;
634
		if (!empty($this->query)) {
635
			$url .= '?' . http_build_query($this->query, null, '&');
636
		}
637
		if (!$base) {
638
			$url = preg_replace('/^' . preg_quote($this->base, '/') . '/', '', $url, 1);
639
		}
640
		return $url;
641
	}
642
 
643
/**
644
 * Read an HTTP header from the Request information.
645
 *
646
 * @param string $name Name of the header you want.
647
 * @return mixed Either false on no header being set or the value of the header.
648
 */
649
	public static function header($name) {
650
		$name = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
651
		if (isset($_SERVER[$name])) {
652
			return $_SERVER[$name];
653
		}
654
		return false;
655
	}
656
 
657
/**
658
 * Get the HTTP method used for this request.
659
 * There are a few ways to specify a method.
660
 *
661
 * - If your client supports it you can use native HTTP methods.
662
 * - You can set the HTTP-X-Method-Override header.
663
 * - You can submit an input with the name `_method`
664
 *
665
 * Any of these 3 approaches can be used to set the HTTP method used
666
 * by CakePHP internally, and will effect the result of this method.
667
 *
668
 * @return string The name of the HTTP method used.
669
 */
670
	public function method() {
671
		return env('REQUEST_METHOD');
672
	}
673
 
674
/**
675
 * Get the host that the request was handled on.
676
 *
677
 * @param bool $trustProxy Whether or not to trust the proxy host.
678
 * @return string
679
 */
680
	public function host($trustProxy = false) {
681
		if ($trustProxy) {
682
			return env('HTTP_X_FORWARDED_HOST');
683
		}
684
		return env('HTTP_HOST');
685
	}
686
 
687
/**
688
 * Get the domain name and include $tldLength segments of the tld.
689
 *
690
 * @param int $tldLength Number of segments your tld contains. For example: `example.com` contains 1 tld.
691
 *   While `example.co.uk` contains 2.
692
 * @return string Domain name without subdomains.
693
 */
694
	public function domain($tldLength = 1) {
695
		$segments = explode('.', $this->host());
696
		$domain = array_slice($segments, -1 * ($tldLength + 1));
697
		return implode('.', $domain);
698
	}
699
 
700
/**
701
 * Get the subdomains for a host.
702
 *
703
 * @param int $tldLength Number of segments your tld contains. For example: `example.com` contains 1 tld.
704
 *   While `example.co.uk` contains 2.
705
 * @return array An array of subdomains.
706
 */
707
	public function subdomains($tldLength = 1) {
708
		$segments = explode('.', $this->host());
709
		return array_slice($segments, 0, -1 * ($tldLength + 1));
710
	}
711
 
712
/**
713
 * Find out which content types the client accepts or check if they accept a
714
 * particular type of content.
715
 *
716
 * #### Get all types:
717
 *
718
 * `$this->request->accepts();`
719
 *
720
 * #### Check for a single type:
721
 *
722
 * `$this->request->accepts('application/json');`
723
 *
724
 * This method will order the returned content types by the preference values indicated
725
 * by the client.
726
 *
727
 * @param string $type The content type to check for. Leave null to get all types a client accepts.
728
 * @return mixed Either an array of all the types the client accepts or a boolean if they accept the
729
 *   provided type.
730
 */
731
	public function accepts($type = null) {
732
		$raw = $this->parseAccept();
733
		$accept = array();
734
		foreach ($raw as $types) {
735
			$accept = array_merge($accept, $types);
736
		}
737
		if ($type === null) {
738
			return $accept;
739
		}
740
		return in_array($type, $accept);
741
	}
742
 
743
/**
744
 * Parse the HTTP_ACCEPT header and return a sorted array with content types
745
 * as the keys, and pref values as the values.
746
 *
747
 * Generally you want to use CakeRequest::accept() to get a simple list
748
 * of the accepted content types.
749
 *
750
 * @return array An array of prefValue => array(content/types)
751
 */
752
	public function parseAccept() {
753
		return $this->_parseAcceptWithQualifier($this->header('accept'));
754
	}
755
 
756
/**
757
 * Get the languages accepted by the client, or check if a specific language is accepted.
758
 *
759
 * Get the list of accepted languages:
760
 *
761
 * ``` CakeRequest::acceptLanguage(); ```
762
 *
763
 * Check if a specific language is accepted:
764
 *
765
 * ``` CakeRequest::acceptLanguage('es-es'); ```
766
 *
767
 * @param string $language The language to test.
768
 * @return mixed If a $language is provided, a boolean. Otherwise the array of accepted languages.
769
 */
770
	public static function acceptLanguage($language = null) {
771
		$raw = self::_parseAcceptWithQualifier(self::header('Accept-Language'));
772
		$accept = array();
773
		foreach ($raw as $languages) {
774
			foreach ($languages as &$lang) {
775
				if (strpos($lang, '_')) {
776
					$lang = str_replace('_', '-', $lang);
777
				}
778
				$lang = strtolower($lang);
779
			}
780
			$accept = array_merge($accept, $languages);
781
		}
782
		if ($language === null) {
783
			return $accept;
784
		}
785
		return in_array(strtolower($language), $accept);
786
	}
787
 
788
/**
789
 * Parse Accept* headers with qualifier options.
790
 *
791
 * Only qualifiers will be extracted, any other accept extensions will be
792
 * discarded as they are not frequently used.
793
 *
794
 * @param string $header Header to parse.
795
 * @return array
796
 */
797
	protected static function _parseAcceptWithQualifier($header) {
798
		$accept = array();
799
		$header = explode(',', $header);
800
		foreach (array_filter($header) as $value) {
801
			$prefValue = '1.0';
802
			$value = trim($value);
803
 
804
			$semiPos = strpos($value, ';');
805
			if ($semiPos !== false) {
806
				$params = explode(';', $value);
807
				$value = trim($params[0]);
808
				foreach ($params as $param) {
809
					$qPos = strpos($param, 'q=');
810
					if ($qPos !== false) {
811
						$prefValue = substr($param, $qPos + 2);
812
					}
813
				}
814
			}
815
 
816
			if (!isset($accept[$prefValue])) {
817
				$accept[$prefValue] = array();
818
			}
819
			if ($prefValue) {
820
				$accept[$prefValue][] = $value;
821
			}
822
		}
823
		krsort($accept);
824
		return $accept;
825
	}
826
 
827
/**
828
 * Provides a read accessor for `$this->query`. Allows you
829
 * to use a syntax similar to `CakeSession` for reading URL query data.
830
 *
831
 * @param string $name Query string variable name
832
 * @return mixed The value being read
833
 */
834
	public function query($name) {
835
		return Hash::get($this->query, $name);
836
	}
837
 
838
/**
839
 * Provides a read/write accessor for `$this->data`. Allows you
840
 * to use a syntax similar to `CakeSession` for reading post data.
841
 *
842
 * ## Reading values.
843
 *
844
 * `$request->data('Post.title');`
845
 *
846
 * When reading values you will get `null` for keys/values that do not exist.
847
 *
848
 * ## Writing values
849
 *
850
 * `$request->data('Post.title', 'New post!');`
851
 *
852
 * You can write to any value, even paths/keys that do not exist, and the arrays
853
 * will be created for you.
854
 *
855
 * @param string $name Dot separated name of the value to read/write, one or more args.
856
 * @return mixed|$this Either the value being read, or $this so you can chain consecutive writes.
857
 */
858
	public function data($name) {
859
		$args = func_get_args();
860
		if (count($args) === 2) {
861
			$this->data = Hash::insert($this->data, $name, $args[1]);
862
			return $this;
863
		}
864
		return Hash::get($this->data, $name);
865
	}
866
 
867
/**
868
 * Safely access the values in $this->params.
869
 *
870
 * @param string $name The name of the parameter to get.
871
 * @return mixed The value of the provided parameter. Will
872
 *   return false if the parameter doesn't exist or is falsey.
873
 */
874
	public function param($name) {
875
		$args = func_get_args();
876
		if (count($args) === 2) {
877
			$this->params = Hash::insert($this->params, $name, $args[1]);
878
			return $this;
879
		}
880
		if (!isset($this->params[$name])) {
881
			return Hash::get($this->params, $name, false);
882
		}
883
		return $this->params[$name];
884
	}
885
 
886
/**
887
 * Read data from `php://input`. Useful when interacting with XML or JSON
888
 * request body content.
889
 *
890
 * Getting input with a decoding function:
891
 *
892
 * `$this->request->input('json_decode');`
893
 *
894
 * Getting input using a decoding function, and additional params:
895
 *
896
 * `$this->request->input('Xml::build', array('return' => 'DOMDocument'));`
897
 *
898
 * Any additional parameters are applied to the callback in the order they are given.
899
 *
900
 * @param string $callback A decoding callback that will convert the string data to another
901
 *     representation. Leave empty to access the raw input data. You can also
902
 *     supply additional parameters for the decoding callback using var args, see above.
903
 * @return The decoded/processed request data.
904
 */
905
	public function input($callback = null) {
906
		$input = $this->_readInput();
907
		$args = func_get_args();
908
		if (!empty($args)) {
909
			$callback = array_shift($args);
910
			array_unshift($args, $input);
911
			return call_user_func_array($callback, $args);
912
		}
913
		return $input;
914
	}
915
 
916
/**
917
 * Modify data originally from `php://input`. Useful for altering json/xml data
918
 * in middleware or DispatcherFilters before it gets to RequestHandlerComponent
919
 *
920
 * @param string $input A string to replace original parsed data from input()
921
 * @return void
922
 */
923
	public function setInput($input) {
924
		$this->_input = $input;
925
	}
926
 
927
/**
928
 * Allow only certain HTTP request methods. If the request method does not match
929
 * a 405 error will be shown and the required "Allow" response header will be set.
930
 *
931
 * Example:
932
 *
933
 * $this->request->allowMethod('post', 'delete');
934
 * or
935
 * $this->request->allowMethod(array('post', 'delete'));
936
 *
937
 * If the request would be GET, response header "Allow: POST, DELETE" will be set
938
 * and a 405 error will be returned.
939
 *
940
 * @param string|array $methods Allowed HTTP request methods.
941
 * @return bool true
942
 * @throws MethodNotAllowedException
943
 */
944
	public function allowMethod($methods) {
945
		if (!is_array($methods)) {
946
			$methods = func_get_args();
947
		}
948
		foreach ($methods as $method) {
949
			if ($this->is($method)) {
950
				return true;
951
			}
952
		}
953
		$allowed = strtoupper(implode(', ', $methods));
954
		$e = new MethodNotAllowedException();
955
		$e->responseHeader('Allow', $allowed);
956
		throw $e;
957
	}
958
 
959
/**
960
 * Alias of CakeRequest::allowMethod() for backwards compatibility.
961
 *
962
 * @param string|array $methods Allowed HTTP request methods.
963
 * @return bool true
964
 * @throws MethodNotAllowedException
965
 * @see CakeRequest::allowMethod()
966
 * @deprecated 3.0.0 Since 2.5, use CakeRequest::allowMethod() instead.
967
 */
968
	public function onlyAllow($methods) {
969
		if (!is_array($methods)) {
970
			$methods = func_get_args();
971
		}
972
		return $this->allowMethod($methods);
973
	}
974
 
975
/**
976
 * Read data from php://input, mocked in tests.
977
 *
978
 * @return string contents of php://input
979
 */
980
	protected function _readInput() {
981
		if (empty($this->_input)) {
982
			$fh = fopen('php://input', 'r');
983
			$content = stream_get_contents($fh);
984
			fclose($fh);
985
			$this->_input = $content;
986
		}
987
		return $this->_input;
988
	}
989
 
990
/**
991
 * Array access read implementation
992
 *
993
 * @param string $name Name of the key being accessed.
994
 * @return mixed
995
 */
996
	public function offsetGet($name) {
997
		if (isset($this->params[$name])) {
998
			return $this->params[$name];
999
		}
1000
		if ($name === 'url') {
1001
			return $this->query;
1002
		}
1003
		if ($name === 'data') {
1004
			return $this->data;
1005
		}
1006
		return null;
1007
	}
1008
 
1009
/**
1010
 * Array access write implementation
1011
 *
1012
 * @param string $name Name of the key being written
1013
 * @param mixed $value The value being written.
1014
 * @return void
1015
 */
1016
	public function offsetSet($name, $value) {
1017
		$this->params[$name] = $value;
1018
	}
1019
 
1020
/**
1021
 * Array access isset() implementation
1022
 *
1023
 * @param string $name thing to check.
1024
 * @return bool
1025
 */
1026
	public function offsetExists($name) {
1027
		return isset($this->params[$name]);
1028
	}
1029
 
1030
/**
1031
 * Array access unset() implementation
1032
 *
1033
 * @param string $name Name to unset.
1034
 * @return void
1035
 */
1036
	public function offsetUnset($name) {
1037
		unset($this->params[$name]);
1038
	}
1039
 
1040
}