ServerRequestTest.php 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077
  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('https'));
  637. $this->deprecated(function () use ($request) {
  638. $this->assertTrue($request->is('ssl'));
  639. });
  640. $request = $request->withEnv('HTTPS', '1');
  641. $this->assertTrue($request->is('https'));
  642. $this->deprecated(function () use ($request) {
  643. $this->assertTrue($request->is('ssl'));
  644. });
  645. $request = $request->withEnv('HTTPS', 'I am not empty');
  646. $this->assertFalse($request->is('https'));
  647. $this->deprecated(function () use ($request) {
  648. $this->assertFalse($request->is('ssl'));
  649. });
  650. $request = $request->withEnv('HTTPS', 'off');
  651. $this->assertFalse($request->is('https'));
  652. $this->deprecated(function () use ($request) {
  653. $this->assertFalse($request->is('ssl'));
  654. });
  655. $request = $request->withEnv('HTTPS', '');
  656. $this->assertFalse($request->is('https'));
  657. $this->deprecated(function () use ($request) {
  658. $this->assertFalse($request->is('ssl'));
  659. });
  660. }
  661. /**
  662. * Test adding detectors and having them work.
  663. */
  664. public function testAddDetector(): void
  665. {
  666. $request = new ServerRequest();
  667. ServerRequest::addDetector('closure', function ($request) {
  668. return true;
  669. });
  670. $this->assertTrue($request->is('closure'));
  671. ServerRequest::addDetector('get', function ($request) {
  672. return $request->getEnv('REQUEST_METHOD') === 'GET';
  673. });
  674. $request = $request->withEnv('REQUEST_METHOD', 'GET');
  675. $this->assertTrue($request->is('get'));
  676. ServerRequest::addDetector('compare', ['env' => 'TEST_VAR', 'value' => 'something']);
  677. $request = $request->withEnv('TEST_VAR', 'something');
  678. $this->assertTrue($request->is('compare'), 'Value match failed.');
  679. $request = $request->withEnv('TEST_VAR', 'wrong');
  680. $this->assertFalse($request->is('compare'), 'Value mis-match failed.');
  681. ServerRequest::addDetector('compareCamelCase', ['env' => 'TEST_VAR', 'value' => 'foo']);
  682. $request = $request->withEnv('TEST_VAR', 'foo');
  683. $this->assertTrue($request->is('compareCamelCase'), 'Value match failed.');
  684. $this->assertTrue($request->is('comparecamelcase'), 'detectors should be case insensitive');
  685. $this->assertTrue($request->is('COMPARECAMELCASE'), 'detectors should be case insensitive');
  686. $request = $request->withEnv('TEST_VAR', 'not foo');
  687. $this->assertFalse($request->is('compareCamelCase'), 'Value match failed.');
  688. $this->assertFalse($request->is('comparecamelcase'), 'detectors should be case insensitive');
  689. $this->assertFalse($request->is('COMPARECAMELCASE'), 'detectors should be case insensitive');
  690. ServerRequest::addDetector('banana', ['env' => 'TEST_VAR', 'pattern' => '/^ban.*$/']);
  691. $request = $request->withEnv('TEST_VAR', 'banana');
  692. $this->assertTrue($request->isBanana());
  693. $request = $request->withEnv('TEST_VAR', 'wrong value');
  694. $this->assertFalse($request->isBanana());
  695. ServerRequest::addDetector('mobile', ['env' => 'HTTP_USER_AGENT', 'options' => ['Imagination']]);
  696. $request = $request->withEnv('HTTP_USER_AGENT', 'Imagination land');
  697. $this->assertTrue($request->isMobile());
  698. ServerRequest::addDetector('index', ['param' => 'action', 'value' => 'index']);
  699. $request = $request->withParam('action', 'index');
  700. $request->clearDetectorCache();
  701. $this->assertTrue($request->isIndex());
  702. $request = $request->withParam('action', 'add');
  703. $request->clearDetectorCache();
  704. $this->assertFalse($request->isIndex());
  705. ServerRequest::addDetector('withParams', function ($request, array $params) {
  706. foreach ($params as $name => $value) {
  707. if ($request->getParam($name) != $value) {
  708. return false;
  709. }
  710. }
  711. return true;
  712. });
  713. $request = $request->withParam('controller', 'Pages')->withParam('action', 'index');
  714. $request->clearDetectorCache();
  715. $this->assertTrue($request->isWithParams(['controller' => 'Pages', 'action' => 'index']));
  716. $request = $request->withParam('controller', 'Posts');
  717. $request->clearDetectorCache();
  718. $this->assertFalse($request->isWithParams(['controller' => 'Pages', 'action' => 'index']));
  719. ServerRequest::addDetector('callme', function ($request) {
  720. return $request->getAttribute('return');
  721. });
  722. $request = $request->withAttribute('return', true);
  723. $request->clearDetectorCache();
  724. $this->assertTrue($request->isCallMe());
  725. ServerRequest::addDetector('extension', ['param' => '_ext', 'options' => ['pdf', 'png', 'txt']]);
  726. $request = $request->withParam('_ext', 'pdf');
  727. $request->clearDetectorCache();
  728. $this->assertTrue($request->is('extension'));
  729. $request = $request->withParam('_ext', 'exe');
  730. $request->clearDetectorCache();
  731. $this->assertFalse($request->isExtension());
  732. }
  733. /**
  734. * Test getting headers
  735. */
  736. public function testHeader(): void
  737. {
  738. $request = new ServerRequest(['environment' => [
  739. 'HTTP_HOST' => 'localhost',
  740. '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',
  741. 'CONTENT_TYPE' => 'application/json',
  742. 'CONTENT_LENGTH' => '1337',
  743. 'HTTP_CONTENT_MD5' => 'abc123',
  744. ]]);
  745. $this->assertEquals($request->getEnv('HTTP_HOST'), $request->getHeaderLine('host'));
  746. $this->assertEquals($request->getEnv('HTTP_USER_AGENT'), $request->getHeaderLine('User-Agent'));
  747. $this->assertEquals($request->getEnv('CONTENT_LENGTH'), $request->getHeaderLine('content-length'));
  748. $this->assertEquals($request->getEnv('CONTENT_TYPE'), $request->getHeaderLine('content-type'));
  749. $this->assertEquals($request->getEnv('HTTP_CONTENT_MD5'), $request->getHeaderLine('content-md5'));
  750. }
  751. /**
  752. * Test getting headers with psr7 methods
  753. */
  754. public function testGetHeaders(): void
  755. {
  756. $request = new ServerRequest(['environment' => [
  757. 'HTTP_HOST' => 'localhost',
  758. 'CONTENT_TYPE' => 'application/json',
  759. 'CONTENT_LENGTH' => 1337,
  760. 'HTTP_CONTENT_MD5' => 'abc123',
  761. 'HTTP_DOUBLE' => ['a', 'b'],
  762. ]]);
  763. $headers = $request->getHeaders();
  764. $expected = [
  765. 'Host' => ['localhost'],
  766. 'Content-Type' => ['application/json'],
  767. 'Content-Length' => [1337],
  768. 'Content-Md5' => ['abc123'],
  769. 'Double' => ['a', 'b'],
  770. ];
  771. $this->assertEquals($expected, $headers);
  772. }
  773. /**
  774. * Test hasHeader
  775. */
  776. public function testHasHeader(): void
  777. {
  778. $request = new ServerRequest(['environment' => [
  779. 'HTTP_HOST' => 'localhost',
  780. 'CONTENT_TYPE' => 'application/json',
  781. 'CONTENT_LENGTH' => 1337,
  782. 'HTTP_CONTENT_MD5' => 'abc123',
  783. 'HTTP_DOUBLE' => ['a', 'b'],
  784. ]]);
  785. $this->assertTrue($request->hasHeader('Host'));
  786. $this->assertTrue($request->hasHeader('Content-Type'));
  787. $this->assertTrue($request->hasHeader('Content-MD5'));
  788. $this->assertTrue($request->hasHeader('Double'));
  789. $this->assertFalse($request->hasHeader('Authorization'));
  790. }
  791. /**
  792. * Test getting headers with psr7 methods
  793. */
  794. public function testGetHeader(): void
  795. {
  796. $request = new ServerRequest(['environment' => [
  797. 'HTTP_HOST' => 'localhost',
  798. 'CONTENT_TYPE' => 'application/json',
  799. 'CONTENT_LENGTH' => 1337,
  800. 'HTTP_CONTENT_MD5' => 'abc123',
  801. 'HTTP_DOUBLE' => ['a', 'b'],
  802. ]]);
  803. $this->assertEquals([], $request->getHeader('Not-there'));
  804. $expected = [$request->getEnv('HTTP_HOST')];
  805. $this->assertEquals($expected, $request->getHeader('Host'));
  806. $this->assertEquals($expected, $request->getHeader('host'));
  807. $this->assertEquals($expected, $request->getHeader('HOST'));
  808. $this->assertEquals(['a', 'b'], $request->getHeader('Double'));
  809. }
  810. /**
  811. * Test getting headers with psr7 methods
  812. */
  813. public function testGetHeaderLine(): void
  814. {
  815. $request = new ServerRequest(['environment' => [
  816. 'HTTP_HOST' => 'localhost',
  817. 'CONTENT_TYPE' => 'application/json',
  818. 'CONTENT_LENGTH' => '1337',
  819. 'HTTP_CONTENT_MD5' => 'abc123',
  820. 'HTTP_DOUBLE' => ['a', 'b'],
  821. ]]);
  822. $this->assertSame('', $request->getHeaderLine('Authorization'));
  823. $expected = $request->getEnv('CONTENT_LENGTH');
  824. $this->assertEquals($expected, $request->getHeaderLine('Content-Length'));
  825. $this->assertEquals($expected, $request->getHeaderLine('content-Length'));
  826. $this->assertEquals($expected, $request->getHeaderLine('ConTent-LenGth'));
  827. $this->assertSame('a, b', $request->getHeaderLine('Double'));
  828. }
  829. /**
  830. * Test setting a header.
  831. */
  832. public function testWithHeader(): void
  833. {
  834. $request = new ServerRequest(['environment' => [
  835. 'HTTP_HOST' => 'localhost',
  836. 'CONTENT_TYPE' => 'application/json',
  837. 'CONTENT_LENGTH' => '1337',
  838. 'HTTP_CONTENT_MD5' => 'abc123',
  839. 'HTTP_DOUBLE' => ['a', 'b'],
  840. ]]);
  841. $new = $request->withHeader('Content-Length', '999');
  842. $this->assertNotSame($new, $request);
  843. $this->assertSame('1337', $request->getHeaderLine('Content-length'), 'old request is unchanged');
  844. $this->assertSame('999', $new->getHeaderLine('Content-length'), 'new request is correct');
  845. $new = $request->withHeader('Double', ['a']);
  846. $this->assertEquals(['a'], $new->getHeader('Double'), 'List values are overwritten');
  847. }
  848. /**
  849. * Test adding a header.
  850. */
  851. public function testWithAddedHeader(): void
  852. {
  853. $request = new ServerRequest(['environment' => [
  854. 'HTTP_HOST' => 'localhost',
  855. 'CONTENT_TYPE' => 'application/json',
  856. 'CONTENT_LENGTH' => 1337,
  857. 'HTTP_CONTENT_MD5' => 'abc123',
  858. 'HTTP_DOUBLE' => ['a', 'b'],
  859. ]]);
  860. $new = $request->withAddedHeader('Double', 'c');
  861. $this->assertNotSame($new, $request);
  862. $this->assertSame('a, b', $request->getHeaderLine('Double'), 'old request is unchanged');
  863. $this->assertSame('a, b, c', $new->getHeaderLine('Double'), 'new request is correct');
  864. $new = $request->withAddedHeader('Content-Length', 777);
  865. $this->assertEquals([1337, 777], $new->getHeader('Content-Length'), 'scalar values are appended');
  866. $new = $request->withAddedHeader('Content-Length', [123, 456]);
  867. $this->assertEquals([1337, 123, 456], $new->getHeader('Content-Length'), 'List values are merged');
  868. }
  869. /**
  870. * Test removing a header.
  871. */
  872. public function testWithoutHeader(): void
  873. {
  874. $request = new ServerRequest(['environment' => [
  875. 'HTTP_HOST' => 'localhost',
  876. 'CONTENT_TYPE' => 'application/json',
  877. 'CONTENT_LENGTH' => 1337,
  878. 'HTTP_CONTENT_MD5' => 'abc123',
  879. 'HTTP_DOUBLE' => ['a', 'b'],
  880. ]]);
  881. $new = $request->withoutHeader('Content-Length');
  882. $this->assertNotSame($new, $request);
  883. $this->assertSame('1337', $request->getHeaderLine('Content-length'), 'old request is unchanged');
  884. $this->assertSame('', $new->getHeaderLine('Content-length'), 'new request is correct');
  885. }
  886. /**
  887. * Test accepts() with and without parameters
  888. */
  889. public function testAccepts(): void
  890. {
  891. $request = new ServerRequest(['environment' => [
  892. 'HTTP_ACCEPT' => 'text/xml,application/xml;q=0.9,application/xhtml+xml,text/html,text/plain,image/png',
  893. ]]);
  894. $result = $request->accepts();
  895. $expected = [
  896. 'text/xml', 'application/xhtml+xml', 'text/html', 'text/plain', 'image/png', 'application/xml',
  897. ];
  898. $this->assertEquals($expected, $result, 'Content types differ.');
  899. $result = $request->accepts('text/html');
  900. $this->assertTrue($result);
  901. $result = $request->accepts('image/gif');
  902. $this->assertFalse($result);
  903. }
  904. /**
  905. * Test that accept header types are trimmed for comparisons.
  906. */
  907. public function testAcceptWithWhitespace(): void
  908. {
  909. $request = new ServerRequest(['environment' => [
  910. 'HTTP_ACCEPT' => 'text/xml , text/html , text/plain,image/png',
  911. ]]);
  912. $result = $request->accepts();
  913. $expected = [
  914. 'text/xml', 'text/html', 'text/plain', 'image/png',
  915. ];
  916. $this->assertEquals($expected, $result, 'Content types differ.');
  917. $this->assertTrue($request->accepts('text/html'));
  918. }
  919. /**
  920. * Content types from accepts() should respect the client's q preference values.
  921. */
  922. public function testAcceptWithQvalueSorting(): void
  923. {
  924. $request = new ServerRequest(['environment' => [
  925. 'HTTP_ACCEPT' => 'text/html;q=0.8,application/json;q=0.7,application/xml;q=1.0',
  926. ]]);
  927. $result = $request->accepts();
  928. $expected = ['application/xml', 'text/html', 'application/json'];
  929. $this->assertEquals($expected, $result);
  930. }
  931. /**
  932. * Test the raw parsing of accept headers into the q value formatting.
  933. */
  934. public function testParseAcceptWithQValue(): void
  935. {
  936. $request = new ServerRequest(['environment' => [
  937. 'HTTP_ACCEPT' => 'text/html;q=0.8,application/json;q=0.7,application/xml;q=1.0,image/png',
  938. ]]);
  939. $result = $request->parseAccept();
  940. $expected = [
  941. '1.0' => ['application/xml', 'image/png'],
  942. '0.8' => ['text/html'],
  943. '0.7' => ['application/json'],
  944. ];
  945. $this->assertEquals($expected, $result);
  946. }
  947. /**
  948. * Test parsing accept with a confusing accept value.
  949. */
  950. public function testParseAcceptNoQValues(): void
  951. {
  952. $request = new ServerRequest(['environment' => [
  953. 'HTTP_ACCEPT' => 'application/json, text/plain, */*',
  954. ]]);
  955. $result = $request->parseAccept();
  956. $expected = [
  957. '1.0' => ['application/json', 'text/plain', '*/*'],
  958. ];
  959. $this->assertEquals($expected, $result);
  960. }
  961. /**
  962. * Test parsing accept ignores index param
  963. */
  964. public function testParseAcceptIgnoreAcceptExtensions(): void
  965. {
  966. $request = new ServerRequest(['environment' => [
  967. 'url' => '/',
  968. 'HTTP_ACCEPT' => 'application/json;level=1, text/plain, */*',
  969. ]]);
  970. $result = $request->parseAccept();
  971. $expected = [
  972. '1.0' => ['application/json', 'text/plain', '*/*'],
  973. ];
  974. $this->assertEquals($expected, $result);
  975. }
  976. /**
  977. * Test that parsing accept headers with invalid syntax works.
  978. *
  979. * The header used is missing a q value for application/xml.
  980. */
  981. public function testParseAcceptInvalidSyntax(): void
  982. {
  983. $request = new ServerRequest(['environment' => [
  984. 'url' => '/',
  985. 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;image/png,image/jpeg,image/*;q=0.9,*/*;q=0.8',
  986. ]]);
  987. $result = $request->parseAccept();
  988. $expected = [
  989. '1.0' => ['text/html', 'application/xhtml+xml', 'application/xml', 'image/jpeg'],
  990. '0.9' => ['image/*'],
  991. '0.8' => ['*/*'],
  992. ];
  993. $this->assertEquals($expected, $result);
  994. }
  995. /**
  996. * Test the getQuery() method
  997. */
  998. public function testGetQuery(): void
  999. {
  1000. $array = [
  1001. 'query' => [
  1002. 'foo' => 'bar',
  1003. 'zero' => '0',
  1004. 'test' => [
  1005. 'foo', 'bar',
  1006. ],
  1007. ],
  1008. ];
  1009. $request = new ServerRequest($array);
  1010. $this->assertEquals([
  1011. 'foo' => 'bar',
  1012. 'zero' => '0',
  1013. 'test' => [
  1014. 'foo', 'bar',
  1015. ],
  1016. ], $request->getQuery());
  1017. $this->assertSame('bar', $request->getQuery('foo'));
  1018. $this->assertSame('0', $request->getQuery('zero'));
  1019. $this->assertNull($request->getQuery('imaginary'));
  1020. $this->assertSame('default', $request->getQuery('imaginary', 'default'));
  1021. $this->assertFalse($request->getQuery('imaginary', false));
  1022. $this->assertSame(['foo', 'bar'], $request->getQuery('test'));
  1023. $this->assertSame('bar', $request->getQuery('test.1'));
  1024. $this->assertNull($request->getQuery('test.2'));
  1025. $this->assertSame('default', $request->getQuery('test.2', 'default'));
  1026. }
  1027. /**
  1028. * Test getQueryParams
  1029. */
  1030. public function testGetQueryParams(): void
  1031. {
  1032. $get = [
  1033. 'test' => ['foo', 'bar'],
  1034. 'key' => 'value',
  1035. ];
  1036. $request = new ServerRequest([
  1037. 'query' => $get,
  1038. ]);
  1039. $this->assertSame($get, $request->getQueryParams());
  1040. }
  1041. /**
  1042. * Test withQueryParams and immutability
  1043. */
  1044. public function testWithQueryParams(): void
  1045. {
  1046. $get = [
  1047. 'test' => ['foo', 'bar'],
  1048. 'key' => 'value',
  1049. ];
  1050. $request = new ServerRequest([
  1051. 'query' => $get,
  1052. ]);
  1053. $new = $request->withQueryParams(['new' => 'data']);
  1054. $this->assertSame($get, $request->getQueryParams());
  1055. $this->assertSame(['new' => 'data'], $new->getQueryParams());
  1056. }
  1057. /**
  1058. * Test using param()
  1059. */
  1060. public function testReadingParams(): void
  1061. {
  1062. $request = new ServerRequest([
  1063. 'params' => [
  1064. 'controller' => 'Posts',
  1065. 'admin' => true,
  1066. 'truthy' => 1,
  1067. 'zero' => '0',
  1068. ],
  1069. ]);
  1070. $this->assertNull($request->getParam('not_set'));
  1071. $this->assertTrue($request->getParam('admin'));
  1072. $this->assertSame(1, $request->getParam('truthy'));
  1073. $this->assertSame('Posts', $request->getParam('controller'));
  1074. $this->assertSame('0', $request->getParam('zero'));
  1075. }
  1076. /**
  1077. * Test the data() method reading
  1078. */
  1079. public function testGetData(): void
  1080. {
  1081. $post = [
  1082. 'Model' => [
  1083. 'field' => 'value',
  1084. ],
  1085. ];
  1086. $request = new ServerRequest(compact('post'));
  1087. $this->assertEquals($post['Model'], $request->getData('Model'));
  1088. $this->assertEquals($post, $request->getData());
  1089. $this->assertNull($request->getData('Model.imaginary'));
  1090. $this->assertSame('value', $request->getData('Model.field', 'default'));
  1091. $this->assertSame('default', $request->getData('Model.imaginary', 'default'));
  1092. }
  1093. /**
  1094. * Test that getData() doesn't fail on scalar data.
  1095. */
  1096. public function testGetDataOnStringData(): void
  1097. {
  1098. $post = 'strange, but could happen';
  1099. $request = new ServerRequest(compact('post'));
  1100. $this->assertNull($request->getData('Model'));
  1101. $this->assertNull($request->getData('Model.field'));
  1102. }
  1103. /**
  1104. * Test writing falsey values.
  1105. */
  1106. public function testDataWritingFalsey(): void
  1107. {
  1108. $request = new ServerRequest();
  1109. $request = $request->withData('Post.null', null);
  1110. $this->assertNull($request->getData('Post.null'));
  1111. $request = $request->withData('Post.false', false);
  1112. $this->assertFalse($request->getData('Post.false'));
  1113. $request = $request->withData('Post.zero', 0);
  1114. $this->assertSame(0, $request->getData('Post.zero'));
  1115. $request = $request->withData('Post.empty', '');
  1116. $this->assertSame('', $request->getData('Post.empty'));
  1117. }
  1118. /**
  1119. * Test reading params
  1120. *
  1121. * @dataProvider paramReadingDataProvider
  1122. * @param mixed $expected
  1123. */
  1124. public function testGetParam(string $toRead, $expected): void
  1125. {
  1126. $request = new ServerRequest([
  1127. 'url' => '/',
  1128. 'params' => [
  1129. 'action' => 'index',
  1130. 'foo' => 'bar',
  1131. 'baz' => [
  1132. 'a' => [
  1133. 'b' => 'c',
  1134. ],
  1135. ],
  1136. 'admin' => true,
  1137. 'truthy' => 1,
  1138. 'zero' => '0',
  1139. ],
  1140. ]);
  1141. $this->assertSame($expected, $request->getParam($toRead));
  1142. }
  1143. /**
  1144. * Test getParam returning a default value.
  1145. */
  1146. public function testGetParamDefault(): void
  1147. {
  1148. $request = new ServerRequest([
  1149. 'params' => [
  1150. 'controller' => 'Articles',
  1151. 'null' => null,
  1152. ],
  1153. ]);
  1154. $this->assertSame('Articles', $request->getParam('controller', 'default'));
  1155. $this->assertSame('default', $request->getParam('null', 'default'));
  1156. $this->assertFalse($request->getParam('unset', false));
  1157. $this->assertNull($request->getParam('unset'));
  1158. }
  1159. /**
  1160. * Data provider for testing reading values with ServerRequest::getParam()
  1161. *
  1162. * @return array
  1163. */
  1164. public function paramReadingDataProvider(): array
  1165. {
  1166. return [
  1167. [
  1168. 'action',
  1169. 'index',
  1170. ],
  1171. [
  1172. 'baz',
  1173. [
  1174. 'a' => [
  1175. 'b' => 'c',
  1176. ],
  1177. ],
  1178. ],
  1179. [
  1180. 'baz.a.b',
  1181. 'c',
  1182. ],
  1183. [
  1184. 'does_not_exist',
  1185. null,
  1186. ],
  1187. [
  1188. 'admin',
  1189. true,
  1190. ],
  1191. [
  1192. 'truthy',
  1193. 1,
  1194. ],
  1195. [
  1196. 'zero',
  1197. '0',
  1198. ],
  1199. ];
  1200. }
  1201. /**
  1202. * test writing request params with param()
  1203. */
  1204. public function testParamWriting(): void
  1205. {
  1206. $request = new ServerRequest(['url' => '/']);
  1207. $request = $request->withParam('action', 'index');
  1208. $this->assertInstanceOf(
  1209. 'Cake\Http\ServerRequest',
  1210. $request->withParam('some', 'thing'),
  1211. 'Method has not returned $this'
  1212. );
  1213. $request = $request->withParam('Post.null', null);
  1214. $this->assertNull($request->getParam('Post.null'), 'default value should be used.');
  1215. $request = $request->withParam('Post.false', false);
  1216. $this->assertFalse($request->getParam('Post.false'));
  1217. $request = $request->withParam('Post.zero', 0);
  1218. $this->assertSame(0, $request->getParam('Post.zero'));
  1219. $request = $request->withParam('Post.empty', '');
  1220. $this->assertSame('', $request->getParam('Post.empty'));
  1221. $this->assertSame('index', $request->getParam('action'));
  1222. $request = $request->withParam('action', 'edit');
  1223. $this->assertSame('edit', $request->getParam('action'));
  1224. }
  1225. /**
  1226. * Test accept language
  1227. */
  1228. public function testAcceptLanguage(): void
  1229. {
  1230. $request = new ServerRequest();
  1231. // Weird language
  1232. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'inexistent,en-ca');
  1233. $result = $request->acceptLanguage();
  1234. $this->assertEquals(['inexistent', 'en-ca'], $result, 'Languages do not match');
  1235. // No qualifier
  1236. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'es_mx,en_ca');
  1237. $result = $request->acceptLanguage();
  1238. $this->assertEquals(['es-mx', 'en-ca'], $result, 'Languages do not match');
  1239. // With qualifier
  1240. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'en-US,en;q=0.8,pt-BR;q=0.6,pt;q=0.4');
  1241. $result = $request->acceptLanguage();
  1242. $this->assertEquals(['en-us', 'en', 'pt-br', 'pt'], $result, 'Languages do not match');
  1243. // With spaces
  1244. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'da, en-gb;q=0.8, en;q=0.7');
  1245. $result = $request->acceptLanguage();
  1246. $this->assertEquals(['da', 'en-gb', 'en'], $result, 'Languages do not match');
  1247. // Checking if requested
  1248. $request = $request->withEnv('HTTP_ACCEPT_LANGUAGE', 'es_mx,en_ca');
  1249. $result = $request->acceptLanguage('en-ca');
  1250. $this->assertTrue($result);
  1251. $result = $request->acceptLanguage('en-CA');
  1252. $this->assertTrue($result);
  1253. $result = $request->acceptLanguage('en-us');
  1254. $this->assertFalse($result);
  1255. $result = $request->acceptLanguage('en-US');
  1256. $this->assertFalse($result);
  1257. }
  1258. /**
  1259. * Test the input() method.
  1260. */
  1261. public function testInput(): void
  1262. {
  1263. $request = new ServerRequest([
  1264. 'input' => 'I came from stdin',
  1265. ]);
  1266. $this->deprecated(function () use ($request): void {
  1267. $result = $request->input();
  1268. $this->assertSame('I came from stdin', $result);
  1269. });
  1270. }
  1271. /**
  1272. * Test input() decoding.
  1273. *
  1274. * @group deprecated
  1275. */
  1276. public function testInputDecode(): void
  1277. {
  1278. $request = new ServerRequest([
  1279. 'input' => '{"name":"value"}',
  1280. ]);
  1281. $this->deprecated(function () use ($request): void {
  1282. $result = $request->input('json_decode');
  1283. $this->assertEquals(['name' => 'value'], (array)$result);
  1284. });
  1285. }
  1286. /**
  1287. * Test input() decoding with additional arguments.
  1288. *
  1289. * @group deprecated
  1290. */
  1291. public function testInputDecodeExtraParams(): void
  1292. {
  1293. $xml = <<<XML
  1294. <?xml version="1.0" encoding="utf-8"?>
  1295. <post>
  1296. <title id="title">Test</title>
  1297. </post>
  1298. XML;
  1299. $request = new ServerRequest([
  1300. 'input' => $xml,
  1301. ]);
  1302. $this->deprecated(function () use ($request): void {
  1303. $result = $request->input('Cake\Utility\Xml::build', ['return' => 'domdocument']);
  1304. $this->assertInstanceOf('DOMDocument', $result);
  1305. $this->assertSame(
  1306. 'Test',
  1307. $result->getElementsByTagName('title')->item(0)->childNodes->item(0)->wholeText
  1308. );
  1309. });
  1310. }
  1311. /**
  1312. * Test getBody
  1313. */
  1314. public function testGetBody(): void
  1315. {
  1316. $request = new ServerRequest([
  1317. 'input' => 'key=val&some=data',
  1318. ]);
  1319. $result = $request->getBody();
  1320. $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $result);
  1321. $this->assertSame('key=val&some=data', $result->getContents());
  1322. }
  1323. /**
  1324. * Test withBody
  1325. */
  1326. public function testWithBody(): void
  1327. {
  1328. $request = new ServerRequest([
  1329. 'input' => 'key=val&some=data',
  1330. ]);
  1331. $body = $this->getMockBuilder('Psr\Http\Message\StreamInterface')->getMock();
  1332. $new = $request->withBody($body);
  1333. $this->assertNotSame($new, $request);
  1334. $this->assertNotSame($body, $request->getBody());
  1335. $this->assertSame($body, $new->getBody());
  1336. }
  1337. /**
  1338. * Test getUri
  1339. */
  1340. public function testGetUri(): void
  1341. {
  1342. $request = new ServerRequest(['url' => 'articles/view/3']);
  1343. $result = $request->getUri();
  1344. $this->assertInstanceOf('Psr\Http\Message\UriInterface', $result);
  1345. $this->assertSame('/articles/view/3', $result->getPath());
  1346. }
  1347. /**
  1348. * Test withUri
  1349. */
  1350. public function testWithUri(): void
  1351. {
  1352. $request = new ServerRequest([
  1353. 'environment' => [
  1354. 'HTTP_HOST' => 'example.com',
  1355. ],
  1356. 'url' => 'articles/view/3',
  1357. ]);
  1358. $uri = $this->getMockBuilder('Psr\Http\Message\UriInterface')->getMock();
  1359. $new = $request->withUri($uri);
  1360. $this->assertNotSame($new, $request);
  1361. $this->assertNotSame($uri, $request->getUri());
  1362. $this->assertSame($uri, $new->getUri());
  1363. }
  1364. /**
  1365. * Test withUri() and preserveHost
  1366. */
  1367. public function testWithUriPreserveHost(): void
  1368. {
  1369. $request = new ServerRequest([
  1370. 'environment' => [
  1371. 'HTTP_HOST' => 'localhost',
  1372. ],
  1373. 'url' => 'articles/view/3',
  1374. ]);
  1375. $uri = new Uri();
  1376. $uri = $uri->withHost('example.com')
  1377. ->withPort(123)
  1378. ->withPath('articles/view/3');
  1379. $new = $request->withUri($uri, false);
  1380. $this->assertNotSame($new, $request);
  1381. $this->assertSame('example.com:123', $new->getHeaderLine('Host'));
  1382. }
  1383. /**
  1384. * Test withUri() and preserveHost missing the host header
  1385. */
  1386. public function testWithUriPreserveHostNoHostHeader(): void
  1387. {
  1388. $request = new ServerRequest([
  1389. 'url' => 'articles/view/3',
  1390. ]);
  1391. $uri = new Uri();
  1392. $uri = $uri->withHost('example.com')
  1393. ->withPort(123)
  1394. ->withPath('articles/view/3');
  1395. $new = $request->withUri($uri, false);
  1396. $this->assertSame('example.com:123', $new->getHeaderLine('Host'));
  1397. }
  1398. /**
  1399. * Test the cookie() method.
  1400. */
  1401. public function testGetCookie(): void
  1402. {
  1403. $request = new ServerRequest([
  1404. 'cookies' => [
  1405. 'testing' => 'A value in the cookie',
  1406. 'user' => [
  1407. 'remember' => '1',
  1408. ],
  1409. '123' => 'a integer key cookie',
  1410. ],
  1411. ]);
  1412. $this->assertSame('A value in the cookie', $request->getCookie('testing'));
  1413. $this->assertSame('a integer key cookie', $request->getCookie('123'));
  1414. $this->assertNull($request->getCookie('not there'));
  1415. $this->assertSame('default', $request->getCookie('not there', 'default'));
  1416. $this->assertSame('1', $request->getCookie('user.remember'));
  1417. $this->assertSame('1', $request->getCookie('user.remember', 'default'));
  1418. $this->assertSame('default', $request->getCookie('user.not there', 'default'));
  1419. }
  1420. /**
  1421. * Test getCookieParams()
  1422. */
  1423. public function testGetCookieParams(): void
  1424. {
  1425. $cookies = [
  1426. 'testing' => 'A value in the cookie',
  1427. ];
  1428. $request = new ServerRequest(['cookies' => $cookies]);
  1429. $this->assertSame($cookies, $request->getCookieParams());
  1430. }
  1431. /**
  1432. * Test withCookieParams()
  1433. */
  1434. public function testWithCookieParams(): void
  1435. {
  1436. $cookies = [
  1437. 'testing' => 'A value in the cookie',
  1438. ];
  1439. $request = new ServerRequest(['cookies' => $cookies]);
  1440. $new = $request->withCookieParams(['remember_me' => 1]);
  1441. $this->assertNotSame($new, $request);
  1442. $this->assertSame($cookies, $request->getCookieParams());
  1443. $this->assertSame(['remember_me' => 1], $new->getCookieParams());
  1444. }
  1445. /**
  1446. * Test getting a cookie collection from a request.
  1447. */
  1448. public function testGetCookieCollection(): void
  1449. {
  1450. $cookies = [
  1451. 'remember_me' => '1',
  1452. 'color' => 'blue',
  1453. ];
  1454. $request = new ServerRequest(['cookies' => $cookies]);
  1455. $cookies = $request->getCookieCollection();
  1456. $this->assertInstanceOf(CookieCollection::class, $cookies);
  1457. $this->assertCount(2, $cookies);
  1458. $this->assertSame('1', $cookies->get('remember_me')->getValue());
  1459. $this->assertSame('blue', $cookies->get('color')->getValue());
  1460. }
  1461. /**
  1462. * Test replacing cookies from a collection
  1463. */
  1464. public function testWithCookieCollection(): void
  1465. {
  1466. $cookies = new CookieCollection([new Cookie('remember_me', 1), new Cookie('color', 'red')]);
  1467. $request = new ServerRequest(['cookies' => ['bad' => 'goaway']]);
  1468. $new = $request->withCookieCollection($cookies);
  1469. $this->assertNotSame($new, $request, 'Should clone');
  1470. $this->assertSame(['bad' => 'goaway'], $request->getCookieParams());
  1471. $this->assertSame(['remember_me' => 1, 'color' => 'red'], $new->getCookieParams());
  1472. $cookies = $new->getCookieCollection();
  1473. $this->assertCount(2, $cookies);
  1474. $this->assertSame('red', $cookies->get('color')->getValue());
  1475. }
  1476. /**
  1477. * TestAllowMethod
  1478. */
  1479. public function testAllowMethod(): void
  1480. {
  1481. $request = new ServerRequest(['environment' => [
  1482. 'url' => '/posts/edit/1',
  1483. 'REQUEST_METHOD' => 'PUT',
  1484. ]]);
  1485. $this->assertTrue($request->allowMethod('put'));
  1486. $request = $request->withEnv('REQUEST_METHOD', 'DELETE');
  1487. $this->assertTrue($request->allowMethod(['post', 'delete']));
  1488. }
  1489. /**
  1490. * Test allowMethod throwing exception
  1491. */
  1492. public function testAllowMethodException(): void
  1493. {
  1494. $request = new ServerRequest([
  1495. 'url' => '/posts/edit/1',
  1496. 'environment' => ['REQUEST_METHOD' => 'PUT'],
  1497. ]);
  1498. try {
  1499. $request->allowMethod(['POST', 'DELETE']);
  1500. $this->fail('An expected exception has not been raised.');
  1501. } catch (MethodNotAllowedException $e) {
  1502. $this->assertEquals(['Allow' => 'POST, DELETE'], $e->getHeaders());
  1503. }
  1504. $this->expectException(MethodNotAllowedException::class);
  1505. $request->allowMethod('POST');
  1506. }
  1507. /**
  1508. * Tests getting the session from the request
  1509. */
  1510. public function testGetSession(): void
  1511. {
  1512. $session = new Session();
  1513. $request = new ServerRequest(['session' => $session]);
  1514. $this->assertSame($session, $request->getSession());
  1515. $request = new ServerRequest();
  1516. $this->assertEquals($session, $request->getSession());
  1517. }
  1518. public function testGetFlash(): void
  1519. {
  1520. $request = new ServerRequest();
  1521. $this->assertInstanceOf(FlashMessage::class, $request->getFlash());
  1522. }
  1523. /**
  1524. * Test the content type method.
  1525. */
  1526. public function testContentType(): void
  1527. {
  1528. $request = new ServerRequest([
  1529. 'environment' => ['HTTP_CONTENT_TYPE' => 'application/json'],
  1530. ]);
  1531. $this->assertSame('application/json', $request->contentType());
  1532. $request = new ServerRequest([
  1533. 'environment' => ['HTTP_CONTENT_TYPE' => 'application/xml'],
  1534. ]);
  1535. $this->assertSame('application/xml', $request->contentType(), 'prefer non http header.');
  1536. }
  1537. /**
  1538. * Test updating params in a psr7 fashion.
  1539. */
  1540. public function testWithParam(): void
  1541. {
  1542. $request = new ServerRequest([
  1543. 'params' => ['controller' => 'Articles'],
  1544. ]);
  1545. $result = $request->withParam('action', 'view');
  1546. $this->assertNotSame($result, $request, 'New instance should be made');
  1547. $this->assertNull($request->getParam('action'), 'No side-effect on original');
  1548. $this->assertSame('view', $result->getParam('action'));
  1549. $result = $request->withParam('action', 'index')
  1550. ->withParam('plugin', 'DebugKit')
  1551. ->withParam('prefix', 'Admin');
  1552. $this->assertNotSame($result, $request, 'New instance should be made');
  1553. $this->assertNull($request->getParam('action'), 'No side-effect on original');
  1554. $this->assertSame('index', $result->getParam('action'));
  1555. $this->assertSame('DebugKit', $result->getParam('plugin'));
  1556. $this->assertSame('Admin', $result->getParam('prefix'));
  1557. }
  1558. /**
  1559. * Test getting the parsed body parameters.
  1560. */
  1561. public function testGetParsedBody(): void
  1562. {
  1563. $data = ['title' => 'First', 'body' => 'Best Article!'];
  1564. $request = new ServerRequest(['post' => $data]);
  1565. $this->assertSame($data, $request->getParsedBody());
  1566. $request = new ServerRequest();
  1567. $this->assertSame([], $request->getParsedBody());
  1568. }
  1569. /**
  1570. * Test replacing the parsed body parameters.
  1571. */
  1572. public function testWithParsedBody(): void
  1573. {
  1574. $data = ['title' => 'First', 'body' => 'Best Article!'];
  1575. $request = new ServerRequest([]);
  1576. $new = $request->withParsedBody($data);
  1577. $this->assertNotSame($request, $new);
  1578. $this->assertSame([], $request->getParsedBody());
  1579. $this->assertSame($data, $new->getParsedBody());
  1580. }
  1581. /**
  1582. * Test updating POST data in a psr7 fashion.
  1583. */
  1584. public function testWithData(): void
  1585. {
  1586. $request = new ServerRequest([
  1587. 'post' => [
  1588. 'Model' => [
  1589. 'field' => 'value',
  1590. ],
  1591. ],
  1592. ]);
  1593. $result = $request->withData('Model.new_value', 'new value');
  1594. $this->assertNull($request->getData('Model.new_value'), 'Original request should not change.');
  1595. $this->assertNotSame($result, $request);
  1596. $this->assertSame('new value', $result->getData('Model.new_value'));
  1597. $this->assertSame('new value', $result->getData()['Model']['new_value']);
  1598. $this->assertSame('value', $result->getData('Model.field'));
  1599. }
  1600. /**
  1601. * Test removing data from a request
  1602. */
  1603. public function testWithoutData(): void
  1604. {
  1605. $request = new ServerRequest([
  1606. 'post' => [
  1607. 'Model' => [
  1608. 'id' => 1,
  1609. 'field' => 'value',
  1610. ],
  1611. ],
  1612. ]);
  1613. $updated = $request->withoutData('Model.field');
  1614. $this->assertNotSame($updated, $request);
  1615. $this->assertSame('value', $request->getData('Model.field'), 'Original request should not change.');
  1616. $this->assertNull($updated->getData('Model.field'), 'data removed from updated request');
  1617. $this->assertFalse(isset($updated->getData()['Model']['field']), 'data removed from updated request');
  1618. }
  1619. /**
  1620. * Test updating POST data when keys don't exist
  1621. */
  1622. public function testWithDataMissingIntermediaryKeys(): void
  1623. {
  1624. $request = new ServerRequest([
  1625. 'post' => [
  1626. 'Model' => [
  1627. 'field' => 'value',
  1628. ],
  1629. ],
  1630. ]);
  1631. $result = $request->withData('Model.field.new_value', 'new value');
  1632. $this->assertSame(
  1633. 'new value',
  1634. $result->getData('Model.field.new_value')
  1635. );
  1636. $this->assertSame(
  1637. 'new value',
  1638. $result->getData()['Model']['field']['new_value']
  1639. );
  1640. }
  1641. /**
  1642. * Test updating POST data with falsey values
  1643. */
  1644. public function testWithDataFalseyValues(): void
  1645. {
  1646. $request = new ServerRequest([
  1647. 'post' => [],
  1648. ]);
  1649. $result = $request->withData('false', false)
  1650. ->withData('null', null)
  1651. ->withData('empty_string', '')
  1652. ->withData('zero', 0)
  1653. ->withData('zero_string', '0');
  1654. $expected = [
  1655. 'false' => false,
  1656. 'null' => null,
  1657. 'empty_string' => '',
  1658. 'zero' => 0,
  1659. 'zero_string' => '0',
  1660. ];
  1661. $this->assertSame($expected, $result->getData());
  1662. }
  1663. /**
  1664. * Test setting attributes.
  1665. */
  1666. public function testWithAttribute(): void
  1667. {
  1668. $request = new ServerRequest([]);
  1669. $this->assertNull($request->getAttribute('key'));
  1670. $this->assertSame('default', $request->getAttribute('key', 'default'));
  1671. $new = $request->withAttribute('key', 'value');
  1672. $this->assertNotEquals($new, $request, 'Should be different');
  1673. $this->assertNull($request->getAttribute('key'), 'Old instance not modified');
  1674. $this->assertSame('value', $new->getAttribute('key'));
  1675. $update = $new->withAttribute('key', ['complex']);
  1676. $this->assertNotEquals($update, $new, 'Should be different');
  1677. $this->assertSame(['complex'], $update->getAttribute('key'));
  1678. }
  1679. /**
  1680. * Test that replacing the session can be done via withAttribute()
  1681. */
  1682. public function testWithAttributeSession(): void
  1683. {
  1684. $request = new ServerRequest([]);
  1685. $request->getSession()->write('attrKey', 'session-value');
  1686. $update = $request->withAttribute('session', Session::create());
  1687. $this->assertSame('session-value', $request->getAttribute('session')->read('attrKey'));
  1688. $this->assertNotSame($request->getAttribute('session'), $update->getAttribute('session'));
  1689. $this->assertNotSame($request->getSession()->read('attrKey'), $update->getSession()->read('attrKey'));
  1690. }
  1691. /**
  1692. * Test getting all attributes.
  1693. */
  1694. public function testGetAttributes(): void
  1695. {
  1696. $request = new ServerRequest([]);
  1697. $new = $request->withAttribute('key', 'value')
  1698. ->withAttribute('nully', null)
  1699. ->withAttribute('falsey', false);
  1700. $this->assertFalse($new->getAttribute('falsey'));
  1701. $this->assertNull($new->getAttribute('nully'));
  1702. $expected = [
  1703. 'key' => 'value',
  1704. 'nully' => null,
  1705. 'falsey' => false,
  1706. 'params' => [
  1707. 'plugin' => null,
  1708. 'controller' => null,
  1709. 'action' => null,
  1710. '_ext' => null,
  1711. 'pass' => [],
  1712. ],
  1713. 'webroot' => '',
  1714. 'base' => '',
  1715. 'here' => '/',
  1716. ];
  1717. $this->assertEquals($expected, $new->getAttributes());
  1718. }
  1719. /**
  1720. * Test unsetting attributes.
  1721. */
  1722. public function testWithoutAttribute(): void
  1723. {
  1724. $request = new ServerRequest([]);
  1725. $new = $request->withAttribute('key', 'value');
  1726. $update = $request->withoutAttribute('key');
  1727. $this->assertNotEquals($update, $new, 'Should be different');
  1728. $this->assertNull($update->getAttribute('key'));
  1729. }
  1730. /**
  1731. * Test that withoutAttribute() cannot remove emulatedAttributes properties.
  1732. *
  1733. * @dataProvider emulatedPropertyProvider
  1734. */
  1735. public function testWithoutAttributesDenyEmulatedProperties(string $prop): void
  1736. {
  1737. $this->expectException(InvalidArgumentException::class);
  1738. $request = new ServerRequest([]);
  1739. $request->withoutAttribute($prop);
  1740. }
  1741. /**
  1742. * Test the requestTarget methods.
  1743. */
  1744. public function testWithRequestTarget(): void
  1745. {
  1746. $request = new ServerRequest([
  1747. 'environment' => [
  1748. 'REQUEST_URI' => '/articles/view/1',
  1749. 'QUERY_STRING' => 'comments=1&open=0',
  1750. ],
  1751. 'base' => '/basedir',
  1752. ]);
  1753. $this->assertSame(
  1754. '/articles/view/1?comments=1&open=0',
  1755. $request->getRequestTarget(),
  1756. 'Should not include basedir.'
  1757. );
  1758. $new = $request->withRequestTarget('/articles/view/3');
  1759. $this->assertNotSame($new, $request);
  1760. $this->assertSame(
  1761. '/articles/view/1?comments=1&open=0',
  1762. $request->getRequestTarget(),
  1763. 'should be unchanged.'
  1764. );
  1765. $this->assertSame('/articles/view/3', $new->getRequestTarget(), 'reflects method call');
  1766. }
  1767. /**
  1768. * Test withEnv()
  1769. */
  1770. public function testWithEnv(): void
  1771. {
  1772. $request = new ServerRequest();
  1773. $newRequest = $request->withEnv('HTTP_HOST', 'cakephp.org');
  1774. $this->assertNotSame($request, $newRequest);
  1775. $this->assertSame('cakephp.org', $newRequest->getEnv('HTTP_HOST'));
  1776. }
  1777. /**
  1778. * Test getEnv()
  1779. */
  1780. public function testGetEnv(): void
  1781. {
  1782. $request = new ServerRequest([
  1783. 'environment' => ['TEST' => 'ing'],
  1784. ]);
  1785. //Test default null
  1786. $this->assertNull($request->getEnv('Foo'));
  1787. //Test default set
  1788. $this->assertSame('Bar', $request->getEnv('Foo', 'Bar'));
  1789. //Test env() fallback
  1790. $this->assertSame('ing', $request->getEnv('test'));
  1791. }
  1792. /**
  1793. * Data provider for emulated property tests.
  1794. *
  1795. * @return array
  1796. */
  1797. public function emulatedPropertyProvider(): array
  1798. {
  1799. return [
  1800. ['here'],
  1801. ['params'],
  1802. ['base'],
  1803. ['webroot'],
  1804. ['session'],
  1805. ];
  1806. }
  1807. }