ServerRequest.php 76 KB

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