1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 2.0.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Http;
16:
17: use ArrayAccess;
18: use BadMethodCallException;
19: use Cake\Core\Configure;
20: use Cake\Http\Cookie\CookieCollection;
21: use Cake\Http\Exception\MethodNotAllowedException;
22: use Cake\Http\Session;
23: use Cake\Utility\Hash;
24: use InvalidArgumentException;
25: use Psr\Http\Message\ServerRequestInterface;
26: use Psr\Http\Message\StreamInterface;
27: use Psr\Http\Message\UploadedFileInterface;
28: use Psr\Http\Message\UriInterface;
29: use Zend\Diactoros\PhpInputStream;
30: use Zend\Diactoros\Stream;
31: use Zend\Diactoros\UploadedFile;
32:
33: /**
34: * A class that helps wrap Request information and particulars about a single request.
35: * Provides methods commonly used to introspect on the request headers and request body.
36: */
37: class ServerRequest implements ArrayAccess, ServerRequestInterface
38: {
39:
40: /**
41: * Array of parameters parsed from the URL.
42: *
43: * @var array
44: * @deprecated 3.4.0 This public property will be removed in 4.0.0. Use getParam() instead.
45: */
46: protected $params = [
47: 'plugin' => null,
48: 'controller' => null,
49: 'action' => null,
50: '_ext' => null,
51: 'pass' => []
52: ];
53:
54: /**
55: * Array of POST data. Will contain form data as well as uploaded files.
56: * In PUT/PATCH/DELETE requests this property will contain the form-urlencoded
57: * data.
58: *
59: * @var null|array|object
60: * @deprecated 3.4.0 This public property will be removed in 4.0.0. Use getData() instead.
61: */
62: protected $data = [];
63:
64: /**
65: * Array of query string arguments
66: *
67: * @var array
68: * @deprecated 3.4.0 This public property will be removed in 4.0.0. Use getQuery() or getQueryParams() instead.
69: */
70: protected $query = [];
71:
72: /**
73: * Array of cookie data.
74: *
75: * @var array
76: * @deprecated 3.4.0 This public property will be removed in 4.0.0. Use getCookie() instead.
77: */
78: protected $cookies = [];
79:
80: /**
81: * Array of environment data.
82: *
83: * @var array
84: */
85: protected $_environment = [];
86:
87: /**
88: * The URL string used for the request.
89: *
90: * @var string
91: * @deprecated 3.6.0 This public property will be removed in 4.0.0. Use getPath() instead.
92: */
93: protected $url;
94:
95: /**
96: * Base URL path.
97: *
98: * @var string
99: * @deprecated 3.4.0 This public property will be removed in 4.0.0. Use getAttribute('base') instead.
100: */
101: protected $base;
102:
103: /**
104: * webroot path segment for the request.
105: *
106: * @var string
107: * @deprecated 3.4.0 This public property will be removed in 4.0.0. Use getAttribute('webroot') instead.
108: */
109: protected $webroot = '/';
110:
111: /**
112: * The full address to the current request
113: *
114: * @var string
115: * @deprecated 3.4.0 This public property will be removed in 4.0.0. Use getAttribute('here') or getUri()->getPath() instead.
116: */
117: protected $here;
118:
119: /**
120: * Whether or not to trust HTTP_X headers set by most load balancers.
121: * Only set to true if your application runs behind load balancers/proxies
122: * that you control.
123: *
124: * @var bool
125: */
126: public $trustProxy = false;
127:
128: /**
129: * trusted proxies list
130: *
131: * @var array
132: */
133: protected $trustedProxies = [];
134:
135: /**
136: * Contents of php://input
137: *
138: * @var string
139: */
140: protected $_input;
141:
142: /**
143: * The built in detectors used with `is()` can be modified with `addDetector()`.
144: *
145: * There are several ways to specify a detector, see \Cake\Http\ServerRequest::addDetector() for the
146: * various formats and ways to define detectors.
147: *
148: * @var array
149: */
150: protected static $_detectors = [
151: 'get' => ['env' => 'REQUEST_METHOD', 'value' => 'GET'],
152: 'post' => ['env' => 'REQUEST_METHOD', 'value' => 'POST'],
153: 'put' => ['env' => 'REQUEST_METHOD', 'value' => 'PUT'],
154: 'patch' => ['env' => 'REQUEST_METHOD', 'value' => 'PATCH'],
155: 'delete' => ['env' => 'REQUEST_METHOD', 'value' => 'DELETE'],
156: 'head' => ['env' => 'REQUEST_METHOD', 'value' => 'HEAD'],
157: 'options' => ['env' => 'REQUEST_METHOD', 'value' => 'OPTIONS'],
158: 'ssl' => ['env' => 'HTTPS', 'options' => [1, 'on']],
159: 'ajax' => ['env' => 'HTTP_X_REQUESTED_WITH', 'value' => 'XMLHttpRequest'],
160: 'flash' => ['env' => 'HTTP_USER_AGENT', 'pattern' => '/^(Shockwave|Adobe) Flash/'],
161: 'requested' => ['param' => 'requested', 'value' => 1],
162: 'json' => ['accept' => ['application/json'], 'param' => '_ext', 'value' => 'json'],
163: 'xml' => ['accept' => ['application/xml', 'text/xml'], 'param' => '_ext', 'value' => 'xml'],
164: ];
165:
166: /**
167: * Instance cache for results of is(something) calls
168: *
169: * @var array
170: */
171: protected $_detectorCache = [];
172:
173: /**
174: * Request body stream. Contains php://input unless `input` constructor option is used.
175: *
176: * @var \Psr\Http\Message\StreamInterface
177: */
178: protected $stream;
179:
180: /**
181: * Uri instance
182: *
183: * @var \Psr\Http\Message\UriInterface
184: */
185: protected $uri;
186:
187: /**
188: * Instance of a Session object relative to this request
189: *
190: * @var \Cake\Http\Session
191: */
192: protected $session;
193:
194: /**
195: * Store the additional attributes attached to the request.
196: *
197: * @var array
198: */
199: protected $attributes = [];
200:
201: /**
202: * A list of propertes that emulated by the PSR7 attribute methods.
203: *
204: * @var array
205: */
206: protected $emulatedAttributes = ['session', 'webroot', 'base', 'params', 'here'];
207:
208: /**
209: * Array of Psr\Http\Message\UploadedFileInterface objects.
210: *
211: * @var array
212: */
213: protected $uploadedFiles = [];
214:
215: /**
216: * The HTTP protocol version used.
217: *
218: * @var string|null
219: */
220: protected $protocol;
221:
222: /**
223: * The request target if overridden
224: *
225: * @var string|null
226: */
227: protected $requestTarget;
228:
229: /**
230: * List of deprecated properties that have backwards
231: * compatibility offered through magic methods.
232: *
233: * @var array
234: */
235: private $deprecatedProperties = [
236: 'data' => ['get' => 'getData()', 'set' => 'withData()'],
237: 'query' => ['get' => 'getQuery()', 'set' => 'withQueryParams()'],
238: 'params' => ['get' => 'getParam()', 'set' => 'withParam()'],
239: 'cookies' => ['get' => 'getCookie()', 'set' => 'withCookieParams()'],
240: 'url' => ['get' => 'getPath()', 'set' => 'withRequestTarget()'],
241: 'base' => ['get' => 'getAttribute("base")', 'set' => 'withAttribute("base")'],
242: 'webroot' => ['get' => 'getAttribute("webroot")', 'set' => 'withAttribute("webroot")'],
243: 'here' => ['get' => 'getAttribute("here")', 'set' => 'withAttribute("here")'],
244: ];
245:
246: /**
247: * Wrapper method to create a new request from PHP superglobals.
248: *
249: * Uses the $_GET, $_POST, $_FILES, $_COOKIE, $_SERVER, $_ENV and php://input data to construct
250: * the request.
251: *
252: * @return self
253: * @deprecated 3.4.0 Use `Cake\Http\ServerRequestFactory` instead.
254: */
255: public static function createFromGlobals()
256: {
257: deprecationWarning(
258: 'ServerRequest::createFromGlobals() is deprecated. ' .
259: 'Use `Cake\Http\ServerRequestFactory` instead.'
260: );
261:
262: return ServerRequestFactory::fromGlobals();
263: }
264:
265: /**
266: * Create a new request object.
267: *
268: * You can supply the data as either an array or as a string. If you use
269: * a string you can only supply the URL for the request. Using an array will
270: * let you provide the following keys:
271: *
272: * - `post` POST data or non query string data
273: * - `query` Additional data from the query string.
274: * - `files` Uploaded file data formatted like $_FILES.
275: * - `cookies` Cookies for this request.
276: * - `environment` $_SERVER and $_ENV data.
277: * - `url` The URL without the base path for the request.
278: * - `uri` The PSR7 UriInterface object. If null, one will be created.
279: * - `base` The base URL for the request.
280: * - `webroot` The webroot directory for the request.
281: * - `input` The data that would come from php://input this is useful for simulating
282: * requests with put, patch or delete data.
283: * - `session` An instance of a Session object
284: *
285: * @param string|array $config An array of request data to create a request with.
286: * The string version of this argument is *deprecated* and will be removed in 4.0.0
287: */
288: public function __construct($config = [])
289: {
290: if (is_string($config)) {
291: $config = ['url' => $config];
292: }
293: $config += [
294: 'params' => $this->params,
295: 'query' => [],
296: 'post' => [],
297: 'files' => [],
298: 'cookies' => [],
299: 'environment' => [],
300: 'url' => '',
301: 'uri' => null,
302: 'base' => '',
303: 'webroot' => '',
304: 'input' => null,
305: ];
306:
307: $this->_setConfig($config);
308: }
309:
310: /**
311: * Process the config/settings data into properties.
312: *
313: * @param array $config The config data to use.
314: * @return void
315: */
316: protected function _setConfig($config)
317: {
318: if (strlen($config['url']) > 1 && $config['url'][0] === '/') {
319: $config['url'] = substr($config['url'], 1);
320: }
321:
322: if (empty($config['session'])) {
323: $config['session'] = new Session([
324: 'cookiePath' => $config['base']
325: ]);
326: }
327:
328: $this->_environment = $config['environment'];
329: $this->cookies = $config['cookies'];
330:
331: if (isset($config['uri']) && $config['uri'] instanceof UriInterface) {
332: $uri = $config['uri'];
333: } else {
334: $uri = ServerRequestFactory::createUri($config['environment']);
335: }
336:
337: // Extract a query string from config[url] if present.
338: // This is required for backwards compatibility and keeping
339: // UriInterface implementations happy.
340: $querystr = '';
341: if (strpos($config['url'], '?') !== false) {
342: list($config['url'], $querystr) = explode('?', $config['url']);
343: }
344: if (strlen($config['url'])) {
345: $uri = $uri->withPath('/' . $config['url']);
346: }
347: if (strlen($querystr)) {
348: $uri = $uri->withQuery($querystr);
349: }
350:
351: $this->uri = $uri;
352: $this->base = $config['base'];
353: $this->webroot = $config['webroot'];
354:
355: $this->url = substr($uri->getPath(), 1);
356: $this->here = $this->base . '/' . $this->url;
357:
358: if (isset($config['input'])) {
359: $stream = new Stream('php://memory', 'rw');
360: $stream->write($config['input']);
361: $stream->rewind();
362: } else {
363: $stream = new PhpInputStream();
364: }
365: $this->stream = $stream;
366:
367: $config['post'] = $this->_processPost($config['post']);
368: $this->data = $this->_processFiles($config['post'], $config['files']);
369: $this->query = $this->_processGet($config['query'], $querystr);
370: $this->params = $config['params'];
371: $this->session = $config['session'];
372: }
373:
374: /**
375: * Sets the REQUEST_METHOD environment variable based on the simulated _method
376: * HTTP override value. The 'ORIGINAL_REQUEST_METHOD' is also preserved, if you
377: * want the read the non-simulated HTTP method the client used.
378: *
379: * @param array $data Array of post data.
380: * @return array
381: */
382: protected function _processPost($data)
383: {
384: $method = $this->getEnv('REQUEST_METHOD');
385: $override = false;
386:
387: if (in_array($method, ['PUT', 'DELETE', 'PATCH']) &&
388: strpos($this->contentType(), 'application/x-www-form-urlencoded') === 0
389: ) {
390: $data = $this->input();
391: parse_str($data, $data);
392: }
393: if ($this->hasHeader('X-Http-Method-Override')) {
394: $data['_method'] = $this->getHeaderLine('X-Http-Method-Override');
395: $override = true;
396: }
397: $this->_environment['ORIGINAL_REQUEST_METHOD'] = $method;
398: if (isset($data['_method'])) {
399: $this->_environment['REQUEST_METHOD'] = $data['_method'];
400: unset($data['_method']);
401: $override = true;
402: }
403:
404: if ($override && !in_array($this->_environment['REQUEST_METHOD'], ['PUT', 'POST', 'DELETE', 'PATCH'])) {
405: $data = [];
406: }
407:
408: return $data;
409: }
410:
411: /**
412: * Process the GET parameters and move things into the object.
413: *
414: * @param array $query The array to which the parsed keys/values are being added.
415: * @param string $queryString A query string from the URL if provided
416: * @return array An array containing the parsed query string as keys/values.
417: */
418: protected function _processGet($query, $queryString = '')
419: {
420: $unsetUrl = '/' . str_replace(['.', ' '], '_', urldecode($this->url));
421: unset($query[$unsetUrl], $query[$this->base . $unsetUrl]);
422: if (strlen($queryString)) {
423: parse_str($queryString, $queryArgs);
424: $query += $queryArgs;
425: }
426:
427: return $query;
428: }
429:
430: /**
431: * Process uploaded files and move things onto the post data.
432: *
433: * @param array $post Post data to merge files onto.
434: * @param array $files Uploaded files to merge in.
435: * @return array merged post + file data.
436: */
437: protected function _processFiles($post, $files)
438: {
439: if (!is_array($files)) {
440: return $post;
441: }
442: $fileData = [];
443: foreach ($files as $key => $value) {
444: if ($value instanceof UploadedFileInterface) {
445: $fileData[$key] = $value;
446: continue;
447: }
448:
449: if (is_array($value) && isset($value['tmp_name'])) {
450: $fileData[$key] = $this->_createUploadedFile($value);
451: continue;
452: }
453:
454: throw new InvalidArgumentException(sprintf(
455: 'Invalid value in FILES "%s"',
456: json_encode($value)
457: ));
458: }
459: $this->uploadedFiles = $fileData;
460:
461: // Make a flat map that can be inserted into $post for BC.
462: $fileMap = Hash::flatten($fileData);
463: foreach ($fileMap as $key => $file) {
464: $error = $file->getError();
465: $tmpName = '';
466: if ($error === UPLOAD_ERR_OK) {
467: $tmpName = $file->getStream()->getMetadata('uri');
468: }
469: $post = Hash::insert($post, $key, [
470: 'tmp_name' => $tmpName,
471: 'error' => $error,
472: 'name' => $file->getClientFilename(),
473: 'type' => $file->getClientMediaType(),
474: 'size' => $file->getSize(),
475: ]);
476: }
477:
478: return $post;
479: }
480:
481: /**
482: * Create an UploadedFile instance from a $_FILES array.
483: *
484: * If the value represents an array of values, this method will
485: * recursively process the data.
486: *
487: * @param array $value $_FILES struct
488: * @return array|UploadedFileInterface
489: */
490: protected function _createUploadedFile(array $value)
491: {
492: if (is_array($value['tmp_name'])) {
493: return $this->_normalizeNestedFiles($value);
494: }
495:
496: return new UploadedFile(
497: $value['tmp_name'],
498: $value['size'],
499: $value['error'],
500: $value['name'],
501: $value['type']
502: );
503: }
504:
505: /**
506: * Normalize an array of file specifications.
507: *
508: * Loops through all nested files and returns a normalized array of
509: * UploadedFileInterface instances.
510: *
511: * @param array $files The file data to normalize & convert.
512: * @return array An array of UploadedFileInterface objects.
513: */
514: protected function _normalizeNestedFiles(array $files = [])
515: {
516: $normalizedFiles = [];
517: foreach (array_keys($files['tmp_name']) as $key) {
518: $spec = [
519: 'tmp_name' => $files['tmp_name'][$key],
520: 'size' => $files['size'][$key],
521: 'error' => $files['error'][$key],
522: 'name' => $files['name'][$key],
523: 'type' => $files['type'][$key],
524: ];
525: $normalizedFiles[$key] = $this->_createUploadedFile($spec);
526: }
527:
528: return $normalizedFiles;
529: }
530:
531: /**
532: * Get the content type used in this request.
533: *
534: * @return string
535: */
536: public function contentType()
537: {
538: $type = $this->getEnv('CONTENT_TYPE');
539: if ($type) {
540: return $type;
541: }
542:
543: return $this->getEnv('HTTP_CONTENT_TYPE');
544: }
545:
546: /**
547: * Returns the instance of the Session object for this request
548: *
549: * @return \Cake\Http\Session
550: */
551: public function getSession()
552: {
553: return $this->session;
554: }
555:
556: /**
557: * Returns the instance of the Session object for this request
558: *
559: * If a session object is passed as first argument it will be set as
560: * the session to use for this request
561: *
562: * @deprecated 3.5.0 Use getSession() instead. The setter part will be removed.
563: * @param \Cake\Http\Session|null $session the session object to use
564: * @return \Cake\Http\Session
565: */
566: public function session(Session $session = null)
567: {
568: deprecationWarning(
569: 'ServerRequest::session() is deprecated. ' .
570: 'Use getSession() instead. The setter part will be removed.'
571: );
572:
573: if ($session === null) {
574: return $this->session;
575: }
576:
577: return $this->session = $session;
578: }
579:
580: /**
581: * Get the IP the client is using, or says they are using.
582: *
583: * @return string The client IP.
584: */
585: public function clientIp()
586: {
587: if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_FOR')) {
588: $addresses = array_map('trim', explode(',', $this->getEnv('HTTP_X_FORWARDED_FOR')));
589: $trusted = (count($this->trustedProxies) > 0);
590: $n = count($addresses);
591:
592: if ($trusted) {
593: $trusted = array_diff($addresses, $this->trustedProxies);
594: $trusted = (count($trusted) === 1);
595: }
596:
597: if ($trusted) {
598: return $addresses[0];
599: }
600:
601: return $addresses[$n - 1];
602: }
603:
604: if ($this->trustProxy && $this->getEnv('HTTP_X_REAL_IP')) {
605: $ipaddr = $this->getEnv('HTTP_X_REAL_IP');
606: } elseif ($this->trustProxy && $this->getEnv('HTTP_CLIENT_IP')) {
607: $ipaddr = $this->getEnv('HTTP_CLIENT_IP');
608: } else {
609: $ipaddr = $this->getEnv('REMOTE_ADDR');
610: }
611:
612: return trim($ipaddr);
613: }
614:
615: /**
616: * register trusted proxies
617: *
618: * @param array $proxies ips list of trusted proxies
619: * @return void
620: */
621: public function setTrustedProxies(array $proxies)
622: {
623: $this->trustedProxies = $proxies;
624: $this->trustProxy = true;
625: }
626:
627: /**
628: * Get trusted proxies
629: *
630: * @return array
631: */
632: public function getTrustedProxies()
633: {
634: return $this->trustedProxies;
635: }
636:
637: /**
638: * Returns the referer that referred this request.
639: *
640: * @param bool $local Attempt to return a local address.
641: * Local addresses do not contain hostnames.
642: * @return string The referring address for this request.
643: */
644: public function referer($local = false)
645: {
646: $ref = $this->getEnv('HTTP_REFERER');
647:
648: $base = Configure::read('App.fullBaseUrl') . $this->webroot;
649: if (!empty($ref) && !empty($base)) {
650: if ($local && strpos($ref, $base) === 0) {
651: $ref = substr($ref, strlen($base));
652: if (!strlen($ref) || strpos($ref, '//') === 0) {
653: $ref = '/';
654: }
655: if ($ref[0] !== '/') {
656: $ref = '/' . $ref;
657: }
658:
659: return $ref;
660: }
661: if (!$local) {
662: return $ref;
663: }
664: }
665:
666: return '/';
667: }
668:
669: /**
670: * Missing method handler, handles wrapping older style isAjax() type methods
671: *
672: * @param string $name The method called
673: * @param array $params Array of parameters for the method call
674: * @return mixed
675: * @throws \BadMethodCallException when an invalid method is called.
676: */
677: public function __call($name, $params)
678: {
679: if (strpos($name, 'is') === 0) {
680: $type = strtolower(substr($name, 2));
681:
682: array_unshift($params, $type);
683:
684: return $this->is(...$params);
685: }
686: throw new BadMethodCallException(sprintf('Method %s does not exist', $name));
687: }
688:
689: /**
690: * Magic set method allows backward compatibility for former public properties
691: *
692: *
693: * @param string $name The property being accessed.
694: * @param mixed $value The property value.
695: * @return mixed Either the value of the parameter or null.
696: * @deprecated 3.6.0 Public properties will be removed in 4.0.0.
697: * Use appropriate setters instead.
698: */
699: public function __set($name, $value)
700: {
701: if (isset($this->deprecatedProperties[$name])) {
702: $method = $this->deprecatedProperties[$name]['set'];
703: deprecationWarning(
704: "Setting {$name} as a property will be removed in 4.0.0. " .
705: "Use {$method} instead."
706: );
707:
708: return $this->{$name} = $value;
709: }
710: throw new BadMethodCallException("Cannot set {$name} it is not a known property.");
711: }
712:
713: /**
714: * Magic get method allows access to parsed routing parameters directly on the object.
715: *
716: * Allows access to `$this->params['controller']` via `$this->controller`
717: *
718: * @param string $name The property being accessed.
719: * @return mixed Either the value of the parameter or null.
720: * @deprecated 3.4.0 Accessing routing parameters through __get will removed in 4.0.0.
721: * Use getParam() instead.
722: */
723: public function &__get($name)
724: {
725: if (isset($this->deprecatedProperties[$name])) {
726: $method = $this->deprecatedProperties[$name]['get'];
727: deprecationWarning(
728: "Accessing `{$name}` as a property will be removed in 4.0.0. " .
729: "Use request->{$method} instead."
730: );
731:
732: return $this->{$name};
733: }
734:
735: deprecationWarning(sprintf(
736: 'Accessing routing parameters through `%s` will removed in 4.0.0. ' .
737: 'Use `getParam()` instead.',
738: $name
739: ));
740:
741: if (isset($this->params[$name])) {
742: return $this->params[$name];
743: }
744: $value = null;
745:
746: return $value;
747: }
748:
749: /**
750: * Magic isset method allows isset/empty checks
751: * on routing parameters.
752: *
753: * @param string $name The property being accessed.
754: * @return bool Existence
755: * @deprecated 3.4.0 Accessing routing parameters through __isset will removed in 4.0.0.
756: * Use getParam() instead.
757: */
758: public function __isset($name)
759: {
760: if (isset($this->deprecatedProperties[$name])) {
761: $method = $this->deprecatedProperties[$name]['get'];
762: deprecationWarning(
763: "Accessing {$name} as a property will be removed in 4.0.0. " .
764: "Use {$method} instead."
765: );
766:
767: return isset($this->{$name});
768: }
769:
770: deprecationWarning(
771: 'Accessing routing parameters through __isset will removed in 4.0.0. ' .
772: 'Use getParam() instead.'
773: );
774:
775: return isset($this->params[$name]);
776: }
777:
778: /**
779: * Check whether or not a Request is a certain type.
780: *
781: * Uses the built in detection rules as well as additional rules
782: * defined with Cake\Http\ServerRequest::addDetector(). Any detector can be called
783: * as `is($type)` or `is$Type()`.
784: *
785: * @param string|array $type The type of request you want to check. If an array
786: * this method will return true if the request matches any type.
787: * @param array ...$args List of arguments
788: * @return bool Whether or not the request is the type you are checking.
789: */
790: public function is($type, ...$args)
791: {
792: if (is_array($type)) {
793: $result = array_map([$this, 'is'], $type);
794:
795: return count(array_filter($result)) > 0;
796: }
797:
798: $type = strtolower($type);
799: if (!isset(static::$_detectors[$type])) {
800: return false;
801: }
802: if ($args) {
803: return $this->_is($type, $args);
804: }
805: if (!isset($this->_detectorCache[$type])) {
806: $this->_detectorCache[$type] = $this->_is($type, $args);
807: }
808:
809: return $this->_detectorCache[$type];
810: }
811:
812: /**
813: * Clears the instance detector cache, used by the is() function
814: *
815: * @return void
816: */
817: public function clearDetectorCache()
818: {
819: $this->_detectorCache = [];
820: }
821:
822: /**
823: * Worker for the public is() function
824: *
825: * @param string $type The type of request you want to check.
826: * @param array $args Array of custom detector arguments.
827: * @return bool Whether or not the request is the type you are checking.
828: */
829: protected function _is($type, $args)
830: {
831: $detect = static::$_detectors[$type];
832: if (is_callable($detect)) {
833: array_unshift($args, $this);
834:
835: return $detect(...$args);
836: }
837: if (isset($detect['env']) && $this->_environmentDetector($detect)) {
838: return true;
839: }
840: if (isset($detect['header']) && $this->_headerDetector($detect)) {
841: return true;
842: }
843: if (isset($detect['accept']) && $this->_acceptHeaderDetector($detect)) {
844: return true;
845: }
846: if (isset($detect['param']) && $this->_paramDetector($detect)) {
847: return true;
848: }
849:
850: return false;
851: }
852:
853: /**
854: * Detects if a specific accept header is present.
855: *
856: * @param array $detect Detector options array.
857: * @return bool Whether or not the request is the type you are checking.
858: */
859: protected function _acceptHeaderDetector($detect)
860: {
861: $acceptHeaders = explode(',', $this->getEnv('HTTP_ACCEPT'));
862: foreach ($detect['accept'] as $header) {
863: if (in_array($header, $acceptHeaders)) {
864: return true;
865: }
866: }
867:
868: return false;
869: }
870:
871: /**
872: * Detects if a specific header is present.
873: *
874: * @param array $detect Detector options array.
875: * @return bool Whether or not the request is the type you are checking.
876: */
877: protected function _headerDetector($detect)
878: {
879: foreach ($detect['header'] as $header => $value) {
880: $header = $this->getEnv('http_' . $header);
881: if ($header !== null) {
882: if (!is_string($value) && !is_bool($value) && is_callable($value)) {
883: return call_user_func($value, $header);
884: }
885:
886: return ($header === $value);
887: }
888: }
889:
890: return false;
891: }
892:
893: /**
894: * Detects if a specific request parameter is present.
895: *
896: * @param array $detect Detector options array.
897: * @return bool Whether or not the request is the type you are checking.
898: */
899: protected function _paramDetector($detect)
900: {
901: $key = $detect['param'];
902: if (isset($detect['value'])) {
903: $value = $detect['value'];
904:
905: return isset($this->params[$key]) ? $this->params[$key] == $value : false;
906: }
907: if (isset($detect['options'])) {
908: return isset($this->params[$key]) ? in_array($this->params[$key], $detect['options']) : false;
909: }
910:
911: return false;
912: }
913:
914: /**
915: * Detects if a specific environment variable is present.
916: *
917: * @param array $detect Detector options array.
918: * @return bool Whether or not the request is the type you are checking.
919: */
920: protected function _environmentDetector($detect)
921: {
922: if (isset($detect['env'])) {
923: if (isset($detect['value'])) {
924: return $this->getEnv($detect['env']) == $detect['value'];
925: }
926: if (isset($detect['pattern'])) {
927: return (bool)preg_match($detect['pattern'], $this->getEnv($detect['env']));
928: }
929: if (isset($detect['options'])) {
930: $pattern = '/' . implode('|', $detect['options']) . '/i';
931:
932: return (bool)preg_match($pattern, $this->getEnv($detect['env']));
933: }
934: }
935:
936: return false;
937: }
938:
939: /**
940: * Check that a request matches all the given types.
941: *
942: * Allows you to test multiple types and union the results.
943: * See Request::is() for how to add additional types and the
944: * built-in types.
945: *
946: * @param array $types The types to check.
947: * @return bool Success.
948: * @see \Cake\Http\ServerRequest::is()
949: */
950: public function isAll(array $types)
951: {
952: $result = array_filter(array_map([$this, 'is'], $types));
953:
954: return count($result) === count($types);
955: }
956:
957: /**
958: * Add a new detector to the list of detectors that a request can use.
959: * There are several different formats and types of detectors that can be set.
960: *
961: * ### Callback detectors
962: *
963: * Callback detectors allow you to provide a callable to handle the check.
964: * The callback will receive the request object as its only parameter.
965: *
966: * ```
967: * addDetector('custom', function ($request) { //Return a boolean });
968: * addDetector('custom', ['SomeClass', 'somemethod']);
969: * ```
970: *
971: * ### Environment value comparison
972: *
973: * An environment value comparison, compares a value fetched from `env()` to a known value
974: * the environment value is equality checked against the provided value.
975: *
976: * e.g `addDetector('post', ['env' => 'REQUEST_METHOD', 'value' => 'POST'])`
977: *
978: * ### Pattern value comparison
979: *
980: * Pattern value comparison allows you to compare a value fetched from `env()` to a regular expression.
981: *
982: * ```
983: * addDetector('iphone', ['env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i']);
984: * ```
985: *
986: * ### Option based comparison
987: *
988: * Option based comparisons use a list of options to create a regular expression. Subsequent calls
989: * to add an already defined options detector will merge the options.
990: *
991: * ```
992: * addDetector('mobile', ['env' => 'HTTP_USER_AGENT', 'options' => ['Fennec']]);
993: * ```
994: *
995: * ### Request parameter detectors
996: *
997: * Allows for custom detectors on the request parameters.
998: *
999: * e.g `addDetector('requested', ['param' => 'requested', 'value' => 1]`
1000: *
1001: * You can also make parameter detectors that accept multiple values
1002: * using the `options` key. This is useful when you want to check
1003: * if a request parameter is in a list of options.
1004: *
1005: * `addDetector('extension', ['param' => 'ext', 'options' => ['pdf', 'csv']]`
1006: *
1007: * @param string $name The name of the detector.
1008: * @param callable|array $callable A callable or options array for the detector definition.
1009: * @return void
1010: */
1011: public static function addDetector($name, $callable)
1012: {
1013: $name = strtolower($name);
1014: if (is_callable($callable)) {
1015: static::$_detectors[$name] = $callable;
1016:
1017: return;
1018: }
1019: if (isset(static::$_detectors[$name], $callable['options'])) {
1020: $callable = Hash::merge(static::$_detectors[$name], $callable);
1021: }
1022: static::$_detectors[$name] = $callable;
1023: }
1024:
1025: /**
1026: * Add parameters to the request's parsed parameter set. This will overwrite any existing parameters.
1027: * This modifies the parameters available through `$request->getParam()`.
1028: *
1029: * @param array $params Array of parameters to merge in
1030: * @return $this The current object, you can chain this method.
1031: * @deprecated 3.6.0 ServerRequest::addParams() is deprecated. Use `withParam()` or
1032: * `withAttribute('params')` instead.
1033: */
1034: public function addParams(array $params)
1035: {
1036: deprecationWarning(
1037: 'ServerRequest::addParams() is deprecated. ' .
1038: 'Use `withParam()` or `withAttribute("params", $params)` instead.'
1039: );
1040: $this->params = array_merge($this->params, $params);
1041:
1042: return $this;
1043: }
1044:
1045: /**
1046: * Add paths to the requests' paths vars. This will overwrite any existing paths.
1047: * Provides an easy way to modify, here, webroot and base.
1048: *
1049: * @param array $paths Array of paths to merge in
1050: * @return $this The current object, you can chain this method.
1051: * @deprecated 3.6.0 Mutating a request in place is deprecated. Use `withAttribute()` to modify paths instead.
1052: */
1053: public function addPaths(array $paths)
1054: {
1055: deprecationWarning(
1056: 'ServerRequest::addPaths() is deprecated. ' .
1057: 'Use `withAttribute($key, $value)` instead.'
1058: );
1059: foreach (['webroot', 'here', 'base'] as $element) {
1060: if (isset($paths[$element])) {
1061: $this->{$element} = $paths[$element];
1062: }
1063: }
1064:
1065: return $this;
1066: }
1067:
1068: /**
1069: * Get the value of the current requests URL. Will include the query string arguments.
1070: *
1071: * @param bool $base Include the base path, set to false to trim the base path off.
1072: * @return string The current request URL including query string args.
1073: * @deprecated 3.4.0 This method will be removed in 4.0.0. You should use getRequestTarget() instead.
1074: */
1075: public function here($base = true)
1076: {
1077: deprecationWarning(
1078: 'ServerRequest::here() will be removed in 4.0.0. You should use getRequestTarget() instead.'
1079: );
1080:
1081: $url = $this->here;
1082: if (!empty($this->query)) {
1083: $url .= '?' . http_build_query($this->query, null, '&');
1084: }
1085: if (!$base) {
1086: $url = preg_replace('/^' . preg_quote($this->base, '/') . '/', '', $url, 1);
1087: }
1088:
1089: return $url;
1090: }
1091:
1092: /**
1093: * Normalize a header name into the SERVER version.
1094: *
1095: * @param string $name The header name.
1096: * @return string The normalized header name.
1097: */
1098: protected function normalizeHeaderName($name)
1099: {
1100: $name = str_replace('-', '_', strtoupper($name));
1101: if (!in_array($name, ['CONTENT_LENGTH', 'CONTENT_TYPE'])) {
1102: $name = 'HTTP_' . $name;
1103: }
1104:
1105: return $name;
1106: }
1107:
1108: /**
1109: * Read an HTTP header from the Request information.
1110: *
1111: * If the header is not defined in the request, this method
1112: * will fallback to reading data from $_SERVER and $_ENV.
1113: * This fallback behavior is deprecated, and will be removed in 4.0.0
1114: *
1115: * @param string $name Name of the header you want.
1116: * @return string|null Either null on no header being set or the value of the header.
1117: * @deprecated 4.0.0 The automatic fallback to env() will be removed in 4.0.0, see getHeader()
1118: */
1119: public function header($name)
1120: {
1121: deprecationWarning(
1122: 'ServerRequest::header() is deprecated. ' .
1123: 'The automatic fallback to env() will be removed in 4.0.0, see getHeader()'
1124: );
1125:
1126: $name = $this->normalizeHeaderName($name);
1127:
1128: return $this->getEnv($name);
1129: }
1130:
1131: /**
1132: * Get all headers in the request.
1133: *
1134: * Returns an associative array where the header names are
1135: * the keys and the values are a list of header values.
1136: *
1137: * While header names are not case-sensitive, getHeaders() will normalize
1138: * the headers.
1139: *
1140: * @return array An associative array of headers and their values.
1141: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1142: */
1143: public function getHeaders()
1144: {
1145: $headers = [];
1146: foreach ($this->_environment as $key => $value) {
1147: $name = null;
1148: if (strpos($key, 'HTTP_') === 0) {
1149: $name = substr($key, 5);
1150: }
1151: if (strpos($key, 'CONTENT_') === 0) {
1152: $name = $key;
1153: }
1154: if ($name !== null) {
1155: $name = str_replace('_', ' ', strtolower($name));
1156: $name = str_replace(' ', '-', ucwords($name));
1157: $headers[$name] = (array)$value;
1158: }
1159: }
1160:
1161: return $headers;
1162: }
1163:
1164: /**
1165: * Check if a header is set in the request.
1166: *
1167: * @param string $name The header you want to get (case-insensitive)
1168: * @return bool Whether or not the header is defined.
1169: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1170: */
1171: public function hasHeader($name)
1172: {
1173: $name = $this->normalizeHeaderName($name);
1174:
1175: return isset($this->_environment[$name]);
1176: }
1177:
1178: /**
1179: * Get a single header from the request.
1180: *
1181: * Return the header value as an array. If the header
1182: * is not present an empty array will be returned.
1183: *
1184: * @param string $name The header you want to get (case-insensitive)
1185: * @return array An associative array of headers and their values.
1186: * If the header doesn't exist, an empty array will be returned.
1187: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1188: */
1189: public function getHeader($name)
1190: {
1191: $name = $this->normalizeHeaderName($name);
1192: if (isset($this->_environment[$name])) {
1193: return (array)$this->_environment[$name];
1194: }
1195:
1196: return [];
1197: }
1198:
1199: /**
1200: * Get a single header as a string from the request.
1201: *
1202: * @param string $name The header you want to get (case-insensitive)
1203: * @return string Header values collapsed into a comma separated string.
1204: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1205: */
1206: public function getHeaderLine($name)
1207: {
1208: $value = $this->getHeader($name);
1209:
1210: return implode(', ', $value);
1211: }
1212:
1213: /**
1214: * Get a modified request with the provided header.
1215: *
1216: * @param string $name The header name.
1217: * @param string|array $value The header value
1218: * @return static
1219: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1220: */
1221: public function withHeader($name, $value)
1222: {
1223: $new = clone $this;
1224: $name = $this->normalizeHeaderName($name);
1225: $new->_environment[$name] = $value;
1226:
1227: return $new;
1228: }
1229:
1230: /**
1231: * Get a modified request with the provided header.
1232: *
1233: * Existing header values will be retained. The provided value
1234: * will be appended into the existing values.
1235: *
1236: * @param string $name The header name.
1237: * @param string|array $value The header value
1238: * @return static
1239: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1240: */
1241: public function withAddedHeader($name, $value)
1242: {
1243: $new = clone $this;
1244: $name = $this->normalizeHeaderName($name);
1245: $existing = [];
1246: if (isset($new->_environment[$name])) {
1247: $existing = (array)$new->_environment[$name];
1248: }
1249: $existing = array_merge($existing, (array)$value);
1250: $new->_environment[$name] = $existing;
1251:
1252: return $new;
1253: }
1254:
1255: /**
1256: * Get a modified request without a provided header.
1257: *
1258: * @param string $name The header name to remove.
1259: * @return static
1260: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1261: */
1262: public function withoutHeader($name)
1263: {
1264: $new = clone $this;
1265: $name = $this->normalizeHeaderName($name);
1266: unset($new->_environment[$name]);
1267:
1268: return $new;
1269: }
1270:
1271: /**
1272: * Get the HTTP method used for this request.
1273: *
1274: * @return string The name of the HTTP method used.
1275: * @deprecated 3.4.0 This method will be removed in 4.0.0. Use getMethod() instead.
1276: */
1277: public function method()
1278: {
1279: deprecationWarning(
1280: 'ServerRequest::method() is deprecated. ' .
1281: 'This method will be removed in 4.0.0. Use getMethod() instead.'
1282: );
1283:
1284: return $this->getEnv('REQUEST_METHOD');
1285: }
1286:
1287: /**
1288: * Get the HTTP method used for this request.
1289: * There are a few ways to specify a method.
1290: *
1291: * - If your client supports it you can use native HTTP methods.
1292: * - You can set the HTTP-X-Method-Override header.
1293: * - You can submit an input with the name `_method`
1294: *
1295: * Any of these 3 approaches can be used to set the HTTP method used
1296: * by CakePHP internally, and will effect the result of this method.
1297: *
1298: * @return string The name of the HTTP method used.
1299: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1300: */
1301: public function getMethod()
1302: {
1303: return $this->getEnv('REQUEST_METHOD');
1304: }
1305:
1306: /**
1307: * Update the request method and get a new instance.
1308: *
1309: * @param string $method The HTTP method to use.
1310: * @return static A new instance with the updated method.
1311: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1312: */
1313: public function withMethod($method)
1314: {
1315: $new = clone $this;
1316:
1317: if (!is_string($method) ||
1318: !preg_match('/^[!#$%&\'*+.^_`\|~0-9a-z-]+$/i', $method)
1319: ) {
1320: throw new InvalidArgumentException(sprintf(
1321: 'Unsupported HTTP method "%s" provided',
1322: $method
1323: ));
1324: }
1325: $new->_environment['REQUEST_METHOD'] = $method;
1326:
1327: return $new;
1328: }
1329:
1330: /**
1331: * Get all the server environment parameters.
1332: *
1333: * Read all of the 'environment' or 'server' data that was
1334: * used to create this request.
1335: *
1336: * @return array
1337: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1338: */
1339: public function getServerParams()
1340: {
1341: return $this->_environment;
1342: }
1343:
1344: /**
1345: * Get all the query parameters in accordance to the PSR-7 specifications. To read specific query values
1346: * use the alternative getQuery() method.
1347: *
1348: * @return array
1349: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1350: */
1351: public function getQueryParams()
1352: {
1353: return $this->query;
1354: }
1355:
1356: /**
1357: * Update the query string data and get a new instance.
1358: *
1359: * @param array $query The query string data to use
1360: * @return static A new instance with the updated query string data.
1361: * @link http://www.php-fig.org/psr/psr-7/ This method is part of the PSR-7 server request interface.
1362: */
1363: public function withQueryParams(array $query)
1364: {
1365: $new = clone $this;
1366: $new->query = $query;
1367:
1368: return $new;
1369: }
1370:
1371: /**
1372: * Get the host that the request was handled on.
1373: *
1374: * @return string
1375: */
1376: public function host()
1377: {
1378: if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_HOST')) {
1379: return $this->getEnv('HTTP_X_FORWARDED_HOST');
1380: }
1381:
1382: return $this->getEnv('HTTP_HOST');
1383: }
1384:
1385: /**
1386: * Get the port the request was handled on.
1387: *
1388: * @return string
1389: */
1390: public function port()
1391: {
1392: if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_PORT')) {
1393: return $this->getEnv('HTTP_X_FORWARDED_PORT');
1394: }
1395:
1396: return $this->getEnv('SERVER_PORT');
1397: }
1398:
1399: /**
1400: * Get the current url scheme used for the request.
1401: *
1402: * e.g. 'http', or 'https'
1403: *
1404: * @return string The scheme used for the request.
1405: */
1406: public function scheme()
1407: {
1408: if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_PROTO')) {
1409: return $this->getEnv('HTTP_X_FORWARDED_PROTO');
1410: }
1411:
1412: return $this->getEnv('HTTPS') ? 'https' : 'http';
1413: }
1414:
1415: /**
1416: * Get the domain name and include $tldLength segments of the tld.
1417: *
1418: * @param int $tldLength Number of segments your tld contains. For example: `example.com` contains 1 tld.
1419: * While `example.co.uk` contains 2.
1420: * @return string Domain name without subdomains.
1421: */
1422: public function domain($tldLength = 1)
1423: {
1424: $segments = explode('.', $this->host());
1425: $domain = array_slice($segments, -1 * ($tldLength + 1));
1426:
1427: return implode('.', $domain);
1428: }
1429:
1430: /**
1431: * Get the subdomains for a host.
1432: *
1433: * @param int $tldLength Number of segments your tld contains. For example: `example.com` contains 1 tld.
1434: * While `example.co.uk` contains 2.
1435: * @return array An array of subdomains.
1436: */
1437: public function subdomains($tldLength = 1)
1438: {
1439: $segments = explode('.', $this->host());
1440:
1441: return array_slice($segments, 0, -1 * ($tldLength + 1));
1442: }
1443:
1444: /**
1445: * Find out which content types the client accepts or check if they accept a
1446: * particular type of content.
1447: *
1448: * #### Get all types:
1449: *
1450: * ```
1451: * $this->request->accepts();
1452: * ```
1453: *
1454: * #### Check for a single type:
1455: *
1456: * ```
1457: * $this->request->accepts('application/json');
1458: * ```
1459: *
1460: * This method will order the returned content types by the preference values indicated
1461: * by the client.
1462: *
1463: * @param string|null $type The content type to check for. Leave null to get all types a client accepts.
1464: * @return array|bool Either an array of all the types the client accepts or a boolean if they accept the
1465: * provided type.
1466: */
1467: public function accepts($type = null)
1468: {
1469: $raw = $this->parseAccept();
1470: $accept = [];
1471: foreach ($raw as $types) {
1472: $accept = array_merge($accept, $types);
1473: }
1474: if ($type === null) {
1475: return $accept;
1476: }
1477:
1478: return in_array($type, $accept);
1479: }
1480:
1481: /**
1482: * Parse the HTTP_ACCEPT header and return a sorted array with content types
1483: * as the keys, and pref values as the values.
1484: *
1485: * Generally you want to use Cake\Http\ServerRequest::accept() to get a simple list
1486: * of the accepted content types.
1487: *
1488: * @return array An array of prefValue => [content/types]
1489: */
1490: public function parseAccept()
1491: {
1492: return $this->_parseAcceptWithQualifier($this->getHeaderLine('Accept'));
1493: }
1494:
1495: /**
1496: * Get the languages accepted by the client, or check if a specific language is accepted.
1497: *
1498: * Get the list of accepted languages:
1499: *
1500: * ``` \Cake\Http\ServerRequest::acceptLanguage(); ```
1501: *
1502: * Check if a specific language is accepted:
1503: *
1504: * ``` \Cake\Http\ServerRequest::acceptLanguage('es-es'); ```
1505: *
1506: * @param string|null $language The language to test.
1507: * @return array|bool If a $language is provided, a boolean. Otherwise the array of accepted languages.
1508: */
1509: public function acceptLanguage($language = null)
1510: {
1511: $raw = $this->_parseAcceptWithQualifier($this->getHeaderLine('Accept-Language'));
1512: $accept = [];
1513: foreach ($raw as $languages) {
1514: foreach ($languages as &$lang) {
1515: if (strpos($lang, '_')) {
1516: $lang = str_replace('_', '-', $lang);
1517: }
1518: $lang = strtolower($lang);
1519: }
1520: $accept = array_merge($accept, $languages);
1521: }
1522: if ($language === null) {
1523: return $accept;
1524: }
1525:
1526: return in_array(strtolower($language), $accept);
1527: }
1528:
1529: /**
1530: * Parse Accept* headers with qualifier options.
1531: *
1532: * Only qualifiers will be extracted, any other accept extensions will be
1533: * discarded as they are not frequently used.
1534: *
1535: * @param string $header Header to parse.
1536: * @return array
1537: */
1538: protected function _parseAcceptWithQualifier($header)
1539: {
1540: $accept = [];
1541: $headers = explode(',', $header);
1542: foreach (array_filter($headers) as $value) {
1543: $prefValue = '1.0';
1544: $value = trim($value);
1545:
1546: $semiPos = strpos($value, ';');
1547: if ($semiPos !== false) {
1548: $params = explode(';', $value);
1549: $value = trim($params[0]);
1550: foreach ($params as $param) {
1551: $qPos = strpos($param, 'q=');
1552: if ($qPos !== false) {
1553: $prefValue = substr($param, $qPos + 2);
1554: }
1555: }
1556: }
1557:
1558: if (!isset($accept[$prefValue])) {
1559: $accept[$prefValue] = [];
1560: }
1561: if ($prefValue) {
1562: $accept[$prefValue][] = $value;
1563: }
1564: }
1565: krsort($accept);
1566:
1567: return $accept;
1568: }
1569:
1570: /**
1571: * Provides a read accessor for `$this->query`.
1572: * Allows you to use a `Hash::get()` compatible syntax for reading post data.
1573: *
1574: * @param string|null $name Query string variable name or null to read all.
1575: * @return string|array|null The value being read
1576: * @deprecated 3.4.0 Use getQuery() or the PSR-7 getQueryParams() and withQueryParams() methods instead.
1577: */
1578: public function query($name = null)
1579: {
1580: deprecationWarning(
1581: 'ServerRequest::query() is deprecated. ' .
1582: 'Use getQuery() or the PSR-7 getQueryParams() and withQueryParams() methods instead.'
1583: );
1584:
1585: if ($name === null) {
1586: return $this->query;
1587: }
1588:
1589: return $this->getQuery($name);
1590: }
1591:
1592: /**
1593: * Read a specific query value or dotted path.
1594: *
1595: * Developers are encouraged to use getQueryParams() when possible as it is PSR-7 compliant, and this method
1596: * is not.
1597: *
1598: * ### PSR-7 Alternative
1599: *
1600: * ```
1601: * $value = Hash::get($request->getQueryParams(), 'Post.id', null);
1602: * ```
1603: *
1604: * @param string|null $name The name or dotted path to the query param or null to read all.
1605: * @param mixed $default The default value if the named parameter is not set, and $name is not null.
1606: * @return null|string|array Query data.
1607: * @see ServerRequest::getQueryParams()
1608: */
1609: public function getQuery($name = null, $default = null)
1610: {
1611: if ($name === null) {
1612: return $this->query;
1613: }
1614:
1615: return Hash::get($this->query, $name, $default);
1616: }
1617:
1618: /**
1619: * Provides a read/write accessor for `$this->data`.
1620: * Allows you to use a `Hash::get()` compatible syntax for reading post data.
1621: *
1622: * ### Reading values.
1623: *
1624: * ```
1625: * $request->data('Post.title');
1626: * ```
1627: *
1628: * When reading values you will get `null` for keys/values that do not exist.
1629: *
1630: * ### Writing values
1631: *
1632: * ```
1633: * $request->data('Post.title', 'New post!');
1634: * ```
1635: *
1636: * You can write to any value, even paths/keys that do not exist, and the arrays
1637: * will be created for you.
1638: *
1639: * @param string|null $name Dot separated name of the value to read/write
1640: * @param mixed ...$args The data to set (deprecated)
1641: * @return mixed|$this Either the value being read, or this so you can chain consecutive writes.
1642: * @deprecated 3.4.0 Use withData() and getData() or getParsedBody() instead.
1643: */
1644: public function data($name = null, ...$args)
1645: {
1646: deprecationWarning(
1647: 'ServerRequest::data() is deprecated. ' .
1648: 'Use withData() and getData() or getParsedBody() instead.'
1649: );
1650:
1651: if (count($args) === 1) {
1652: $this->data = Hash::insert($this->data, $name, $args[0]);
1653:
1654: return $this;
1655: }
1656: if ($name !== null) {
1657: return Hash::get($this->data, $name);
1658: }
1659:
1660: return $this->data;
1661: }
1662:
1663: /**
1664: * Provides a safe accessor for request data. Allows
1665: * you to use Hash::get() compatible paths.
1666: *
1667: * ### Reading values.
1668: *
1669: * ```
1670: * // get all data
1671: * $request->getData();
1672: *
1673: * // Read a specific field.
1674: * $request->getData('Post.title');
1675: *
1676: * // With a default value.
1677: * $request->getData('Post.not there', 'default value');
1678: * ```
1679: *
1680: * When reading values you will get `null` for keys/values that do not exist.
1681: *
1682: * @param string|null $name Dot separated name of the value to read. Or null to read all data.
1683: * @param mixed $default The default data.
1684: * @return null|string|array The value being read.
1685: */
1686: public function getData($name = null, $default = null)
1687: {
1688: if ($name === null) {
1689: return $this->data;
1690: }
1691: if (!is_array($this->data) && $name) {
1692: return $default;
1693: }
1694:
1695: return Hash::get($this->data, $name, $default);
1696: }
1697:
1698: /**
1699: * Safely access the values in $this->params.
1700: *
1701: * @param string $name The name of the parameter to get.
1702: * @param mixed ...$args Value to set (deprecated).
1703: * @return mixed|$this The value of the provided parameter. Will
1704: * return false if the parameter doesn't exist or is falsey.
1705: * @deprecated 3.4.0 Use getParam() and withParam() instead.
1706: */
1707: public function param($name, ...$args)
1708: {
1709: deprecationWarning(
1710: 'ServerRequest::param() is deprecated. ' .
1711: 'Use getParam() and withParam() instead.'
1712: );
1713:
1714: if (count($args) === 1) {
1715: $this->params = Hash::insert($this->params, $name, $args[0]);
1716:
1717: return $this;
1718: }
1719:
1720: return $this->getParam($name);
1721: }
1722:
1723: /**
1724: * Read data from `php://input`. Useful when interacting with XML or JSON
1725: * request body content.
1726: *
1727: * Getting input with a decoding function:
1728: *
1729: * ```
1730: * $this->request->input('json_decode');
1731: * ```
1732: *
1733: * Getting input using a decoding function, and additional params:
1734: *
1735: * ```
1736: * $this->request->input('Xml::build', ['return' => 'DOMDocument']);
1737: * ```
1738: *
1739: * Any additional parameters are applied to the callback in the order they are given.
1740: *
1741: * @param string|null $callback A decoding callback that will convert the string data to another
1742: * representation. Leave empty to access the raw input data. You can also
1743: * supply additional parameters for the decoding callback using var args, see above.
1744: * @param array ...$args The additional arguments
1745: * @return string The decoded/processed request data.
1746: */
1747: public function input($callback = null, ...$args)
1748: {
1749: $this->stream->rewind();
1750: $input = $this->stream->getContents();
1751: if ($callback) {
1752: array_unshift($args, $input);
1753:
1754: return call_user_func_array($callback, $args);
1755: }
1756:
1757: return $input;
1758: }
1759:
1760: /**
1761: * Read cookie data from the request's cookie data.
1762: *
1763: * @param string $key The key you want to read.
1764: * @return null|string Either the cookie value, or null if the value doesn't exist.
1765: * @deprecated 3.4.0 Use getCookie() instead.
1766: */
1767: public function cookie($key)
1768: {
1769: deprecationWarning(
1770: 'ServerRequest::cookie() is deprecated. ' .
1771: 'Use getCookie() instead.'
1772: );
1773:
1774: if (isset($this->cookies[$key])) {
1775: return $this->cookies[$key];
1776: }
1777:
1778: return null;
1779: }
1780:
1781: /**
1782: * Read cookie data from the request's cookie data.
1783: *
1784: * @param string $key The key or dotted path you want to read.
1785: * @param string $default The default value if the cookie is not set.
1786: * @return null|array|string Either the cookie value, or null if the value doesn't exist.
1787: */
1788: public function getCookie($key, $default = null)
1789: {
1790: return Hash::get($this->cookies, $key, $default);
1791: }
1792:
1793: /**
1794: * Get a cookie collection based on the request's cookies
1795: *
1796: * The CookieCollection lets you interact with request cookies using
1797: * `\Cake\Http\Cookie\Cookie` objects and can make converting request cookies
1798: * into response cookies easier.
1799: *
1800: * This method will create a new cookie collection each time it is called.
1801: * This is an optimization that allows fewer objects to be allocated until
1802: * the more complex CookieCollection is needed. In general you should prefer
1803: * `getCookie()` and `getCookieParams()` over this method. Using a CookieCollection
1804: * is ideal if your cookies contain complex JSON encoded data.
1805: *
1806: * @return \Cake\Http\Cookie\CookieCollection
1807: */
1808: public function getCookieCollection()
1809: {
1810: return CookieCollection::createFromServerRequest($this);
1811: }
1812:
1813: /**
1814: * Replace the cookies in the request with those contained in
1815: * the provided CookieCollection.
1816: *
1817: * @param \Cake\Http\Cookie\CookieCollection $cookies The cookie collection
1818: * @return static
1819: */
1820: public function withCookieCollection(CookieCollection $cookies)
1821: {
1822: $new = clone $this;
1823: $values = [];
1824: foreach ($cookies as $cookie) {
1825: $values[$cookie->getName()] = $cookie->getValue();
1826: }
1827: $new->cookies = $values;
1828:
1829: return $new;
1830: }
1831:
1832: /**
1833: * Get all the cookie data from the request.
1834: *
1835: * @return array An array of cookie data.
1836: */
1837: public function getCookieParams()
1838: {
1839: return $this->cookies;
1840: }
1841:
1842: /**
1843: * Replace the cookies and get a new request instance.
1844: *
1845: * @param array $cookies The new cookie data to use.
1846: * @return static
1847: */
1848: public function withCookieParams(array $cookies)
1849: {
1850: $new = clone $this;
1851: $new->cookies = $cookies;
1852:
1853: return $new;
1854: }
1855:
1856: /**
1857: * Get the parsed request body data.
1858: *
1859: * If the request Content-Type is either application/x-www-form-urlencoded
1860: * or multipart/form-data, and the request method is POST, this will be the
1861: * post data. For other content types, it may be the deserialized request
1862: * body.
1863: *
1864: * @return null|array|object The deserialized body parameters, if any.
1865: * These will typically be an array or object.
1866: */
1867: public function getParsedBody()
1868: {
1869: return $this->data;
1870: }
1871:
1872: /**
1873: * Update the parsed body and get a new instance.
1874: *
1875: * @param null|array|object $data The deserialized body data. This will
1876: * typically be in an array or object.
1877: * @return static
1878: */
1879: public function withParsedBody($data)
1880: {
1881: $new = clone $this;
1882: $new->data = $data;
1883:
1884: return $new;
1885: }
1886:
1887: /**
1888: * Retrieves the HTTP protocol version as a string.
1889: *
1890: * @return string HTTP protocol version.
1891: */
1892: public function getProtocolVersion()
1893: {
1894: if ($this->protocol) {
1895: return $this->protocol;
1896: }
1897:
1898: // Lazily populate this data as it is generally not used.
1899: preg_match('/^HTTP\/([\d.]+)$/', $this->getEnv('SERVER_PROTOCOL'), $match);
1900: $protocol = '1.1';
1901: if (isset($match[1])) {
1902: $protocol = $match[1];
1903: }
1904: $this->protocol = $protocol;
1905:
1906: return $this->protocol;
1907: }
1908:
1909: /**
1910: * Return an instance with the specified HTTP protocol version.
1911: *
1912: * The version string MUST contain only the HTTP version number (e.g.,
1913: * "1.1", "1.0").
1914: *
1915: * @param string $version HTTP protocol version
1916: * @return static
1917: */
1918: public function withProtocolVersion($version)
1919: {
1920: if (!preg_match('/^(1\.[01]|2)$/', $version)) {
1921: throw new InvalidArgumentException("Unsupported protocol version '{$version}' provided");
1922: }
1923: $new = clone $this;
1924: $new->protocol = $version;
1925:
1926: return $new;
1927: }
1928:
1929: /**
1930: * Get a value from the request's environment data.
1931: * Fallback to using env() if the key is not set in the $environment property.
1932: *
1933: * @param string $key The key you want to read from.
1934: * @param string|null $default Default value when trying to retrieve an environment
1935: * variable's value that does not exist.
1936: * @return string|null Either the environment value, or null if the value doesn't exist.
1937: */
1938: public function getEnv($key, $default = null)
1939: {
1940: $key = strtoupper($key);
1941: if (!array_key_exists($key, $this->_environment)) {
1942: $this->_environment[$key] = env($key);
1943: }
1944:
1945: return $this->_environment[$key] !== null ? $this->_environment[$key] : $default;
1946: }
1947:
1948: /**
1949: * Update the request with a new environment data element.
1950: *
1951: * Returns an updated request object. This method returns
1952: * a *new* request object and does not mutate the request in-place.
1953: *
1954: * @param string $key The key you want to write to.
1955: * @param string $value Value to set
1956: * @return static
1957: */
1958: public function withEnv($key, $value)
1959: {
1960: $new = clone $this;
1961: $new->_environment[$key] = $value;
1962: $new->clearDetectorCache();
1963:
1964: return $new;
1965: }
1966:
1967: /**
1968: * Get/Set value from the request's environment data.
1969: * Fallback to using env() if key not set in $environment property.
1970: *
1971: * @deprecated 3.5.0 Use getEnv()/withEnv() instead.
1972: * @param string $key The key you want to read/write from/to.
1973: * @param string|null $value Value to set. Default null.
1974: * @param string|null $default Default value when trying to retrieve an environment
1975: * variable's value that does not exist. The value parameter must be null.
1976: * @return $this|string|null This instance if used as setter,
1977: * if used as getter either the environment value, or null if the value doesn't exist.
1978: */
1979: public function env($key, $value = null, $default = null)
1980: {
1981: deprecationWarning(
1982: 'ServerRequest::env() is deprecated. ' .
1983: 'Use getEnv()/withEnv() instead.'
1984: );
1985:
1986: if ($value !== null) {
1987: $this->_environment[$key] = $value;
1988: $this->clearDetectorCache();
1989:
1990: return $this;
1991: }
1992:
1993: $key = strtoupper($key);
1994: if (!array_key_exists($key, $this->_environment)) {
1995: $this->_environment[$key] = env($key);
1996: }
1997:
1998: return $this->_environment[$key] !== null ? $this->_environment[$key] : $default;
1999: }
2000:
2001: /**
2002: * Allow only certain HTTP request methods, if the request method does not match
2003: * a 405 error will be shown and the required "Allow" response header will be set.
2004: *
2005: * Example:
2006: *
2007: * $this->request->allowMethod('post');
2008: * or
2009: * $this->request->allowMethod(['post', 'delete']);
2010: *
2011: * If the request would be GET, response header "Allow: POST, DELETE" will be set
2012: * and a 405 error will be returned.
2013: *
2014: * @param string|array $methods Allowed HTTP request methods.
2015: * @return bool true
2016: * @throws \Cake\Http\Exception\MethodNotAllowedException
2017: */
2018: public function allowMethod($methods)
2019: {
2020: $methods = (array)$methods;
2021: foreach ($methods as $method) {
2022: if ($this->is($method)) {
2023: return true;
2024: }
2025: }
2026: $allowed = strtoupper(implode(', ', $methods));
2027: $e = new MethodNotAllowedException();
2028: $e->responseHeader('Allow', $allowed);
2029: throw $e;
2030: }
2031:
2032: /**
2033: * Read data from php://input, mocked in tests.
2034: *
2035: * @return string contents of php://input
2036: */
2037: protected function _readInput()
2038: {
2039: if (empty($this->_input)) {
2040: $fh = fopen('php://input', 'rb');
2041: $content = stream_get_contents($fh);
2042: fclose($fh);
2043: $this->_input = $content;
2044: }
2045:
2046: return $this->_input;
2047: }
2048:
2049: /**
2050: * Modify data originally from `php://input`. Useful for altering json/xml data
2051: * in middleware or DispatcherFilters before it gets to RequestHandlerComponent
2052: *
2053: * @param string $input A string to replace original parsed data from input()
2054: * @return void
2055: * @deprecated 3.4.0 This method will be removed in 4.0.0. Use withBody() instead.
2056: */
2057: public function setInput($input)
2058: {
2059: deprecationWarning(
2060: 'This method will be removed in 4.0.0.' .
2061: 'Use withBody() instead.'
2062: );
2063:
2064: $stream = new Stream('php://memory', 'rw');
2065: $stream->write($input);
2066: $stream->rewind();
2067: $this->stream = $stream;
2068: }
2069:
2070: /**
2071: * Update the request with a new request data element.
2072: *
2073: * Returns an updated request object. This method returns
2074: * a *new* request object and does not mutate the request in-place.
2075: *
2076: * Use `withParsedBody()` if you need to replace the all request data.
2077: *
2078: * @param string $name The dot separated path to insert $value at.
2079: * @param mixed $value The value to insert into the request data.
2080: * @return static
2081: */
2082: public function withData($name, $value)
2083: {
2084: $copy = clone $this;
2085: $copy->data = Hash::insert($copy->data, $name, $value);
2086:
2087: return $copy;
2088: }
2089:
2090: /**
2091: * Update the request removing a data element.
2092: *
2093: * Returns an updated request object. This method returns
2094: * a *new* request object and does not mutate the request in-place.
2095: *
2096: * @param string $name The dot separated path to remove.
2097: * @return static
2098: */
2099: public function withoutData($name)
2100: {
2101: $copy = clone $this;
2102: $copy->data = Hash::remove($copy->data, $name);
2103:
2104: return $copy;
2105: }
2106:
2107: /**
2108: * Update the request with a new routing parameter
2109: *
2110: * Returns an updated request object. This method returns
2111: * a *new* request object and does not mutate the request in-place.
2112: *
2113: * @param string $name The dot separated path to insert $value at.
2114: * @param mixed $value The value to insert into the the request parameters.
2115: * @return static
2116: */
2117: public function withParam($name, $value)
2118: {
2119: $copy = clone $this;
2120: $copy->params = Hash::insert($copy->params, $name, $value);
2121:
2122: return $copy;
2123: }
2124:
2125: /**
2126: * Safely access the values in $this->params.
2127: *
2128: * @param string $name The name or dotted path to parameter.
2129: * @param mixed $default The default value if `$name` is not set. Default `false`.
2130: * @return mixed
2131: */
2132: public function getParam($name, $default = false)
2133: {
2134: return Hash::get($this->params, $name, $default);
2135: }
2136:
2137: /**
2138: * Return an instance with the specified request attribute.
2139: *
2140: * @param string $name The attribute name.
2141: * @param mixed $value The value of the attribute.
2142: * @return static
2143: */
2144: public function withAttribute($name, $value)
2145: {
2146: $new = clone $this;
2147: if (in_array($name, $this->emulatedAttributes, true)) {
2148: $new->{$name} = $value;
2149: } else {
2150: $new->attributes[$name] = $value;
2151: }
2152:
2153: return $new;
2154: }
2155:
2156: /**
2157: * Return an instance without the specified request attribute.
2158: *
2159: * @param string $name The attribute name.
2160: * @return static
2161: * @throws \InvalidArgumentException
2162: */
2163: public function withoutAttribute($name)
2164: {
2165: $new = clone $this;
2166: if (in_array($name, $this->emulatedAttributes, true)) {
2167: throw new InvalidArgumentException(
2168: "You cannot unset '$name'. It is a required CakePHP attribute."
2169: );
2170: }
2171: unset($new->attributes[$name]);
2172:
2173: return $new;
2174: }
2175:
2176: /**
2177: * Read an attribute from the request, or get the default
2178: *
2179: * @param string $name The attribute name.
2180: * @param mixed|null $default The default value if the attribute has not been set.
2181: * @return mixed
2182: */
2183: public function getAttribute($name, $default = null)
2184: {
2185: if (in_array($name, $this->emulatedAttributes, true)) {
2186: return $this->{$name};
2187: }
2188: if (array_key_exists($name, $this->attributes)) {
2189: return $this->attributes[$name];
2190: }
2191:
2192: return $default;
2193: }
2194:
2195: /**
2196: * Get all the attributes in the request.
2197: *
2198: * This will include the params, webroot, base, and here attributes that CakePHP
2199: * provides.
2200: *
2201: * @return array
2202: */
2203: public function getAttributes()
2204: {
2205: $emulated = [
2206: 'params' => $this->params,
2207: 'webroot' => $this->webroot,
2208: 'base' => $this->base,
2209: 'here' => $this->here
2210: ];
2211:
2212: return $this->attributes + $emulated;
2213: }
2214:
2215: /**
2216: * Get the uploaded file from a dotted path.
2217: *
2218: * @param string $path The dot separated path to the file you want.
2219: * @return null|\Psr\Http\Message\UploadedFileInterface
2220: */
2221: public function getUploadedFile($path)
2222: {
2223: $file = Hash::get($this->uploadedFiles, $path);
2224: if (!$file instanceof UploadedFile) {
2225: return null;
2226: }
2227:
2228: return $file;
2229: }
2230:
2231: /**
2232: * Get the array of uploaded files from the request.
2233: *
2234: * @return array
2235: */
2236: public function getUploadedFiles()
2237: {
2238: return $this->uploadedFiles;
2239: }
2240:
2241: /**
2242: * Update the request replacing the files, and creating a new instance.
2243: *
2244: * @param array $files An array of uploaded file objects.
2245: * @return static
2246: * @throws \InvalidArgumentException when $files contains an invalid object.
2247: */
2248: public function withUploadedFiles(array $files)
2249: {
2250: $this->validateUploadedFiles($files, '');
2251: $new = clone $this;
2252: $new->uploadedFiles = $files;
2253:
2254: return $new;
2255: }
2256:
2257: /**
2258: * Recursively validate uploaded file data.
2259: *
2260: * @param array $uploadedFiles The new files array to validate.
2261: * @param string $path The path thus far.
2262: * @return void
2263: * @throws \InvalidArgumentException If any leaf elements are not valid files.
2264: */
2265: protected function validateUploadedFiles(array $uploadedFiles, $path)
2266: {
2267: foreach ($uploadedFiles as $key => $file) {
2268: if (is_array($file)) {
2269: $this->validateUploadedFiles($file, $key . '.');
2270: continue;
2271: }
2272:
2273: if (!$file instanceof UploadedFileInterface) {
2274: throw new InvalidArgumentException("Invalid file at '{$path}{$key}'");
2275: }
2276: }
2277: }
2278:
2279: /**
2280: * Gets the body of the message.
2281: *
2282: * @return \Psr\Http\Message\StreamInterface Returns the body as a stream.
2283: */
2284: public function getBody()
2285: {
2286: return $this->stream;
2287: }
2288:
2289: /**
2290: * Return an instance with the specified message body.
2291: *
2292: * @param \Psr\Http\Message\StreamInterface $body The new request body
2293: * @return static
2294: */
2295: public function withBody(StreamInterface $body)
2296: {
2297: $new = clone $this;
2298: $new->stream = $body;
2299:
2300: return $new;
2301: }
2302:
2303: /**
2304: * Retrieves the URI instance.
2305: *
2306: * @return \Psr\Http\Message\UriInterface Returns a UriInterface instance
2307: * representing the URI of the request.
2308: */
2309: public function getUri()
2310: {
2311: return $this->uri;
2312: }
2313:
2314: /**
2315: * Return an instance with the specified uri
2316: *
2317: * *Warning* Replacing the Uri will not update the `base`, `webroot`,
2318: * and `url` attributes.
2319: *
2320: * @param \Psr\Http\Message\UriInterface $uri The new request uri
2321: * @param bool $preserveHost Whether or not the host should be retained.
2322: * @return static
2323: */
2324: public function withUri(UriInterface $uri, $preserveHost = false)
2325: {
2326: $new = clone $this;
2327: $new->uri = $uri;
2328:
2329: if ($preserveHost && $this->hasHeader('Host')) {
2330: return $new;
2331: }
2332:
2333: $host = $uri->getHost();
2334: if (!$host) {
2335: return $new;
2336: }
2337: if ($uri->getPort()) {
2338: $host .= ':' . $uri->getPort();
2339: }
2340: $new->_environment['HTTP_HOST'] = $host;
2341:
2342: return $new;
2343: }
2344:
2345: /**
2346: * Create a new instance with a specific request-target.
2347: *
2348: * You can use this method to overwrite the request target that is
2349: * inferred from the request's Uri. This also lets you change the request
2350: * target's form to an absolute-form, authority-form or asterisk-form
2351: *
2352: * @link https://tools.ietf.org/html/rfc7230#section-2.7 (for the various
2353: * request-target forms allowed in request messages)
2354: * @param string $target The request target.
2355: * @return static
2356: */
2357: public function withRequestTarget($target)
2358: {
2359: $new = clone $this;
2360: $new->requestTarget = $target;
2361:
2362: return $new;
2363: }
2364:
2365: /**
2366: * Retrieves the request's target.
2367: *
2368: * Retrieves the message's request-target either as it was requested,
2369: * or as set with `withRequestTarget()`. By default this will return the
2370: * application relative path without base directory, and the query string
2371: * defined in the SERVER environment.
2372: *
2373: * @return string
2374: */
2375: public function getRequestTarget()
2376: {
2377: if ($this->requestTarget !== null) {
2378: return $this->requestTarget;
2379: }
2380:
2381: $target = $this->uri->getPath();
2382: if ($this->uri->getQuery()) {
2383: $target .= '?' . $this->uri->getQuery();
2384: }
2385:
2386: if (empty($target)) {
2387: $target = '/';
2388: }
2389:
2390: return $target;
2391: }
2392:
2393: /**
2394: * Get the path of current request.
2395: *
2396: * @return string
2397: * @since 3.6.1
2398: */
2399: public function getPath()
2400: {
2401: if ($this->requestTarget === null) {
2402: return $this->uri->getPath();
2403: }
2404:
2405: list($path) = explode('?', $this->requestTarget);
2406:
2407: return $path;
2408: }
2409:
2410: /**
2411: * Array access read implementation
2412: *
2413: * @param string $name Name of the key being accessed.
2414: * @return mixed
2415: * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use getParam(), getData() and getQuery() instead.
2416: */
2417: public function offsetGet($name)
2418: {
2419: deprecationWarning(
2420: 'The ArrayAccess methods will be removed in 4.0.0.' .
2421: 'Use getParam(), getData() and getQuery() instead.'
2422: );
2423:
2424: if (isset($this->params[$name])) {
2425: return $this->params[$name];
2426: }
2427: if ($name === 'url') {
2428: return $this->query;
2429: }
2430: if ($name === 'data') {
2431: return $this->data;
2432: }
2433:
2434: return null;
2435: }
2436:
2437: /**
2438: * Array access write implementation
2439: *
2440: * @param string $name Name of the key being written
2441: * @param mixed $value The value being written.
2442: * @return void
2443: * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use withParam() instead.
2444: */
2445: public function offsetSet($name, $value)
2446: {
2447: deprecationWarning(
2448: 'The ArrayAccess methods will be removed in 4.0.0.' .
2449: 'Use withParam() instead.'
2450: );
2451:
2452: $this->params[$name] = $value;
2453: }
2454:
2455: /**
2456: * Array access isset() implementation
2457: *
2458: * @param string $name thing to check.
2459: * @return bool
2460: * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use getParam() instead.
2461: */
2462: public function offsetExists($name)
2463: {
2464: deprecationWarning(
2465: 'The ArrayAccess methods will be removed in 4.0.0.' .
2466: 'Use getParam() instead.'
2467: );
2468:
2469: if ($name === 'url' || $name === 'data') {
2470: return true;
2471: }
2472:
2473: return isset($this->params[$name]);
2474: }
2475:
2476: /**
2477: * Array access unset() implementation
2478: *
2479: * @param string $name Name to unset.
2480: * @return void
2481: * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use withParam() instead.
2482: */
2483: public function offsetUnset($name)
2484: {
2485: deprecationWarning(
2486: 'The ArrayAccess methods will be removed in 4.0.0.' .
2487: 'Use withParam() instead.'
2488: );
2489:
2490: unset($this->params[$name]);
2491: }
2492: }
2493:
2494: // @deprecated 3.4.0 Add backwards compat alias.
2495: class_alias('Cake\Http\ServerRequest', 'Cake\Network\Request');
2496: