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