ServerRequestTest.php 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 2.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Http;
  17. use BadMethodCallException;
  18. use Cake\Core\Configure;
  19. use Cake\Http\Cookie\Cookie;
  20. use Cake\Http\Cookie\CookieCollection;
  21. use Cake\Http\Exception\MethodNotAllowedException;
  22. use Cake\Http\FlashMessage;
  23. use Cake\Http\ServerRequest;
  24. use Cake\Http\Session;
  25. use Cake\TestSuite\TestCase;
  26. use InvalidArgumentException;
  27. use Laminas\Diactoros\UploadedFile;
  28. use Laminas\Diactoros\Uri;
  29. /**
  30. * ServerRequest Test
  31. */
  32. class ServerRequestTest extends TestCase
  33. {
  34. /**
  35. * Test custom detector with extra arguments.
  36. */
  37. public function testCustomArgsDetector(): void
  38. {
  39. $request = new ServerRequest();
  40. $request->addDetector('controller', function ($request, $name) {
  41. return $request->getParam('controller') === $name;
  42. });
  43. $request = $request->withParam('controller', 'cake');
  44. $this->assertTrue($request->is('controller', 'cake'));
  45. $this->assertFalse($request->is('controller', 'nonExistingController'));
  46. $this->assertTrue($request->isController('cake'));
  47. $this->assertFalse($request->isController('nonExistingController'));
  48. }
  49. /**
  50. * Test the header detector.
  51. */
  52. public function testHeaderDetector(): void
  53. {
  54. $request = new ServerRequest();
  55. $request->addDetector('host', ['header' => ['host' => 'cakephp.org']]);
  56. $request = $request->withEnv('HTTP_HOST', 'cakephp.org');
  57. $this->assertTrue($request->is('host'));
  58. $request = $request->withEnv('HTTP_HOST', 'php.net');
  59. $this->assertFalse($request->is('host'));
  60. }
  61. /**
  62. * Test the accept header detector.
  63. */
  64. public function testExtensionDetector(): void
  65. {
  66. $request = new ServerRequest();
  67. $request = $request->withParam('_ext', 'json');
  68. $this->assertTrue($request->is('json'));
  69. $request = new ServerRequest();
  70. $request = $request->withParam('_ext', 'xml');
  71. $this->assertFalse($request->is('json'));
  72. }
  73. /**
  74. * Test the accept header detector.
  75. */
  76. public function testAcceptHeaderDetector(): void
  77. {
  78. $request = new ServerRequest();
  79. $request = $request->withEnv('HTTP_ACCEPT', 'application/json, text/plain, */*');
  80. $this->assertTrue($request->is('json'));
  81. $request = new ServerRequest();
  82. $request = $request->withEnv('HTTP_ACCEPT', 'text/plain, */*');
  83. $this->assertFalse($request->is('json'));
  84. $request = new ServerRequest();
  85. $request = $request->withEnv('HTTP_ACCEPT', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8');
  86. $this->assertFalse($request->is('json'));
  87. $this->assertFalse($request->is('xml'));
  88. $this->assertFalse($request->is('xml'));
  89. }
  90. public function testConstructor(): void
  91. {
  92. $request = new ServerRequest();
  93. $this->assertInstanceOf(FlashMessage::class, $request->getAttribute('flash'));
  94. }
  95. /**
  96. * Test construction with query data
  97. */
  98. public function testConstructionQueryData(): void
  99. {
  100. $data = [
  101. 'query' => [
  102. 'one' => 'param',
  103. 'two' => 'banana',
  104. ],
  105. 'url' => 'some/path',
  106. ];
  107. $request = new ServerRequest($data);
  108. $this->assertSame('param', $request->getQuery('one'));
  109. $this->assertEquals($data['query'], $request->getQueryParams());
  110. $this->assertSame('/some/path', $request->getRequestTarget());
  111. }
  112. /**
  113. * Test constructing with a string url.
  114. */
  115. public function testConstructStringUrlIgnoreServer(): void
  116. {
  117. $request = new ServerRequest([
  118. 'url' => '/articles/view/1',
  119. 'environment' => ['REQUEST_URI' => '/some/other/path'],
  120. ]);
  121. $this->assertSame('/articles/view/1', $request->getUri()->getPath());
  122. $request = new ServerRequest(['url' => '/']);
  123. $this->assertSame('/', $request->getUri()->getPath());
  124. }
  125. /**
  126. * Test that querystring args provided in the URL string are parsed.
  127. */
  128. public function testQueryStringParsingFromInputUrl(): void
  129. {
  130. $request = new ServerRequest(['url' => 'some/path?one=something&two=else']);
  131. $expected = ['one' => 'something', 'two' => 'else'];
  132. $this->assertEquals($expected, $request->getQueryParams());
  133. $this->assertSame('/some/path', $request->getUri()->getPath());
  134. $this->assertSame('one=something&two=else', $request->getUri()->getQuery());
  135. }
  136. /**
  137. * Test that querystrings are handled correctly.
  138. */
  139. public function testQueryStringAndNamedParams(): void
  140. {
  141. $config = ['environment' => ['REQUEST_URI' => '/tasks/index?ts=123456']];
  142. $request = new ServerRequest($config);
  143. $this->assertSame('/tasks/index', $request->getRequestTarget());
  144. $config = ['environment' => ['REQUEST_URI' => '/some/path?url=http://cakephp.org']];
  145. $request = new ServerRequest($config);
  146. $this->assertSame('/some/path', $request->getRequestTarget());
  147. $config = ['environment' => [
  148. 'REQUEST_URI' => Configure::read('App.fullBaseUrl') . '/other/path?url=http://cakephp.org',
  149. ]];
  150. $request = new ServerRequest($config);
  151. $this->assertSame('/other/path', $request->getRequestTarget());
  152. }
  153. /**
  154. * Test that URL in path is handled correctly.
  155. */
  156. public function testUrlInPath(): void
  157. {
  158. $config = ['environment' => ['REQUEST_URI' => '/jump/http://cakephp.org']];
  159. $request = new ServerRequest($config);
  160. $this->assertSame('/jump/http://cakephp.org', $request->getRequestTarget());
  161. $config = ['environment' => [
  162. 'REQUEST_URI' => Configure::read('App.fullBaseUrl') . '/jump/http://cakephp.org',
  163. ]];
  164. $request = new ServerRequest($config);
  165. $this->assertSame('/jump/http://cakephp.org', $request->getRequestTarget());
  166. }
  167. /**
  168. * Test getPath().
  169. */
  170. public function testGetPath(): void
  171. {
  172. $request = new ServerRequest(['url' => '']);
  173. $this->assertSame('/', $request->getPath());
  174. $request = new ServerRequest(['url' => 'some/path?one=something&two=else']);
  175. $this->assertSame('/some/path', $request->getPath());
  176. $request = $request->withRequestTarget('/foo/bar?x=y');
  177. $this->assertSame('/foo/bar', $request->getPath());
  178. }
  179. /**
  180. * Test parsing POST data into the object.
  181. */
  182. public function testPostParsing(): void
  183. {
  184. $post = [
  185. 'Article' => ['title'],
  186. ];
  187. $request = new ServerRequest(compact('post'));
  188. $this->assertEquals($post, $request->getData());
  189. $post = ['one' => 1, 'two' => 'three'];
  190. $request = new ServerRequest(compact('post'));
  191. $this->assertEquals($post, $request->getData());
  192. $post = [
  193. 'Article' => ['title' => 'Testing'],
  194. 'action' => 'update',
  195. ];
  196. $request = new ServerRequest(compact('post'));
  197. $this->assertEquals($post, $request->getData());
  198. }
  199. /**
  200. * Test parsing JSON PUT data into the object.
  201. *
  202. * @group deprecated
  203. */
  204. public function testPutParsingJSON(): void
  205. {
  206. $data = '{"Article":["title"]}';
  207. $request = new ServerRequest([
  208. 'input' => $data,
  209. 'environment' => [
  210. 'REQUEST_METHOD' => 'PUT',
  211. 'CONTENT_TYPE' => 'application/json',
  212. ],
  213. ]);
  214. $this->assertEquals([], $request->getData());
  215. $this->deprecated(function () use ($request): void {
  216. $result = $request->input('json_decode', true);
  217. $this->assertEquals(['title'], $result['Article']);
  218. });
  219. }
  220. /**
  221. * Test that the constructor uses uploaded file objects
  222. * if they are present. This could happen in test scenarios.
  223. */
  224. public function testFilesObject(): void
  225. {
  226. $file = new UploadedFile(
  227. __FILE__,
  228. 123,
  229. UPLOAD_ERR_OK,
  230. 'test.php',
  231. 'text/plain'
  232. );
  233. $request = new ServerRequest(['files' => ['avatar' => $file]]);
  234. $this->assertSame(['avatar' => $file], $request->getUploadedFiles());
  235. }
  236. /**
  237. * Test passing an empty files list.
  238. */
  239. public function testFilesWithEmptyList(): void
  240. {
  241. $request = new ServerRequest([
  242. 'files' => [],
  243. ]);
  244. $this->assertEmpty($request->getData());
  245. $this->assertEmpty($request->getUploadedFiles());
  246. }
  247. /**
  248. * Test replacing files.
  249. */
  250. public function testWithUploadedFiles(): void
  251. {
  252. $file = new UploadedFile(
  253. __FILE__,
  254. 123,
  255. UPLOAD_ERR_OK,
  256. 'test.php',
  257. 'text/plain'
  258. );
  259. $request = new ServerRequest();
  260. $new = $request->withUploadedFiles(['picture' => $file]);
  261. $this->assertSame([], $request->getUploadedFiles());
  262. $this->assertNotSame($new, $request);
  263. $this->assertSame(['picture' => $file], $new->getUploadedFiles());
  264. }
  265. /**
  266. * Test getting a single file
  267. */
  268. public function testGetUploadedFile(): void
  269. {
  270. $file = new UploadedFile(
  271. __FILE__,
  272. 123,
  273. UPLOAD_ERR_OK,
  274. 'test.php',
  275. 'text/plain'
  276. );
  277. $request = new ServerRequest();
  278. $new = $request->withUploadedFiles(['picture' => $file]);
  279. $this->assertNull($new->getUploadedFile(''));
  280. $this->assertSame($file, $new->getUploadedFile('picture'));
  281. $new = $request->withUploadedFiles([
  282. 'pictures' => [
  283. [
  284. 'image' => $file,
  285. ],
  286. ],
  287. ]);
  288. $this->assertNull($new->getUploadedFile('pictures'));
  289. $this->assertNull($new->getUploadedFile('pictures.0'));
  290. $this->assertNull($new->getUploadedFile('pictures.1'));
  291. $this->assertSame($file, $new->getUploadedFile('pictures.0.image'));
  292. }
  293. /**
  294. * Test replacing files with an invalid file
  295. */
  296. public function testWithUploadedFilesInvalidFile(): void
  297. {
  298. $this->expectException(InvalidArgumentException::class);
  299. $this->expectExceptionMessage('Invalid file at \'avatar\'');
  300. $request = new ServerRequest();
  301. $request->withUploadedFiles(['avatar' => 'not a file']);
  302. }
  303. /**
  304. * Test replacing files with an invalid file
  305. */
  306. public function testWithUploadedFilesInvalidFileNested(): void
  307. {
  308. $this->expectException(InvalidArgumentException::class);
  309. $this->expectExceptionMessage('Invalid file at \'user.avatar\'');
  310. $request = new ServerRequest();
  311. $request->withUploadedFiles(['user' => ['avatar' => 'not a file']]);
  312. }
  313. /**
  314. * Test the clientIp method.
  315. */
  316. public function testClientIp(): void
  317. {
  318. $request = new ServerRequest(['environment' => [
  319. 'HTTP_X_FORWARDED_FOR' => '192.168.1.5, 10.0.1.1, proxy.com, real.ip',
  320. 'HTTP_X_REAL_IP' => '192.168.1.1',
  321. 'HTTP_CLIENT_IP' => '192.168.1.2',
  322. 'REMOTE_ADDR' => '192.168.1.3',
  323. ]]);
  324. $request->trustProxy = true;
  325. $this->assertSame('real.ip', $request->clientIp());
  326. $request = $request->withEnv('HTTP_X_FORWARDED_FOR', '');
  327. $this->assertSame('192.168.1.1', $request->clientIp());
  328. $request = $request->withEnv('HTTP_X_REAL_IP', '');
  329. $this->assertSame('192.168.1.2', $request->clientIp());
  330. $request->trustProxy = false;
  331. $this->assertSame('192.168.1.3', $request->clientIp());
  332. $request = $request->withEnv('HTTP_X_FORWARDED_FOR', '');
  333. $this->assertSame('192.168.1.3', $request->clientIp());
  334. $request = $request->withEnv('HTTP_CLIENT_IP', '');
  335. $this->assertSame('192.168.1.3', $request->clientIp());
  336. }
  337. /**
  338. * test clientIp method with trusted proxies
  339. */
  340. public function testClientIpWithTrustedProxies(): void
  341. {
  342. $request = new ServerRequest(['environment' => [
  343. 'HTTP_X_FORWARDED_FOR' => 'real.ip, 192.168.1.0, 192.168.1.2, 192.168.1.3',
  344. 'HTTP_X_REAL_IP' => '192.168.1.1',
  345. 'HTTP_CLIENT_IP' => '192.168.1.2',
  346. 'REMOTE_ADDR' => '192.168.1.4',
  347. ]]);
  348. $request->setTrustedProxies([
  349. '192.168.1.0',
  350. '192.168.1.1',
  351. '192.168.1.2',
  352. '192.168.1.3',
  353. ]);
  354. $this->assertSame('real.ip', $request->clientIp());
  355. $request = $request->withEnv(
  356. 'HTTP_X_FORWARDED_FOR',
  357. 'spoof.fake.ip, real.ip, 192.168.1.0, 192.168.1.2, 192.168.1.3'
  358. );
  359. $this->assertSame('192.168.1.3', $request->clientIp());
  360. $request = $request->withEnv('HTTP_X_FORWARDED_FOR', '');
  361. $this->assertSame('192.168.1.1', $request->clientIp());
  362. $request->trustProxy = false;
  363. $this->assertSame('192.168.1.4', $request->clientIp());
  364. }
  365. /**
  366. * Test the referrer function.
  367. */
  368. public function testReferer(): void
  369. {
  370. $request = new ServerRequest(['webroot' => '/']);
  371. $request = $request->withEnv('HTTP_REFERER', 'http://cakephp.org');
  372. $result = $request->referer(false);
  373. $this->assertSame('http://cakephp.org', $result);
  374. $request = $request->withEnv('HTTP_REFERER', '');
  375. $result = $request->referer(true);
  376. $this->assertNull($result);
  377. $result = $request->referer(false);
  378. $this->assertNull($result);
  379. $request = $request->withEnv('HTTP_REFERER', Configure::read('App.fullBaseUrl') . '/some/path');
  380. $result = $request->referer();
  381. $this->assertSame('/some/path', $result);
  382. $request = $request->withEnv('HTTP_REFERER', Configure::read('App.fullBaseUrl') . '///cakephp.org/');
  383. $result = $request->referer();
  384. $this->assertSame('/', $result); // Avoid returning scheme-relative URLs.
  385. $request = $request->withEnv('HTTP_REFERER', Configure::read('App.fullBaseUrl') . '/0');
  386. $result = $request->referer();
  387. $this->assertSame('/0', $result);
  388. $request = $request->withEnv('HTTP_REFERER', Configure::read('App.fullBaseUrl') . '/');
  389. $result = $request->referer();
  390. $this->assertSame('/', $result);
  391. $request = $request->withEnv('HTTP_REFERER', Configure::read('App.fullBaseUrl') . '/some/path');
  392. $result = $request->referer(false);
  393. $this->assertSame(Configure::read('App.fullBaseUrl') . '/some/path', $result);
  394. }
  395. /**
  396. * Test referer() with a base path that duplicates the
  397. * first segment.
  398. */
  399. public function testRefererBasePath(): void
  400. {
  401. $request = new ServerRequest([
  402. 'url' => '/waves/users/login',
  403. 'webroot' => '/waves/',
  404. 'base' => '/waves',
  405. ]);
  406. $request = $request->withEnv('HTTP_REFERER', Configure::read('App.fullBaseUrl') . '/waves/waves/add');
  407. $result = $request->referer();
  408. $this->assertSame('/waves/add', $result);
  409. }
  410. /**
  411. * test the simple uses of is()
  412. */
  413. public function testIsHttpMethods(): void
  414. {
  415. $request = new ServerRequest();
  416. $request = $request->withEnv('REQUEST_METHOD', 'GET');
  417. $this->assertTrue($request->is('get'));
  418. $request = $request->withEnv('REQUEST_METHOD', 'POST');
  419. $this->assertTrue($request->is('POST'));
  420. $request = $request->withEnv('REQUEST_METHOD', 'PUT');
  421. $this->assertTrue($request->is('put'));
  422. $this->assertFalse($request->is('get'));
  423. $request = $request->withEnv('REQUEST_METHOD', 'DELETE');
  424. $this->assertTrue($request->is('delete'));
  425. $this->assertTrue($request->isDelete());
  426. $request = $request->withEnv('REQUEST_METHOD', 'delete');
  427. $this->assertFalse($request->is('delete'));
  428. }
  429. public function testExceptionForInvalidType()
  430. {
  431. $this->expectException(InvalidArgumentException::class);
  432. $this->expectExceptionMessage('No detector set for type `nonexistent`');
  433. $request = new ServerRequest();
  434. $this->assertFalse($request->is('nonexistent'));
  435. }
  436. /**
  437. * Test is() with JSON and XML.
  438. */
  439. public function testIsJsonAndXml(): void
  440. {
  441. $request = new ServerRequest();
  442. $request = $request->withEnv('HTTP_ACCEPT', 'application/json, text/plain, */*');
  443. $this->assertTrue($request->is('json'));
  444. $request = new ServerRequest();
  445. $request = $request->withEnv('HTTP_ACCEPT', 'application/xml, text/plain, */*');
  446. $this->assertTrue($request->is('xml'));
  447. $request = new ServerRequest();
  448. $request = $request->withEnv('HTTP_ACCEPT', 'text/xml, */*');
  449. $this->assertTrue($request->is('xml'));
  450. }
  451. /**
  452. * Test is() with multiple types.
  453. */
  454. public function testIsMultiple(): void
  455. {
  456. $request = new ServerRequest();
  457. $request = $request->withEnv('REQUEST_METHOD', 'GET');
  458. $this->assertTrue($request->is(['get', 'post']));
  459. $request = $request->withEnv('REQUEST_METHOD', 'POST');
  460. $this->assertTrue($request->is(['get', 'post']));
  461. $request = $request->withEnv('REQUEST_METHOD', 'PUT');
  462. $this->assertFalse($request->is(['get', 'post']));
  463. }
  464. /**
  465. * Test isAll()
  466. */
  467. public function testIsAll(): void
  468. {
  469. $request = new ServerRequest();
  470. $request = $request->withEnv('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest');
  471. $request = $request->withEnv('REQUEST_METHOD', 'GET');
  472. $this->assertTrue($request->isAll(['ajax', 'get']));
  473. $this->assertFalse($request->isAll(['post', 'get']));
  474. $this->assertFalse($request->isAll(['ajax', 'post']));
  475. }
  476. /**
  477. * Test getMethod()
  478. */
  479. public function testGetMethod(): void
  480. {
  481. $request = new ServerRequest([
  482. 'environment' => ['REQUEST_METHOD' => 'delete'],
  483. ]);
  484. $this->assertSame('delete', $request->getMethod());
  485. }
  486. /**
  487. * Test withMethod()
  488. */
  489. public function testWithMethod(): void
  490. {
  491. $request = new ServerRequest([
  492. 'environment' => ['REQUEST_METHOD' => 'delete'],
  493. ]);
  494. $new = $request->withMethod('put');
  495. $this->assertNotSame($new, $request);
  496. $this->assertSame('delete', $request->getMethod());
  497. $this->assertSame('put', $new->getMethod());
  498. }
  499. /**
  500. * Test withMethod() and invalid data
  501. */
  502. public function testWithMethodInvalid(): void
  503. {
  504. $this->expectException(InvalidArgumentException::class);
  505. $this->expectExceptionMessage('Unsupported HTTP method "no good" provided');
  506. $request = new ServerRequest([
  507. 'environment' => ['REQUEST_METHOD' => 'delete'],
  508. ]);
  509. $request->withMethod('no good');
  510. }
  511. /**
  512. * Test getProtocolVersion()
  513. */
  514. public function testGetProtocolVersion(): void
  515. {
  516. $request = new ServerRequest();
  517. $this->assertSame('1.1', $request->getProtocolVersion());
  518. // SERVER var.
  519. $request = new ServerRequest([
  520. 'environment' => ['SERVER_PROTOCOL' => 'HTTP/1.0'],
  521. ]);
  522. $this->assertSame('1.0', $request->getProtocolVersion());
  523. }
  524. /**
  525. * Test withProtocolVersion()
  526. */
  527. public function testWithProtocolVersion(): void
  528. {
  529. $request = new ServerRequest();
  530. $new = $request->withProtocolVersion('1.0');
  531. $this->assertNotSame($new, $request);
  532. $this->assertSame('1.1', $request->getProtocolVersion());
  533. $this->assertSame('1.0', $new->getProtocolVersion());
  534. }
  535. /**
  536. * Test withProtocolVersion() and invalid data
  537. */
  538. public function testWithProtocolVersionInvalid(): void
  539. {
  540. $this->expectException(InvalidArgumentException::class);
  541. $this->expectExceptionMessage('Unsupported protocol version \'no good\' provided');
  542. $request = new ServerRequest();
  543. $request->withProtocolVersion('no good');
  544. }
  545. /**
  546. * Test host retrieval.
  547. */
  548. public function testHost(): void
  549. {
  550. $request = new ServerRequest(['environment' => [
  551. 'HTTP_HOST' => 'localhost',
  552. 'HTTP_X_FORWARDED_HOST' => 'cakephp.org',
  553. ]]);
  554. $this->assertSame('localhost', $request->host());
  555. $request->trustProxy = true;
  556. $this->assertSame('cakephp.org', $request->host());
  557. }
  558. /**
  559. * test port retrieval.
  560. */
  561. public function testPort(): void
  562. {
  563. $request = new ServerRequest(['environment' => ['SERVER_PORT' => '80']]);
  564. $this->assertSame('80', $request->port());
  565. $request = $request->withEnv('SERVER_PORT', '443');
  566. $request = $request->withEnv('HTTP_X_FORWARDED_PORT', '80');
  567. $this->assertSame('443', $request->port());
  568. $request->trustProxy = true;
  569. $this->assertSame('80', $request->port());
  570. }
  571. /**
  572. * test domain retrieval.
  573. */
  574. public function testDomain(): void
  575. {
  576. $request = new ServerRequest(['environment' => ['HTTP_HOST' => 'something.example.com']]);
  577. $this->assertSame('example.com', $request->domain());
  578. $request = $request->withEnv('HTTP_HOST', 'something.example.co.uk');
  579. $this->assertSame('example.co.uk', $request->domain(2));
  580. }
  581. /**
  582. * Test scheme() method.
  583. */
  584. public function testScheme(): void
  585. {
  586. $request = new ServerRequest(['environment' => ['HTTPS' => 'on']]);
  587. $this->assertSame('https', $request->scheme());
  588. $request = $request->withEnv('HTTPS', '');
  589. $this->assertSame('http', $request->scheme());
  590. $request = $request->withEnv('HTTP_X_FORWARDED_PROTO', 'https');
  591. $request->trustProxy = true;
  592. $this->assertSame('https', $request->scheme());
  593. }
  594. /**
  595. * test getting subdomains for a host.
  596. */
  597. public function testSubdomain(): void
  598. {
  599. $request = new ServerRequest(['environment' => ['HTTP_HOST' => 'something.example.com']]);
  600. $this->assertEquals(['something'], $request->subdomains());
  601. $request = $request->withEnv('HTTP_HOST', 'www.something.example.com');
  602. $this->assertEquals(['www', 'something'], $request->subdomains());
  603. $request = $request->withEnv('HTTP_HOST', 'www.something.example.co.uk');
  604. $this->assertEquals(['www', 'something'], $request->subdomains(2));
  605. $request = $request->withEnv('HTTP_HOST', 'example.co.uk');
  606. $this->assertEquals([], $request->subdomains(2));
  607. }
  608. /**
  609. * Test AJAX, flash and friends
  610. */
  611. public function testisAjax(): void
  612. {
  613. $request = new ServerRequest();
  614. $request = $request->withEnv('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest');
  615. $this->assertTrue($request->is('ajax'));
  616. $request = $request->withEnv('HTTP_X_REQUESTED_WITH', 'XMLHTTPREQUEST');
  617. $this->assertFalse($request->is('ajax'));
  618. $this->assertFalse($request->isAjax());
  619. }
  620. /**
  621. * Test __call exceptions
  622. */
  623. public function testMagicCallExceptionOnUnknownMethod(): void
  624. {
  625. $this->expectException(BadMethodCallException::class);
  626. $request = new ServerRequest();
  627. $request->IamABanana();
  628. }
  629. /**
  630. * Test is(ssl)
  631. */
  632. public function testIsSsl(): void
  633. {
  634. $request = new ServerRequest();
  635. $request = $request->withEnv('HTTPS', 'on');
  636. $this->assertTrue($request->is('ssl'));
  637. $this->assertTrue($request->is('https'));
  638. $request = $request->withEnv('HTTPS', '1');
  639. $this->assertTrue($request->is('ssl'));
  640. $this->assertTrue($request->is('https'));
  641. $request = $request->withEnv('HTTPS', 'I am not empty');
  642. $this->assertFalse($request->is('ssl'));
  643. $this->assertFalse($request->is('https'));
  644. $request = $request->withEnv('HTTPS', 'off');
  645. $this->assertFalse($request->is('ssl'));
  646. $this->assertFalse($request->is('https'));
  647. $request = $request->withEnv('HTTPS', '');
  648. $this->assertFalse($request->is('ssl'));
  649. $this->assertFalse($request->is('https'));
  650. }
  651. /**
  652. * Test adding detectors and having them work.
  653. */
  654. public function testAddDetector(): void
  655. {
  656. $request = new ServerRequest();
  657. ServerRequest::addDetector('closure', function ($request) {
  658. return true;
  659. });
  660. $this->assertTrue($request->is('closure'));
  661. ServerRequest::addDetector('get', function ($request) {
  662. return $request->getEnv('REQUEST_METHOD') === 'GET';
  663. });
  664. $request = $request->withEnv('REQUEST_METHOD', 'GET');
  665. $this->assertTrue($request->is('get'));
  666. ServerRequest::addDetector('compare', ['env' => 'TEST_VAR', 'value' => 'something']);
  667. $request = $request->withEnv('TEST_VAR', 'something');
  668. $this->assertTrue($request->is('compare'), 'Value match failed.');
  669. $request = $request->withEnv('TEST_VAR', 'wrong');
  670. $this->assertFalse($request->is('compare'), 'Value mis-match failed.');
  671. ServerRequest::addDetector('compareCamelCase', ['env' => 'TEST_VAR', 'value' => 'foo']);
  672. $request = $request->withEnv('TEST_VAR', 'foo');
  673. $this->assertTrue($request->is('compareCamelCase'), 'Value match failed.');
  674. $this->assertTrue($request->is('comparecamelcase'), 'detectors should be case insensitive');
  675. $this->assertTrue($request->is('COMPARECAMELCASE'), 'detectors should be case insensitive');
  676. $request = $request->withEnv('TEST_VAR', 'not foo');
  677. $this->assertFalse($request->is('compareCamelCase'), 'Value match failed.');
  678. $this->assertFalse($request->is('comparecamelcase'), 'detectors should be case insensitive');
  679. $this->assertFalse($request->is('COMPARECAMELCASE'), 'detectors should be case insensitive');
  680. ServerRequest::addDetector('banana', ['env' => 'TEST_VAR', 'pattern' => '/^ban.*$/']);
  681. $request = $request->withEnv('TEST_VAR', 'banana');
  682. $this->assertTrue($request->isBanana());
  683. $request = $request->withEnv('TEST_VAR', 'wrong value');
  684. $this->assertFalse($request->isBanana());
  685. ServerRequest::addDetector('mobile', ['env' => 'HTTP_USER_AGENT', 'options' => ['Imagination']]);
  686. $request = $request->withEnv('HTTP_USER_AGENT', 'Imagination land');
  687. $this->assertTrue($request->isMobile());
  688. ServerRequest::addDetector('index', ['param' => 'action', 'value' => 'index']);
  689. $request = $request->withParam('action', 'index');
  690. $request->clearDetectorCache();
  691. $this->assertTrue($request->isIndex());
  692. $request = $request->withParam('action', 'add');
  693. $request->clearDetectorCache();
  694. $this->assertFalse($request->isIndex());
  695. ServerRequest::addDetector('withParams', function ($request, array $params) {
  696. foreach ($params as $name => $value) {
  697. if ($request->getParam($name) != $value) {
  698. return false;
  699. }
  700. }
  701. return true;
  702. });
  703. $request = $request->withParam('controller', 'Pages')->withParam('action', 'index');
  704. $request->clearDetectorCache();
  705. $this->assertTrue($request->isWithParams(['controller' => 'Pages', 'action' => 'index']));
  706. $request = $request->withParam('controller', 'Posts');
  707. $request->clearDetectorCache();
  708. $this->assertFalse($request->isWithParams(['controller' => 'Pages', 'action' => 'index']));
  709. ServerRequest::addDetector('callme', function ($request) {
  710. return $request->getAttribute('return');
  711. });
  712. $request = $request->withAttribute('return', true);
  713. $request->clearDetectorCache();
  714. $this->assertTrue($request->isCallMe());
  715. ServerRequest::addDetector('extension', ['param' => '_ext', 'options' => ['pdf', 'png', 'txt']]);
  716. $request = $request->withParam('_ext', 'pdf');
  717. $request->clearDetectorCache();
  718. $this->assertTrue($request->is('extension'));
  719. $request = $request->withParam('_ext', 'exe');
  720. $request->clearDetectorCache();
  721. $this->assertFalse($request->isExtension());
  722. }
  723. /**
  724. * Test getting headers
  725. */
  726. public function testHeader(): void
  727. {
  728. $request = new ServerRequest(['environment' => [
  729. 'HTTP_HOST' => 'localhost',
  730. 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-ca) AppleWebKit/534.8+ (KHTML, like Gecko) Version/5.0 Safari/533.16',
  731. 'CONTENT_TYPE' => 'application/json',
  732. 'CONTENT_LENGTH' => '1337',
  733. 'HTTP_CONTENT_MD5' => 'abc123',
  734. ]]);
  735. $this->assertEquals($request->getEnv('HTTP_HOST'), $request->getHeaderLine('host'));
  736. $this->assertEquals($request->getEnv('HTTP_USER_AGENT'), $request->getHeaderLine('User-Agent'));
  737. $this->assertEquals($request->getEnv('CONTENT_LENGTH'), $request->getHeaderLine('content-length'));
  738. $this->assertEquals($request->getEnv('CONTENT_TYPE'), $request->getHeaderLine('content-type'));
  739. $this->assertEquals($request->getEnv('HTTP_CONTENT_MD5'), $request->getHeaderLine('content-md5'));
  740. }
  741. /**
  742. * Test getting headers with psr7 methods
  743. */
  744. public function testGetHeaders(): void
  745. {
  746. $request = new ServerRequest(['environment' => [
  747. 'HTTP_HOST' => 'localhost',
  748. 'CONTENT_TYPE' => 'application/json',
  749. 'CONTENT_LENGTH' => 1337,
  750. 'HTTP_CONTENT_MD5' => 'abc123',
  751. 'HTTP_DOUBLE' => ['a', 'b'],
  752. ]]);
  753. $headers = $request->getHeaders();
  754. $expected = [
  755. 'Host' => ['localhost'],
  756. 'Content-Type' => ['application/json'],
  757. 'Content-Length' => [1337],
  758. 'Content-Md5' => ['abc123'],
  759. 'Double' => ['a', 'b'],
  760. ];
  761. $this->assertEquals($expected, $headers);
  762. }
  763. /**
  764. * Test hasHeader
  765. */
  766. public function testHasHeader(): void
  767. {
  768. $request = new ServerRequest(['environment' => [
  769. 'HTTP_HOST' => 'localhost',
  770. 'CONTENT_TYPE' => 'application/json',
  771. 'CONTENT_LENGTH' => 1337,
  772. 'HTTP_CONTENT_MD5' => 'abc123',
  773. 'HTTP_DOUBLE' => ['a', 'b'],
  774. ]]);
  775. $this->assertTrue($request->hasHeader('Host'));
  776. $this->assertTrue($request->hasHeader('Content-Type'));
  777. $this->assertTrue($request->hasHeader('Content-MD5'));
  778. $this->assertTrue($request->hasHeader('Double'));
  779. $this->assertFalse($request->hasHeader('Authorization'));
  780. }
  781. /**
  782. * Test getting headers with psr7 methods
  783. */
  784. public function testGetHeader(): void
  785. {
  786. $request = new ServerRequest(['environment' => [
  787. 'HTTP_HOST' => 'localhost',
  788. 'CONTENT_TYPE' => 'application/json',
  789. 'CONTENT_LENGTH' => 1337,
  790. 'HTTP_CONTENT_MD5' => 'abc123',
  791. 'HTTP_DOUBLE' => ['a', 'b'],
  792. ]]);
  793. $this->assertEquals([], $request->getHeader('Not-there'));
  794. $expected = [$request->getEnv('HTTP_HOST')];
  795. $this->assertEquals($expected, $request->getHeader('Host'));
  796. $this->assertEquals($expected, $request->getHeader('host'));
  797. $this->assertEquals($expected, $request->getHeader('HOST'));
  798. $this->assertEquals(['a', 'b'], $request->getHeader('Double'));
  799. }
  800. /**
  801. * Test getting headers with psr7 methods
  802. */
  803. public function testGetHeaderLine(): void
  804. {
  805. $request = new ServerRequest(['environment' => [
  806. 'HTTP_HOST' => 'localhost',
  807. 'CONTENT_TYPE' => 'application/json',
  808. 'CONTENT_LENGTH' => '1337',
  809. 'HTTP_CONTENT_MD5' => 'abc123',
  810. 'HTTP_DOUBLE' => ['a', 'b'],
  811. ]]);
  812. $this->assertSame('', $request->getHeaderLine('Authorization'));
  813. $expected = $request->getEnv('CONTENT_LENGTH');
  814. $this->assertEquals($expected, $request->getHeaderLine('Content-Length'));
  815. $this->assertEquals($expected, $request->getHeaderLine('content-Length'));
  816. $this->assertEquals($expected, $request->getHeaderLine('ConTent-LenGth'));
  817. $this->assertSame('a, b', $request->getHeaderLine('Double'));
  818. }
  819. /**
  820. * Test setting a header.
  821. */
  822. public function testWithHeader(): void
  823. {
  824. $request = new ServerRequest(['environment' => [
  825. 'HTTP_HOST' => 'localhost',
  826. 'CONTENT_TYPE' => 'application/json',
  827. 'CONTENT_LENGTH' => '1337',
  828. 'HTTP_CONTENT_MD5' => 'abc123',
  829. 'HTTP_DOUBLE' => ['a', 'b'],
  830. ]]);
  831. $new = $request->withHeader('Content-Length', '999');
  832. $this->assertNotSame($new, $request);
  833. $this->assertSame('1337', $request->getHeaderLine('Content-length'), 'old request is unchanged');
  834. $this->assertSame('999', $new->getHeaderLine('Content-length'), 'new request is correct');
  835. $new = $request->withHeader('Double', ['a']);
  836. $this->assertEquals(['a'], $new->getHeader('Double'), 'List values are overwritten');
  837. }
  838. /**
  839. * Test adding a header.
  840. */
  841. public function testWithAddedHeader(): void
  842. {
  843. $request = new ServerRequest(['environment' => [
  844. 'HTTP_HOST' => 'localhost',
  845. 'CONTENT_TYPE' => 'application/json',
  846. 'CONTENT_LENGTH' => 1337,
  847. 'HTTP_CONTENT_MD5' => 'abc123',
  848. 'HTTP_DOUBLE' => ['a', 'b'],
  849. ]]);
  850. $new = $request->withAddedHeader('Double', 'c');
  851. $this->assertNotSame($new, $request);
  852. $this->assertSame('a, b', $request->getHeaderLine('Double'), 'old request is unchanged');
  853. $this->assertSame('a, b, c', $new->getHeaderLine('Double'), 'new request is correct');
  854. $new = $request->withAddedHeader('Content-Length', 777);
  855. $this->assertEquals([1337, 777], $new->getHeader('Content-Length'), 'scalar values are appended');
  856. $new = $request->withAddedHeader('Content-Length', [123, 456]);
  857. $this->assertEquals([1337, 123, 456], $new->getHeader('Content-Length'), 'List values are merged');
  858. }
  859. /**
  860. * Test removing a header.
  861. */
  862. public function testWithoutHeader(): void
  863. {
  864. $request = new ServerRequest(['environment' => [
  865. 'HTTP_HOST' => 'localhost',
  866. 'CONTENT_TYPE' => 'application/json',
  867. 'CONTENT_LENGTH' => 1337,
  868. 'HTTP_CONTENT_MD5' => 'abc123',
  869. 'HTTP_DOUBLE' => ['a', 'b'],
  870. ]]);
  871. $new = $request->withoutHeader('Content-Length');
  872. $this->assertNotSame($new, $request);
  873. $this->assertSame('1337', $request->getHeaderLine('Content-length'), 'old request is unchanged');
  874. $this->assertSame('', $new->getHeaderLine('Content-length'), 'new request is correct');
  875. }
  876. /**
  877. * Test accepts() with and without parameters
  878. */
  879. public function testAccepts(): void
  880. {
  881. $request = new ServerRequest(['environment' => [
  882. 'HTTP_ACCEPT' => 'text/xml,application/xml;q=0.9,application/xhtml+xml,text/html,text/plain,image/png',
  883. ]]);
  884. $result = $request->accepts();
  885. $expected = [
  886. 'text/xml', 'application/xhtml+xml', 'text/html', 'text/plain', 'image/png', 'application/xml',
  887. ];
  888. $this->assertEquals($expected, $result, 'Content types differ.');
  889. $result = $request->accepts('text/html');
  890. $this->assertTrue($result);
  891. $result = $request->accepts('image/gif');
  892. $this->assertFalse($result);
  893. }
  894. /**
  895. * Test that accept header types are trimmed for comparisons.
  896. */
  897. public function testAcceptWithWhitespace(): void
  898. {
  899. $request = new ServerRequest(['environment' => [
  900. 'HTTP_ACCEPT' => 'text/xml , text/html , text/plain,image/png',
  901. ]]);
  902. $result = $request->accepts();
  903. $expected = [
  904. 'text/xml', 'text/html', 'text/plain', 'image/png',
  905. ];
  906. $this->assertEquals($expected, $result, 'Content types differ.');
  907. $this->assertTrue($request->accepts('text/html'));
  908. }
  909. /**
  910. * Content types from accepts() should respect the client's q preference values.
  911. */
  912. public function testAcceptWithQvalueSorting(): void
  913. {
  914. $request = new ServerRequest(['environment' => [
  915. 'HTTP_ACCEPT' => 'text/html;q=0.8,application/json;q=0.7,application/xml;q=1.0',
  916. ]]);
  917. $result = $request->accepts();
  918. $expected = ['application/xml', 'text/html', 'application/json'];
  919. $this->assertEquals($expected, $result);
  920. }
  921. /**
  922. * Test the raw parsing of accept headers into the q value formatting.
  923. */
  924. public function testParseAcceptWithQValue(): void
  925. {
  926. $request = new ServerRequest(['environment' => [
  927. 'HTTP_ACCEPT' => 'text/html;q=0.8,application/json;q=0.7,application/xml;q=1.0,image/png',
  928. ]]);
  929. $result = $request->parseAccept();
  930. $expected = [
  931. '1.0' => ['application/xml', 'image/png'],
  932. '0.8' => ['text/html'],
  933. '0.7' => ['application/json'],
  934. ];
  935. $this->assertEquals($expected, $result);
  936. }
  937. /**
  938. * Test parsing accept with a confusing accept value.
  939. */
  940. public function testParseAcceptNoQValues(): void
  941. {
  942. $request = new ServerRequest(['environment' => [
  943. 'HTTP_ACCEPT' => 'application/json, text/plain, */*',
  944. ]]);
  945. $result = $request->parseAccept();
  946. $expected = [
  947. '1.0' => ['application/json', 'text/plain', '*/*'],
  948. ];
  949. $this->assertEquals($expected, $result);
  950. }
  951. /**
  952. * Test parsing accept ignores index param
  953. */
  954. public function testParseAcceptIgnoreAcceptExtensions(): void
  955. {
  956. $request = new ServerRequest(['environment' => [
  957. 'url' => '/',
  958. 'HTTP_ACCEPT' => 'application/json;level=1, text/plain, */*',
  959. ]]);
  960. $result = $request->parseAccept();
  961. $expected = [
  962. '1.0' => ['application/json', 'text/plain', '*/*'],
  963. ];
  964. $this->assertEquals($expected, $result);
  965. }
  966. /**
  967. * Test that parsing accept headers with invalid syntax works.
  968. *
  969. * The header used is missing a q value for application/xml.
  970. */
  971. public function testParseAcceptInvalidSyntax(): void
  972. {
  973. $request = new ServerRequest(['environment' => [
  974. 'url' => '/',
  975. 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;image/png,image/jpeg,image/*;q=0.9,*/*;q=0.8',
  976. ]]);
  977. $result = $request->parseAccept();
  978. $expected = [
  979. '1.0' => ['text/html', 'application/xhtml+xml', 'application/xml', 'image/jpeg'],
  980. '0.9' => ['image/*'],
  981. '0.8' => ['*/*'],
  982. ];
  983. $this->assertEquals($expected, $result);
  984. }
  985. /**
  986. * Test the getQuery() method
  987. */
  988. public function testGetQuery(): void
  989. {
  990. $array = [
  991. 'query' => [
  992. 'foo' => 'bar',
  993. 'zero' => '0',
  994. 'test' => [
  995. 'foo', 'bar',
  996. ],
  997. ],
  998. ];
  999. $request = new ServerRequest($array);
  1000. $this->assertEquals([
  1001. 'foo' => 'bar',
  1002. 'zero' => '0',
  1003. 'test' => [
  1004. 'foo', 'bar',
  1005. ],
  1006. ], $request->getQuery());
  1007. $this->assertSame('bar', $request->getQuery('foo'));
  1008. $this->assertSame('0', $request->getQuery('zero'));
  1009. $this->assertNull($request->getQuery('imaginary'));
  1010. $this->assertSame('default', $request->getQuery('imaginary', 'default'));
  1011. $this->assertFalse($request->getQuery('imaginary', false));
  1012. $this->assertSame(['foo', 'bar'], $request->getQuery('test'));
  1013. $this->assertSame('bar', $request->getQuery('test.1'));
  1014. $this->assertNull($request->getQuery('test.2'));
  1015. $this->assertSame('default', $request->getQuery('test.2', 'default'));
  1016. }
  1017. /**
  1018. * Test getQueryParams
  1019. */
  1020. public function testGetQueryParams(): void
  1021. {
  1022. $get = [
  1023. 'test' => ['foo', 'bar'],
  1024. 'key' => 'value',
  1025. ];
  1026. $request = new ServerRequest([
  1027. 'query' => $get,
  1028. ]);
  1029. $this->assertSame($get, $request->getQueryParams());
  1030. }
  1031. /**
  1032. * Test withQueryParams and immutability
  1033. */
  1034. public function testWithQueryParams(): void
  1035. {
  1036. $get = [
  1037. 'test' => ['foo', 'bar'],
  1038. 'key' => 'value',
  1039. ];
  1040. $request = new ServerRequest([
  1041. 'query' => $get,
  1042. ]);
  1043. $new = $request->withQueryParams(['new' => 'data']);
  1044. $this->assertSame($get, $request->getQueryParams());
  1045. $this->assertSame(['new' => 'data'], $new->getQueryParams());
  1046. }
  1047. /**
  1048. * Test using param()
  1049. */
  1050. public function testReadingParams(): void
  1051. {
  1052. $request = new ServerRequest([
  1053. 'params' => [
  1054. 'controller' => 'Posts',
  1055. 'admin' => true,
  1056. 'truthy' => 1,
  1057. 'zero' => '0',
  1058. ],
  1059. ]);
  1060. $this->assertNull($request->getParam('not_set'));
  1061. $this->assertTrue($request->getParam('admin'));
  1062. $this->assertSame(1, $request->getParam('truthy'));
  1063. $this->assertSame('Posts', $request->getParam('controller'));
  1064. $this->assertSame('0', $request->getParam('zero'));
  1065. }
  1066. /**
  1067. * Test the data() method reading
  1068. */
  1069. public function testGetData(): void
  1070. {
  1071. $post = [
  1072. 'Model' => [
  1073. 'field' => 'value',
  1074. ],
  1075. ];
  1076. $request = new ServerRequest(compact('post'));
  1077. $this->assertEquals($post['Model'], $request->getData('Model'));
  1078. $this->assertEquals($post, $request->getData());
  1079. $this->assertNull($request->getData('Model.imaginary'));
  1080. $this->assertSame('value', $request->getData('Model.field', 'default'));
  1081. $this->assertSame('default', $request->getData('Model.imaginary', 'default'));
  1082. }
  1083. /**
  1084. * Test that getData() doesn't fail on scalar data.
  1085. */
  1086. public function testGetDataOnStringData(): void
  1087. {
  1088. $post = 'strange, but could happen';
  1089. $request = new ServerRequest(compact('post'));
  1090. $this->assertNull($request->getData('Model'));
  1091. $this->assertNull($request->getData('Model.field'));
  1092. }
  1093. /**
  1094. * Test writing falsey values.
  1095. */
  1096. public function testDataWritingFalsey(): void
  1097. {
  1098. $request = new ServerRequest();
  1099. $request = $request->withData('Post.null', null);
  1100. $this->assertNull($request->getData('Post.null'));
  1101. $request = $request->withData('Post.false', false);
  1102. $this->assertFalse($request->getData('Post.false'));
  1103. $request = $request->withData('Post.zero', 0);
  1104. $this->assertSame(0, $request->getData('Post.zero'));
  1105. $request = $request->withData('Post.empty', '');
  1106. $this->assertSame('', $request->getData('Post.empty'));
  1107. }
  1108. /**
  1109. * Test reading params
  1110. *
  1111. * @dataProvider paramReadingDataProvider
  1112. * @param mixed $expected
  1113. */
  1114. public function testGetParam(string $toRead, $expected): void
  1115. {
  1116. $request = new ServerRequest([
  1117. 'url' => '/',
  1118. 'params' => [
  1119. 'action' => 'index',
  1120. 'foo' => 'bar',
  1121. 'baz' => [
  1122. 'a' => [
  1123. 'b' => 'c',
  1124. ],
  1125. ],
  1126. 'admin' => true,
  1127. 'truthy' => 1,
  1128. 'zero' => '0',
  1129. ],
  1130. ]);
  1131. $this->assertSame($expected, $request->getParam($toRead));
  1132. }
  1133. /**
  1134. * Test getParam returning a default value.
  1135. */
  1136. public function testGetParamDefault(): void
  1137. {
  1138. $request = new ServerRequest([
  1139. 'params' => [
  1140. 'controller' => 'Articles',
  1141. 'null' => null,
  1142. ],
  1143. ]);
  1144. $this->assertSame('Articles', $request->getParam('controller', 'default'));
  1145. $this->assertSame('default', $request->getParam('null', 'default'));
  1146. $this->assertFalse($request->getParam('unset', false));
  1147. $this->assertNull($request->getParam('unset'));
  1148. }
  1149. /**
  1150. * Data provider for testing reading values with ServerRequest::getParam()
  1151. *
  1152. * @return array
  1153. */
  1154. public function paramReadingDataProvider(): array
  1155. {
  1156. return [
  1157. [
  1158. 'action',
  1159. 'index',
  1160. ],
  1161. [
  1162. 'baz',
  1163. [
  1164. 'a' => [
  1165. 'b' => 'c',
  1166. ],
  1167. ],
  1168. ],
  1169. [
  1170. 'baz.a.b',
  1171. 'c',
  1172. ],
  1173. [
  1174. 'does_not_exist',
  1175. null,
  1176. ],
  1177. [
  1178. 'admin',
  1179. true,
  1180. ],
  1181. [
  1182. 'truthy',
  1183. 1,
  1184. ],
  1185. [
  1186. 'zero',
  1187. '0',
  1188. ],
  1189. ];
  1190. }
  1191. /**
  1192. * test writing request params with param()
  1193. */
  1194. public function testParamWriting(): void
  1195. {
  1196. $request = new ServerRequest(['url' => '/']);
  1197. $request = $request->withParam('action', 'index');
  1198. $this->assertInstanceOf(
  1199. 'Cake\Http\ServerRequest',
  1200. $request->withParam('some', 'thing'),
  1201. 'Method has not returned $this'
  1202. );
  1203. $request = $request->withParam('Post.null', null);
  1204. $this->assertNull($request->getParam('Post.null'), 'default value should be used.');
  1205. $request = $request->withParam('Post.false', false);
  1206. $this->assertFalse($request->getParam('Post.false'));
  1207. $request = $request->withParam('Post.zero', 0);
  1208. $this->assertSame(0, $request->getParam('Post.zero'));
  1209. $request = $request->withParam('Post.empty', '');
  1210. $this->assertSame('', $request->getParam('Post.empty'));
  1211. $this->assertSame('index', $request->getParam('action'));
  1212. $request = $request->withParam('action', 'edit');
  1213. $this->assertSame('edit', $request->getParam('action'));
  1214. }
  1215. /**
  1216. * Test accept language
  1217. */
  1218. public function testAcceptLanguage(): void
  1219. {
  1220. $request = new ServerRequest();
  1221. // Weird language
  1222. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'inexistent,en-ca');
  1223. $result = $request->acceptLanguage();
  1224. $this->assertEquals(['inexistent', 'en-ca'], $result, 'Languages do not match');
  1225. // No qualifier
  1226. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'es_mx,en_ca');
  1227. $result = $request->acceptLanguage();
  1228. $this->assertEquals(['es-mx', 'en-ca'], $result, 'Languages do not match');
  1229. // With qualifier
  1230. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'en-US,en;q=0.8,pt-BR;q=0.6,pt;q=0.4');
  1231. $result = $request->acceptLanguage();
  1232. $this->assertEquals(['en-us', 'en', 'pt-br', 'pt'], $result, 'Languages do not match');
  1233. // With spaces
  1234. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'da, en-gb;q=0.8, en;q=0.7');
  1235. $result = $request->acceptLanguage();
  1236. $this->assertEquals(['da', 'en-gb', 'en'], $result, 'Languages do not match');
  1237. // Checking if requested
  1238. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'es_mx,en_ca');
  1239. $result = $request->acceptLanguage('en-ca');
  1240. $this->assertTrue($result);
  1241. $result = $request->acceptLanguage('en-CA');
  1242. $this->assertTrue($result);
  1243. $result = $request->acceptLanguage('en-us');
  1244. $this->assertFalse($result);
  1245. $result = $request->acceptLanguage('en-US');
  1246. $this->assertFalse($result);
  1247. }
  1248. /**
  1249. * Test the input() method.
  1250. */
  1251. public function testInput(): void
  1252. {
  1253. $request = new ServerRequest([
  1254. 'input' => 'I came from stdin',
  1255. ]);
  1256. $this->deprecated(function () use ($request): void {
  1257. $result = $request->input();
  1258. $this->assertSame('I came from stdin', $result);
  1259. });
  1260. }
  1261. /**
  1262. * Test input() decoding.
  1263. *
  1264. * @group deprecated
  1265. */
  1266. public function testInputDecode(): void
  1267. {
  1268. $request = new ServerRequest([
  1269. 'input' => '{"name":"value"}',
  1270. ]);
  1271. $this->deprecated(function () use ($request): void {
  1272. $result = $request->input('json_decode');
  1273. $this->assertEquals(['name' => 'value'], (array)$result);
  1274. });
  1275. }
  1276. /**
  1277. * Test input() decoding with additional arguments.
  1278. *
  1279. * @group deprecated
  1280. */
  1281. public function testInputDecodeExtraParams(): void
  1282. {
  1283. $xml = <<<XML
  1284. <?xml version="1.0" encoding="utf-8"?>
  1285. <post>
  1286. <title id="title">Test</title>
  1287. </post>
  1288. XML;
  1289. $request = new ServerRequest([
  1290. 'input' => $xml,
  1291. ]);
  1292. $this->deprecated(function () use ($request): void {
  1293. $result = $request->input('Cake\Utility\Xml::build', ['return' => 'domdocument']);
  1294. $this->assertInstanceOf('DOMDocument', $result);
  1295. $this->assertSame(
  1296. 'Test',
  1297. $result->getElementsByTagName('title')->item(0)->childNodes->item(0)->wholeText
  1298. );
  1299. });
  1300. }
  1301. /**
  1302. * Test getBody
  1303. */
  1304. public function testGetBody(): void
  1305. {
  1306. $request = new ServerRequest([
  1307. 'input' => 'key=val&some=data',
  1308. ]);
  1309. $result = $request->getBody();
  1310. $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $result);
  1311. $this->assertSame('key=val&some=data', $result->getContents());
  1312. }
  1313. /**
  1314. * Test withBody
  1315. */
  1316. public function testWithBody(): void
  1317. {
  1318. $request = new ServerRequest([
  1319. 'input' => 'key=val&some=data',
  1320. ]);
  1321. $body = $this->getMockBuilder('Psr\Http\Message\StreamInterface')->getMock();
  1322. $new = $request->withBody($body);
  1323. $this->assertNotSame($new, $request);
  1324. $this->assertNotSame($body, $request->getBody());
  1325. $this->assertSame($body, $new->getBody());
  1326. }
  1327. /**
  1328. * Test getUri
  1329. */
  1330. public function testGetUri(): void
  1331. {
  1332. $request = new ServerRequest(['url' => 'articles/view/3']);
  1333. $result = $request->getUri();
  1334. $this->assertInstanceOf('Psr\Http\Message\UriInterface', $result);
  1335. $this->assertSame('/articles/view/3', $result->getPath());
  1336. }
  1337. /**
  1338. * Test withUri
  1339. */
  1340. public function testWithUri(): void
  1341. {
  1342. $request = new ServerRequest([
  1343. 'environment' => [
  1344. 'HTTP_HOST' => 'example.com',
  1345. ],
  1346. 'url' => 'articles/view/3',
  1347. ]);
  1348. $uri = $this->getMockBuilder('Psr\Http\Message\UriInterface')->getMock();
  1349. $new = $request->withUri($uri);
  1350. $this->assertNotSame($new, $request);
  1351. $this->assertNotSame($uri, $request->getUri());
  1352. $this->assertSame($uri, $new->getUri());
  1353. }
  1354. /**
  1355. * Test withUri() and preserveHost
  1356. */
  1357. public function testWithUriPreserveHost(): void
  1358. {
  1359. $request = new ServerRequest([
  1360. 'environment' => [
  1361. 'HTTP_HOST' => 'localhost',
  1362. ],
  1363. 'url' => 'articles/view/3',
  1364. ]);
  1365. $uri = new Uri();
  1366. $uri = $uri->withHost('example.com')
  1367. ->withPort(123)
  1368. ->withPath('articles/view/3');
  1369. $new = $request->withUri($uri, false);
  1370. $this->assertNotSame($new, $request);
  1371. $this->assertSame('example.com:123', $new->getHeaderLine('Host'));
  1372. }
  1373. /**
  1374. * Test withUri() and preserveHost missing the host header
  1375. */
  1376. public function testWithUriPreserveHostNoHostHeader(): void
  1377. {
  1378. $request = new ServerRequest([
  1379. 'url' => 'articles/view/3',
  1380. ]);
  1381. $uri = new Uri();
  1382. $uri = $uri->withHost('example.com')
  1383. ->withPort(123)
  1384. ->withPath('articles/view/3');
  1385. $new = $request->withUri($uri, false);
  1386. $this->assertSame('example.com:123', $new->getHeaderLine('Host'));
  1387. }
  1388. /**
  1389. * Test the cookie() method.
  1390. */
  1391. public function testGetCookie(): void
  1392. {
  1393. $request = new ServerRequest([
  1394. 'cookies' => [
  1395. 'testing' => 'A value in the cookie',
  1396. 'user' => [
  1397. 'remember' => '1',
  1398. ],
  1399. ],
  1400. ]);
  1401. $this->assertSame('A value in the cookie', $request->getCookie('testing'));
  1402. $this->assertNull($request->getCookie('not there'));
  1403. $this->assertSame('default', $request->getCookie('not there', 'default'));
  1404. $this->assertSame('1', $request->getCookie('user.remember'));
  1405. $this->assertSame('1', $request->getCookie('user.remember', 'default'));
  1406. $this->assertSame('default', $request->getCookie('user.not there', 'default'));
  1407. }
  1408. /**
  1409. * Test getCookieParams()
  1410. */
  1411. public function testGetCookieParams(): void
  1412. {
  1413. $cookies = [
  1414. 'testing' => 'A value in the cookie',
  1415. ];
  1416. $request = new ServerRequest(['cookies' => $cookies]);
  1417. $this->assertSame($cookies, $request->getCookieParams());
  1418. }
  1419. /**
  1420. * Test withCookieParams()
  1421. */
  1422. public function testWithCookieParams(): void
  1423. {
  1424. $cookies = [
  1425. 'testing' => 'A value in the cookie',
  1426. ];
  1427. $request = new ServerRequest(['cookies' => $cookies]);
  1428. $new = $request->withCookieParams(['remember_me' => 1]);
  1429. $this->assertNotSame($new, $request);
  1430. $this->assertSame($cookies, $request->getCookieParams());
  1431. $this->assertSame(['remember_me' => 1], $new->getCookieParams());
  1432. }
  1433. /**
  1434. * Test getting a cookie collection from a request.
  1435. */
  1436. public function testGetCookieCollection(): void
  1437. {
  1438. $cookies = [
  1439. 'remember_me' => '1',
  1440. 'color' => 'blue',
  1441. ];
  1442. $request = new ServerRequest(['cookies' => $cookies]);
  1443. $cookies = $request->getCookieCollection();
  1444. $this->assertInstanceOf(CookieCollection::class, $cookies);
  1445. $this->assertCount(2, $cookies);
  1446. $this->assertSame('1', $cookies->get('remember_me')->getValue());
  1447. $this->assertSame('blue', $cookies->get('color')->getValue());
  1448. }
  1449. /**
  1450. * Test replacing cookies from a collection
  1451. */
  1452. public function testWithCookieCollection(): void
  1453. {
  1454. $cookies = new CookieCollection([new Cookie('remember_me', 1), new Cookie('color', 'red')]);
  1455. $request = new ServerRequest(['cookies' => ['bad' => 'goaway']]);
  1456. $new = $request->withCookieCollection($cookies);
  1457. $this->assertNotSame($new, $request, 'Should clone');
  1458. $this->assertSame(['bad' => 'goaway'], $request->getCookieParams());
  1459. $this->assertSame(['remember_me' => 1, 'color' => 'red'], $new->getCookieParams());
  1460. $cookies = $new->getCookieCollection();
  1461. $this->assertCount(2, $cookies);
  1462. $this->assertSame('red', $cookies->get('color')->getValue());
  1463. }
  1464. /**
  1465. * TestAllowMethod
  1466. */
  1467. public function testAllowMethod(): void
  1468. {
  1469. $request = new ServerRequest(['environment' => [
  1470. 'url' => '/posts/edit/1',
  1471. 'REQUEST_METHOD' => 'PUT',
  1472. ]]);
  1473. $this->assertTrue($request->allowMethod('put'));
  1474. $request = $request->withEnv('REQUEST_METHOD', 'DELETE');
  1475. $this->assertTrue($request->allowMethod(['post', 'delete']));
  1476. }
  1477. /**
  1478. * Test allowMethod throwing exception
  1479. */
  1480. public function testAllowMethodException(): void
  1481. {
  1482. $request = new ServerRequest([
  1483. 'url' => '/posts/edit/1',
  1484. 'environment' => ['REQUEST_METHOD' => 'PUT'],
  1485. ]);
  1486. try {
  1487. $request->allowMethod(['POST', 'DELETE']);
  1488. $this->fail('An expected exception has not been raised.');
  1489. } catch (MethodNotAllowedException $e) {
  1490. $this->assertEquals(['Allow' => 'POST, DELETE'], $e->getHeaders());
  1491. }
  1492. $this->expectException(MethodNotAllowedException::class);
  1493. $request->allowMethod('POST');
  1494. }
  1495. /**
  1496. * Tests getting the session from the request
  1497. */
  1498. public function testGetSession(): void
  1499. {
  1500. $session = new Session();
  1501. $request = new ServerRequest(['session' => $session]);
  1502. $this->assertSame($session, $request->getSession());
  1503. $request = new ServerRequest();
  1504. $this->assertEquals($session, $request->getSession());
  1505. }
  1506. public function testGetFlash(): void
  1507. {
  1508. $request = new ServerRequest();
  1509. $this->assertInstanceOf(FlashMessage::class, $request->getFlash());
  1510. }
  1511. /**
  1512. * Test the content type method.
  1513. */
  1514. public function testContentType(): void
  1515. {
  1516. $request = new ServerRequest([
  1517. 'environment' => ['HTTP_CONTENT_TYPE' => 'application/json'],
  1518. ]);
  1519. $this->assertSame('application/json', $request->contentType());
  1520. $request = new ServerRequest([
  1521. 'environment' => ['HTTP_CONTENT_TYPE' => 'application/xml'],
  1522. ]);
  1523. $this->assertSame('application/xml', $request->contentType(), 'prefer non http header.');
  1524. }
  1525. /**
  1526. * Test updating params in a psr7 fashion.
  1527. */
  1528. public function testWithParam(): void
  1529. {
  1530. $request = new ServerRequest([
  1531. 'params' => ['controller' => 'Articles'],
  1532. ]);
  1533. $result = $request->withParam('action', 'view');
  1534. $this->assertNotSame($result, $request, 'New instance should be made');
  1535. $this->assertNull($request->getParam('action'), 'No side-effect on original');
  1536. $this->assertSame('view', $result->getParam('action'));
  1537. $result = $request->withParam('action', 'index')
  1538. ->withParam('plugin', 'DebugKit')
  1539. ->withParam('prefix', 'Admin');
  1540. $this->assertNotSame($result, $request, 'New instance should be made');
  1541. $this->assertNull($request->getParam('action'), 'No side-effect on original');
  1542. $this->assertSame('index', $result->getParam('action'));
  1543. $this->assertSame('DebugKit', $result->getParam('plugin'));
  1544. $this->assertSame('Admin', $result->getParam('prefix'));
  1545. }
  1546. /**
  1547. * Test getting the parsed body parameters.
  1548. */
  1549. public function testGetParsedBody(): void
  1550. {
  1551. $data = ['title' => 'First', 'body' => 'Best Article!'];
  1552. $request = new ServerRequest(['post' => $data]);
  1553. $this->assertSame($data, $request->getParsedBody());
  1554. $request = new ServerRequest();
  1555. $this->assertSame([], $request->getParsedBody());
  1556. }
  1557. /**
  1558. * Test replacing the parsed body parameters.
  1559. */
  1560. public function testWithParsedBody(): void
  1561. {
  1562. $data = ['title' => 'First', 'body' => 'Best Article!'];
  1563. $request = new ServerRequest([]);
  1564. $new = $request->withParsedBody($data);
  1565. $this->assertNotSame($request, $new);
  1566. $this->assertSame([], $request->getParsedBody());
  1567. $this->assertSame($data, $new->getParsedBody());
  1568. }
  1569. /**
  1570. * Test updating POST data in a psr7 fashion.
  1571. */
  1572. public function testWithData(): void
  1573. {
  1574. $request = new ServerRequest([
  1575. 'post' => [
  1576. 'Model' => [
  1577. 'field' => 'value',
  1578. ],
  1579. ],
  1580. ]);
  1581. $result = $request->withData('Model.new_value', 'new value');
  1582. $this->assertNull($request->getData('Model.new_value'), 'Original request should not change.');
  1583. $this->assertNotSame($result, $request);
  1584. $this->assertSame('new value', $result->getData('Model.new_value'));
  1585. $this->assertSame('new value', $result->getData()['Model']['new_value']);
  1586. $this->assertSame('value', $result->getData('Model.field'));
  1587. }
  1588. /**
  1589. * Test removing data from a request
  1590. */
  1591. public function testWithoutData(): void
  1592. {
  1593. $request = new ServerRequest([
  1594. 'post' => [
  1595. 'Model' => [
  1596. 'id' => 1,
  1597. 'field' => 'value',
  1598. ],
  1599. ],
  1600. ]);
  1601. $updated = $request->withoutData('Model.field');
  1602. $this->assertNotSame($updated, $request);
  1603. $this->assertSame('value', $request->getData('Model.field'), 'Original request should not change.');
  1604. $this->assertNull($updated->getData('Model.field'), 'data removed from updated request');
  1605. $this->assertFalse(isset($updated->getData()['Model']['field']), 'data removed from updated request');
  1606. }
  1607. /**
  1608. * Test updating POST data when keys don't exist
  1609. */
  1610. public function testWithDataMissingIntermediaryKeys(): void
  1611. {
  1612. $request = new ServerRequest([
  1613. 'post' => [
  1614. 'Model' => [
  1615. 'field' => 'value',
  1616. ],
  1617. ],
  1618. ]);
  1619. $result = $request->withData('Model.field.new_value', 'new value');
  1620. $this->assertSame(
  1621. 'new value',
  1622. $result->getData('Model.field.new_value')
  1623. );
  1624. $this->assertSame(
  1625. 'new value',
  1626. $result->getData()['Model']['field']['new_value']
  1627. );
  1628. }
  1629. /**
  1630. * Test updating POST data with falsey values
  1631. */
  1632. public function testWithDataFalseyValues(): void
  1633. {
  1634. $request = new ServerRequest([
  1635. 'post' => [],
  1636. ]);
  1637. $result = $request->withData('false', false)
  1638. ->withData('null', null)
  1639. ->withData('empty_string', '')
  1640. ->withData('zero', 0)
  1641. ->withData('zero_string', '0');
  1642. $expected = [
  1643. 'false' => false,
  1644. 'null' => null,
  1645. 'empty_string' => '',
  1646. 'zero' => 0,
  1647. 'zero_string' => '0',
  1648. ];
  1649. $this->assertSame($expected, $result->getData());
  1650. }
  1651. /**
  1652. * Test setting attributes.
  1653. */
  1654. public function testWithAttribute(): void
  1655. {
  1656. $request = new ServerRequest([]);
  1657. $this->assertNull($request->getAttribute('key'));
  1658. $this->assertSame('default', $request->getAttribute('key', 'default'));
  1659. $new = $request->withAttribute('key', 'value');
  1660. $this->assertNotEquals($new, $request, 'Should be different');
  1661. $this->assertNull($request->getAttribute('key'), 'Old instance not modified');
  1662. $this->assertSame('value', $new->getAttribute('key'));
  1663. $update = $new->withAttribute('key', ['complex']);
  1664. $this->assertNotEquals($update, $new, 'Should be different');
  1665. $this->assertSame(['complex'], $update->getAttribute('key'));
  1666. }
  1667. /**
  1668. * Test that replacing the session can be done via withAttribute()
  1669. */
  1670. public function testWithAttributeSession(): void
  1671. {
  1672. $request = new ServerRequest([]);
  1673. $request->getSession()->write('attrKey', 'session-value');
  1674. $update = $request->withAttribute('session', Session::create());
  1675. $this->assertSame('session-value', $request->getAttribute('session')->read('attrKey'));
  1676. $this->assertNotSame($request->getAttribute('session'), $update->getAttribute('session'));
  1677. $this->assertNotSame($request->getSession()->read('attrKey'), $update->getSession()->read('attrKey'));
  1678. }
  1679. /**
  1680. * Test getting all attributes.
  1681. */
  1682. public function testGetAttributes(): void
  1683. {
  1684. $request = new ServerRequest([]);
  1685. $new = $request->withAttribute('key', 'value')
  1686. ->withAttribute('nully', null)
  1687. ->withAttribute('falsey', false);
  1688. $this->assertFalse($new->getAttribute('falsey'));
  1689. $this->assertNull($new->getAttribute('nully'));
  1690. $expected = [
  1691. 'key' => 'value',
  1692. 'nully' => null,
  1693. 'falsey' => false,
  1694. 'params' => [
  1695. 'plugin' => null,
  1696. 'controller' => null,
  1697. 'action' => null,
  1698. '_ext' => null,
  1699. 'pass' => [],
  1700. ],
  1701. 'webroot' => '',
  1702. 'base' => '',
  1703. 'here' => '/',
  1704. ];
  1705. $this->assertEquals($expected, $new->getAttributes());
  1706. }
  1707. /**
  1708. * Test unsetting attributes.
  1709. */
  1710. public function testWithoutAttribute(): void
  1711. {
  1712. $request = new ServerRequest([]);
  1713. $new = $request->withAttribute('key', 'value');
  1714. $update = $request->withoutAttribute('key');
  1715. $this->assertNotEquals($update, $new, 'Should be different');
  1716. $this->assertNull($update->getAttribute('key'));
  1717. }
  1718. /**
  1719. * Test that withoutAttribute() cannot remove emulatedAttributes properties.
  1720. *
  1721. * @dataProvider emulatedPropertyProvider
  1722. */
  1723. public function testWithoutAttributesDenyEmulatedProperties(string $prop): void
  1724. {
  1725. $this->expectException(InvalidArgumentException::class);
  1726. $request = new ServerRequest([]);
  1727. $request->withoutAttribute($prop);
  1728. }
  1729. /**
  1730. * Test the requestTarget methods.
  1731. */
  1732. public function testWithRequestTarget(): void
  1733. {
  1734. $request = new ServerRequest([
  1735. 'environment' => [
  1736. 'REQUEST_URI' => '/articles/view/1',
  1737. 'QUERY_STRING' => 'comments=1&open=0',
  1738. ],
  1739. 'base' => '/basedir',
  1740. ]);
  1741. $this->assertSame(
  1742. '/articles/view/1?comments=1&open=0',
  1743. $request->getRequestTarget(),
  1744. 'Should not include basedir.'
  1745. );
  1746. $new = $request->withRequestTarget('/articles/view/3');
  1747. $this->assertNotSame($new, $request);
  1748. $this->assertSame(
  1749. '/articles/view/1?comments=1&open=0',
  1750. $request->getRequestTarget(),
  1751. 'should be unchanged.'
  1752. );
  1753. $this->assertSame('/articles/view/3', $new->getRequestTarget(), 'reflects method call');
  1754. }
  1755. /**
  1756. * Test withEnv()
  1757. */
  1758. public function testWithEnv(): void
  1759. {
  1760. $request = new ServerRequest();
  1761. $newRequest = $request->withEnv('HTTP_HOST', 'cakephp.org');
  1762. $this->assertNotSame($request, $newRequest);
  1763. $this->assertSame('cakephp.org', $newRequest->getEnv('HTTP_HOST'));
  1764. }
  1765. /**
  1766. * Test getEnv()
  1767. */
  1768. public function testGetEnv(): void
  1769. {
  1770. $request = new ServerRequest([
  1771. 'environment' => ['TEST' => 'ing'],
  1772. ]);
  1773. //Test default null
  1774. $this->assertNull($request->getEnv('Foo'));
  1775. //Test default set
  1776. $this->assertSame('Bar', $request->getEnv('Foo', 'Bar'));
  1777. //Test env() fallback
  1778. $this->assertSame('ing', $request->getEnv('test'));
  1779. }
  1780. /**
  1781. * Data provider for emulated property tests.
  1782. *
  1783. * @return array
  1784. */
  1785. public function emulatedPropertyProvider(): array
  1786. {
  1787. return [
  1788. ['here'],
  1789. ['params'],
  1790. ['base'],
  1791. ['webroot'],
  1792. ['session'],
  1793. ];
  1794. }
  1795. }