Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

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