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