ServerRequestFactoryTest.php 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403
  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 3.3.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Http;
  17. use Cake\Core\Configure;
  18. use Cake\Http\ServerRequest;
  19. use Cake\Http\ServerRequestFactory;
  20. use Cake\Http\Session;
  21. use Cake\TestSuite\TestCase;
  22. use Laminas\Diactoros\Exception\InvalidArgumentException;
  23. use Laminas\Diactoros\UploadedFile;
  24. use PHPUnit\Framework\Attributes\DataProvider;
  25. use PHPUnit\Framework\Attributes\PreserveGlobalState;
  26. use PHPUnit\Framework\Attributes\RunInSeparateProcess;
  27. use Psr\Http\Message\UploadedFileInterface;
  28. /**
  29. * Test case for the server factory.
  30. */
  31. class ServerRequestFactoryTest extends TestCase
  32. {
  33. /**
  34. * Test fromGlobals reads super globals
  35. */
  36. public function testFromGlobalsSuperGlobals(): void
  37. {
  38. $post = [
  39. 'title' => 'custom',
  40. ];
  41. $files = [
  42. 'image' => [
  43. 'tmp_name' => __FILE__,
  44. 'error' => 0,
  45. 'name' => 'cats.png',
  46. 'type' => 'image/png',
  47. 'size' => 2112,
  48. ],
  49. ];
  50. $cookies = ['key' => 'value'];
  51. $query = ['query' => 'string'];
  52. $res = ServerRequestFactory::fromGlobals([], $query, $post, $cookies, $files);
  53. $this->assertSame($cookies['key'], $res->getCookie('key'));
  54. $this->assertSame($query['query'], $res->getQuery('query'));
  55. $this->assertArrayHasKey('title', $res->getData());
  56. $this->assertArrayHasKey('image', $res->getData());
  57. $this->assertCount(1, $res->getUploadedFiles());
  58. /** @var \Psr\Http\Message\UploadedFileInterface $expected */
  59. $expected = $res->getData('image');
  60. $this->assertInstanceOf(UploadedFileInterface::class, $expected);
  61. $this->assertSame($files['image']['size'], $expected->getSize());
  62. $this->assertSame($files['image']['error'], $expected->getError());
  63. $this->assertSame($files['image']['name'], $expected->getClientFilename());
  64. $this->assertSame($files['image']['type'], $expected->getClientMediaType());
  65. }
  66. public function testFromGlobalsUriScheme(): void
  67. {
  68. $server = [
  69. 'DOCUMENT_ROOT' => '/cake/repo/webroot',
  70. 'PHP_SELF' => '/index.php',
  71. 'REQUEST_URI' => '/posts/add',
  72. 'HTTP_X_FORWARDED_PROTO' => 'https',
  73. ];
  74. $request = ServerRequestFactory::fromGlobals($server);
  75. $this->assertSame('http', $request->scheme());
  76. $this->assertSame('http', $request->getUri()->getScheme());
  77. $request->setTrustedProxies([]);
  78. // Yeah even setting an empty list of proxies does the trick.
  79. $this->assertSame('https', $request->scheme());
  80. $this->assertSame('https', $request->getUri()->getScheme());
  81. }
  82. /**
  83. * Test fromGlobals includes the session
  84. */
  85. #[PreserveGlobalState(false)]
  86. #[RunInSeparateProcess]
  87. public function testFromGlobalsUrlSession(): void
  88. {
  89. Configure::write('App.base', '/basedir');
  90. $server = [
  91. 'DOCUMENT_ROOT' => '/cake/repo/branches/1.2.x.x/webroot',
  92. 'PHP_SELF' => '/index.php',
  93. 'REQUEST_URI' => '/posts/add',
  94. ];
  95. $res = ServerRequestFactory::fromGlobals($server);
  96. $session = $res->getAttribute('session');
  97. $this->assertInstanceOf(Session::class, $session);
  98. $this->assertSame('/basedir/', ini_get('session.cookie_path'), 'Needs trailing / for cookie to work');
  99. }
  100. /**
  101. * Test fromGlobals with App.base defined.
  102. */
  103. public function testFromGlobalsUrlBaseDefined(): void
  104. {
  105. Configure::write('App.base', 'basedir');
  106. $server = [
  107. 'DOCUMENT_ROOT' => '/cake/repo/branches/1.2.x.x/webroot',
  108. 'PHP_SELF' => '/index.php',
  109. 'REQUEST_URI' => '/posts/add',
  110. ];
  111. $res = ServerRequestFactory::fromGlobals($server);
  112. $this->assertSame('basedir', $res->getAttribute('base'));
  113. $this->assertSame('basedir/', $res->getAttribute('webroot'));
  114. $this->assertSame('/posts/add', $res->getUri()->getPath());
  115. }
  116. /**
  117. * Test fromGlobals with mod-rewrite server configuration.
  118. */
  119. public function testFromGlobalsUrlModRewrite(): void
  120. {
  121. Configure::write('App.baseUrl', false);
  122. $server = [
  123. 'DOCUMENT_ROOT' => '/cake/repo/branches',
  124. 'PHP_SELF' => '/urlencode me/webroot/index.php',
  125. 'REQUEST_URI' => '/posts/view/1',
  126. ];
  127. $res = ServerRequestFactory::fromGlobals($server);
  128. $this->assertSame('/urlencode%20me', $res->getAttribute('base'));
  129. $this->assertSame('/urlencode%20me/', $res->getAttribute('webroot'));
  130. $this->assertSame('/posts/view/1', $res->getUri()->getPath());
  131. $request = ServerRequestFactory::fromGlobals([
  132. 'DOCUMENT_ROOT' => '/cake/repo/branches',
  133. 'PHP_SELF' => '/1.2.x.x/webroot/index.php',
  134. 'PATH_INFO' => '/posts/view/1',
  135. ]);
  136. $this->assertSame('/1.2.x.x', $request->getAttribute('base'));
  137. $this->assertSame('/1.2.x.x/', $request->getAttribute('webroot'));
  138. $this->assertSame('/posts/view/1', $request->getRequestTarget());
  139. $request = ServerRequestFactory::fromGlobals([
  140. 'DOCUMENT_ROOT' => '/cake/repo/branches/1.2.x.x/test/',
  141. 'PHP_SELF' => '/webroot/index.php',
  142. ]);
  143. $this->assertSame('', $request->getAttribute('base'));
  144. $this->assertSame('/', $request->getAttribute('webroot'));
  145. $request = ServerRequestFactory::fromGlobals([
  146. 'DOCUMENT_ROOT' => '/some/apps/where',
  147. 'PHP_SELF' => '/webroot/index.php',
  148. ]);
  149. $this->assertSame('', $request->getAttribute('base'));
  150. $this->assertSame('/', $request->getAttribute('webroot'));
  151. Configure::write('App.dir', 'auth');
  152. $request = ServerRequestFactory::fromGlobals([
  153. 'DOCUMENT_ROOT' => '/cake/repo/branches',
  154. 'PHP_SELF' => '/demos/webroot/index.php',
  155. ]);
  156. $this->assertSame('/demos', $request->getAttribute('base'));
  157. $this->assertSame('/demos/', $request->getAttribute('webroot'));
  158. Configure::write('App.dir', 'code');
  159. $request = ServerRequestFactory::fromGlobals([
  160. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  161. 'PHP_SELF' => '/clients/PewterReport/webroot/index.php',
  162. ]);
  163. $this->assertSame('/clients/PewterReport', $request->getAttribute('base'));
  164. $this->assertSame('/clients/PewterReport/', $request->getAttribute('webroot'));
  165. }
  166. /**
  167. * Test baseUrl with ModRewrite alias
  168. */
  169. public function testBaseUrlwithModRewriteAlias(): void
  170. {
  171. Configure::write('App.base', '/control');
  172. $request = ServerRequestFactory::fromGlobals([
  173. 'DOCUMENT_ROOT' => '/home/aplusnur/public_html',
  174. 'PHP_SELF' => '/control/index.php',
  175. ]);
  176. $this->assertSame('/control', $request->getAttribute('base'));
  177. $this->assertSame('/control/', $request->getAttribute('webroot'));
  178. Configure::write('App.base', false);
  179. Configure::write('App.dir', 'affiliate');
  180. Configure::write('App.webroot', 'newaffiliate');
  181. $request = ServerRequestFactory::fromGlobals([
  182. 'DOCUMENT_ROOT' => '/var/www/abtravaff/html',
  183. 'PHP_SELF' => '/newaffiliate/index.php',
  184. ]);
  185. $this->assertSame('', $request->getAttribute('base'));
  186. $this->assertSame('/', $request->getAttribute('webroot'));
  187. }
  188. /**
  189. * Test base, webroot, URL and here parsing when there is URL rewriting but
  190. * CakePHP gets called with index.php in URL nonetheless.
  191. *
  192. * Tests uri with
  193. *
  194. * - index.php/
  195. * - index.php/
  196. * - index.php/apples/
  197. * - index.php/bananas/eat/tasty_banana
  198. */
  199. public function testBaseUrlWithModRewriteAndIndexPhp(): void
  200. {
  201. $request = ServerRequestFactory::fromGlobals([
  202. 'DOCUMENT_ROOT' => '/cakephp/webroot/index.php',
  203. 'PHP_SELF' => '/cakephp/webroot/index.php',
  204. ]);
  205. $this->assertSame('/cakephp', $request->getAttribute('base'));
  206. $this->assertSame('/cakephp/', $request->getAttribute('webroot'));
  207. $this->assertSame('/', $request->getRequestTarget());
  208. $request = ServerRequestFactory::fromGlobals([
  209. 'REQUEST_URI' => '/cakephp/webroot/index.php/',
  210. 'PHP_SELF' => '/cakephp/webroot/index.php/',
  211. 'PATH_INFO' => '/',
  212. ]);
  213. $this->assertSame('/cakephp', $request->getAttribute('base'));
  214. $this->assertSame('/cakephp/', $request->getAttribute('webroot'));
  215. $this->assertSame('/', $request->getRequestTarget());
  216. $request = ServerRequestFactory::fromGlobals([
  217. 'REQUEST_URI' => '/cakephp/webroot/index.php/apples',
  218. 'PHP_SELF' => '/cakephp/webroot/index.php/apples',
  219. 'PATH_INFO' => '/apples',
  220. ]);
  221. $this->assertSame('/cakephp', $request->getAttribute('base'));
  222. $this->assertSame('/cakephp/', $request->getAttribute('webroot'));
  223. $this->assertSame('/apples', $request->getRequestTarget());
  224. $request = ServerRequestFactory::fromGlobals([
  225. 'REQUEST_URI' => '/cakephp/webroot/index.php/melons/share/',
  226. 'PHP_SELF' => '/cakephp/webroot/index.php/melons/share/',
  227. 'PATH_INFO' => '/melons/share/',
  228. ]);
  229. $this->assertSame('/cakephp', $request->getAttribute('base'));
  230. $this->assertSame('/cakephp/', $request->getAttribute('webroot'));
  231. $this->assertSame('/melons/share/', $request->getRequestTarget());
  232. $request = ServerRequestFactory::fromGlobals([
  233. 'REQUEST_URI' => '/cakephp/webroot/index.php/bananas/eat/tasty_banana',
  234. 'PHP_SELF' => '/cakephp/webroot/index.php/bananas/eat/tasty_banana',
  235. 'PATH_INFO' => '/bananas/eat/tasty_banana',
  236. ]);
  237. $this->assertSame('/cakephp', $request->getAttribute('base'));
  238. $this->assertSame('/cakephp/', $request->getAttribute('webroot'));
  239. $this->assertSame('/bananas/eat/tasty_banana', $request->getRequestTarget());
  240. }
  241. /**
  242. * Test that even if mod_rewrite is on, and the url contains index.php
  243. * and there are numerous //s that the base/webroot is calculated correctly.
  244. */
  245. public function testBaseUrlWithModRewriteAndExtraSlashes(): void
  246. {
  247. $request = ServerRequestFactory::fromGlobals([
  248. 'REQUEST_URI' => '/cakephp/webroot///index.php/bananas/eat',
  249. 'PHP_SELF' => '/cakephp/webroot///index.php/bananas/eat',
  250. 'PATH_INFO' => '/bananas/eat',
  251. ]);
  252. $this->assertSame('/cakephp', $request->getAttribute('base'));
  253. $this->assertSame('/cakephp/', $request->getAttribute('webroot'));
  254. $this->assertSame('/bananas/eat', $request->getRequestTarget());
  255. }
  256. /**
  257. * Test fromGlobals with mod-rewrite in the root dir.
  258. */
  259. public function testFromGlobalsUrlModRewriteRootDir(): void
  260. {
  261. $server = [
  262. 'DOCUMENT_ROOT' => '/cake/repo/branches/1.2.x.x/webroot',
  263. 'PHP_SELF' => '/index.php',
  264. 'REQUEST_URI' => '/posts/add',
  265. ];
  266. $res = ServerRequestFactory::fromGlobals($server);
  267. $this->assertSame('', $res->getAttribute('base'));
  268. $this->assertSame('/', $res->getAttribute('webroot'));
  269. $this->assertSame('/posts/add', $res->getUri()->getPath());
  270. }
  271. /**
  272. * Test fromGlobals with App.baseUrl defined implying no
  273. * mod-rewrite and no virtual path.
  274. */
  275. public function testFromGlobalsUrlNoModRewriteWebrootDir(): void
  276. {
  277. Configure::write('App', [
  278. 'dir' => 'app',
  279. 'webroot' => 'www',
  280. 'base' => false,
  281. 'baseUrl' => '/cake/index.php',
  282. ]);
  283. $server = [
  284. 'DOCUMENT_ROOT' => '/Users/markstory/Sites',
  285. 'SCRIPT_FILENAME' => '/Users/markstory/Sites/cake/www/index.php',
  286. 'PHP_SELF' => '/cake/www/index.php/posts/index',
  287. 'REQUEST_URI' => '/cake/www/index.php',
  288. ];
  289. $res = ServerRequestFactory::fromGlobals($server);
  290. $this->assertSame('/cake/www/', $res->getAttribute('webroot'));
  291. $this->assertSame('/cake/index.php', $res->getAttribute('base'));
  292. $this->assertSame('/', $res->getUri()->getPath());
  293. }
  294. /**
  295. * Test fromGlobals with App.baseUrl defined implying no
  296. * mod-rewrite
  297. */
  298. public function testFromGlobalsUrlNoModRewrite(): void
  299. {
  300. Configure::write('App', [
  301. 'dir' => 'app',
  302. 'webroot' => 'webroot',
  303. 'base' => false,
  304. 'baseUrl' => '/cake/index.php',
  305. ]);
  306. $server = [
  307. 'DOCUMENT_ROOT' => '/Users/markstory/Sites',
  308. 'SCRIPT_FILENAME' => '/Users/markstory/Sites/cake/index.php',
  309. 'PHP_SELF' => '/cake/index.php/posts/index',
  310. 'REQUEST_URI' => '/cake/index.php/posts/index',
  311. ];
  312. $res = ServerRequestFactory::fromGlobals($server);
  313. $this->assertSame('/cake/webroot/', $res->getAttribute('webroot'));
  314. $this->assertSame('/cake/index.php', $res->getAttribute('base'));
  315. $this->assertSame('/posts/index', $res->getUri()->getPath());
  316. }
  317. /**
  318. * Test fromGlobals with App.baseUrl defined implying no
  319. * mod-rewrite in the root dir.
  320. */
  321. public function testFromGlobalsUrlNoModRewriteRootDir(): void
  322. {
  323. Configure::write('App', [
  324. 'dir' => 'cake',
  325. 'webroot' => 'webroot',
  326. 'base' => false,
  327. 'baseUrl' => '/index.php',
  328. ]);
  329. $server = [
  330. 'DOCUMENT_ROOT' => '/Users/markstory/Sites/cake',
  331. 'SCRIPT_FILENAME' => '/Users/markstory/Sites/cake/index.php',
  332. 'PHP_SELF' => '/index.php/posts/add',
  333. 'REQUEST_URI' => '/index.php/posts/add',
  334. ];
  335. $res = ServerRequestFactory::fromGlobals($server);
  336. $this->assertSame('/webroot/', $res->getAttribute('webroot'));
  337. $this->assertSame('/index.php', $res->getAttribute('base'));
  338. $this->assertSame('/posts/add', $res->getUri()->getPath());
  339. }
  340. /**
  341. * Check that a sub-directory containing app|webroot doesn't get mishandled when re-writing is off.
  342. */
  343. public function testBaseUrlWithAppAndWebrootInDirname(): void
  344. {
  345. Configure::write('App.baseUrl', '/approval/index.php');
  346. $request = ServerRequestFactory::fromGlobals([
  347. 'DOCUMENT_ROOT' => '/Users/markstory/Sites/',
  348. 'SCRIPT_FILENAME' => '/Users/markstory/Sites/approval/index.php',
  349. ]);
  350. $this->assertSame('/approval/index.php', $request->getAttribute('base'));
  351. $this->assertSame('/approval/webroot/', $request->getAttribute('webroot'));
  352. Configure::write('App.baseUrl', '/webrootable/index.php');
  353. $request = ServerRequestFactory::fromGlobals([
  354. 'DOCUMENT_ROOT' => '/Users/markstory/Sites/',
  355. 'SCRIPT_FILENAME' => '/Users/markstory/Sites/webrootable/index.php',
  356. ]);
  357. $this->assertSame('/webrootable/index.php', $request->getAttribute('base'));
  358. $this->assertSame('/webrootable/webroot/', $request->getAttribute('webroot'));
  359. }
  360. /**
  361. * Test baseUrl and webroot with baseUrl
  362. */
  363. public function testBaseUrlAndWebrootWithBaseUrl(): void
  364. {
  365. Configure::write('App.dir', 'App');
  366. Configure::write('App.baseUrl', '/App/webroot/index.php');
  367. $request = ServerRequestFactory::fromGlobals();
  368. $this->assertSame('/App/webroot/index.php', $request->getAttribute('base'));
  369. $this->assertSame('/App/webroot/', $request->getAttribute('webroot'));
  370. Configure::write('App.baseUrl', '/App/webroot/test.php');
  371. $request = ServerRequestFactory::fromGlobals();
  372. $this->assertSame('/App/webroot/test.php', $request->getAttribute('base'));
  373. $this->assertSame('/App/webroot/', $request->getAttribute('webroot'));
  374. Configure::write('App.baseUrl', '/App/index.php');
  375. $request = ServerRequestFactory::fromGlobals();
  376. $this->assertSame('/App/index.php', $request->getAttribute('base'));
  377. $this->assertSame('/App/webroot/', $request->getAttribute('webroot'));
  378. Configure::write('App.baseUrl', '/CakeBB/App/webroot/index.php');
  379. $request = ServerRequestFactory::fromGlobals();
  380. $this->assertSame('/CakeBB/App/webroot/index.php', $request->getAttribute('base'));
  381. $this->assertSame('/CakeBB/App/webroot/', $request->getAttribute('webroot'));
  382. Configure::write('App.baseUrl', '/CakeBB/App/index.php');
  383. $request = ServerRequestFactory::fromGlobals();
  384. $this->assertSame('/CakeBB/App/index.php', $request->getAttribute('base'));
  385. $this->assertSame('/CakeBB/App/webroot/', $request->getAttribute('webroot'));
  386. Configure::write('App.baseUrl', '/CakeBB/index.php');
  387. $request = ServerRequestFactory::fromGlobals();
  388. $this->assertSame('/CakeBB/index.php', $request->getAttribute('base'));
  389. $this->assertSame('/CakeBB/webroot/', $request->getAttribute('webroot'));
  390. Configure::write('App.baseUrl', '/dbhauser/index.php');
  391. $request = ServerRequestFactory::fromGlobals([
  392. 'DOCUMENT_ROOT' => '/kunden/homepages/4/d181710652/htdocs/joomla',
  393. 'SCRIPT_FILENAME' => '/kunden/homepages/4/d181710652/htdocs/joomla/dbhauser/index.php',
  394. ]);
  395. $this->assertSame('/dbhauser/index.php', $request->getAttribute('base'));
  396. $this->assertSame('/dbhauser/webroot/', $request->getAttribute('webroot'));
  397. }
  398. /**
  399. * Test that a request with a . in the main GET parameter is filtered out.
  400. * PHP changes GET parameter keys containing dots to _.
  401. */
  402. public function testGetParamsWithDot(): void
  403. {
  404. $request = ServerRequestFactory::fromGlobals([
  405. 'PHP_SELF' => '/webroot/index.php',
  406. 'REQUEST_URI' => '/posts/index/add.add',
  407. ]);
  408. $this->assertSame('', $request->getAttribute('base'));
  409. $this->assertEquals([], $request->getQueryParams());
  410. $request = ServerRequestFactory::fromGlobals([
  411. 'PHP_SELF' => '/cake_dev/webroot/index.php',
  412. 'REQUEST_URI' => '/cake_dev/posts/index/add.add',
  413. ]);
  414. $this->assertSame('/cake_dev', $request->getAttribute('base'));
  415. $this->assertEquals([], $request->getQueryParams());
  416. }
  417. /**
  418. * Test that a request with urlencoded bits in the main GET parameter are filtered out.
  419. */
  420. public function testGetParamWithUrlencodedElement(): void
  421. {
  422. $request = ServerRequestFactory::fromGlobals([
  423. 'PHP_SELF' => '/webroot/index.php',
  424. 'REQUEST_URI' => '/posts/add/%E2%88%82%E2%88%82',
  425. ]);
  426. $this->assertSame('', $request->getAttribute('base'));
  427. $this->assertEquals([], $request->getQueryParams());
  428. $request = ServerRequestFactory::fromGlobals([
  429. 'PHP_SELF' => '/cake_dev/webroot/index.php',
  430. 'REQUEST_URI' => '/cake_dev/posts/add/%E2%88%82%E2%88%82',
  431. ]);
  432. $this->assertSame('/cake_dev', $request->getAttribute('base'));
  433. $this->assertEquals([], $request->getQueryParams());
  434. }
  435. /**
  436. * Generator for environment configurations
  437. *
  438. * @return array Environment array
  439. */
  440. public static function environmentGenerator(): array
  441. {
  442. return [
  443. [
  444. 'IIS - No rewrite base path',
  445. [
  446. 'App' => [
  447. 'base' => false,
  448. 'baseUrl' => '/index.php',
  449. 'dir' => 'TestApp',
  450. 'webroot' => 'webroot',
  451. ],
  452. 'SERVER' => [
  453. 'SCRIPT_NAME' => '/index.php',
  454. 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot',
  455. 'QUERY_STRING' => '',
  456. 'REQUEST_URI' => '/index.php',
  457. 'URL' => '/index.php',
  458. 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\index.php',
  459. 'ORIG_PATH_INFO' => '/index.php',
  460. 'PATH_INFO' => '',
  461. 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\index.php',
  462. 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot',
  463. 'PHP_SELF' => '/index.php',
  464. ],
  465. ],
  466. [
  467. 'base' => '/index.php',
  468. 'webroot' => '/webroot/',
  469. 'url' => '',
  470. ],
  471. ],
  472. [
  473. 'IIS - No rewrite with path, no PHP_SELF',
  474. [
  475. 'App' => [
  476. 'base' => false,
  477. 'baseUrl' => '/index.php?',
  478. 'dir' => 'TestApp',
  479. 'webroot' => 'webroot',
  480. ],
  481. 'SERVER' => [
  482. 'QUERY_STRING' => '/posts/add',
  483. 'REQUEST_URI' => '/index.php?/posts/add',
  484. 'PHP_SELF' => '',
  485. 'URL' => '/index.php?/posts/add',
  486. 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot',
  487. 'argv' => ['/posts/add'],
  488. 'argc' => 1,
  489. ],
  490. ],
  491. [
  492. 'url' => 'posts/add',
  493. 'base' => '/index.php?',
  494. 'webroot' => '/webroot/',
  495. ],
  496. ],
  497. [
  498. 'IIS - No rewrite sub dir 2',
  499. [
  500. 'App' => [
  501. 'base' => false,
  502. 'baseUrl' => '/site/index.php',
  503. 'dir' => 'TestApp',
  504. 'webroot' => 'webroot',
  505. ],
  506. 'SERVER' => [
  507. 'SCRIPT_NAME' => '/site/index.php',
  508. 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot',
  509. 'QUERY_STRING' => '',
  510. 'REQUEST_URI' => '/site/index.php',
  511. 'URL' => '/site/index.php',
  512. 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\site\\index.php',
  513. 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot',
  514. 'PHP_SELF' => '/site/index.php',
  515. 'argv' => [],
  516. 'argc' => 0,
  517. ],
  518. ],
  519. [
  520. 'url' => '',
  521. 'base' => '/site/index.php',
  522. 'webroot' => '/site/webroot/',
  523. ],
  524. ],
  525. [
  526. 'IIS - No rewrite sub dir 2 with path',
  527. [
  528. 'App' => [
  529. 'base' => false,
  530. 'baseUrl' => '/site/index.php',
  531. 'dir' => 'TestApp',
  532. 'webroot' => 'webroot',
  533. ],
  534. 'SERVER' => [
  535. 'SCRIPT_NAME' => '/site/index.php',
  536. 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot',
  537. 'QUERY_STRING' => '/posts/add',
  538. 'REQUEST_URI' => '/site/index.php/posts/add',
  539. 'URL' => '/site/index.php/posts/add',
  540. 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\site\\index.php',
  541. 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot',
  542. 'PHP_SELF' => '/site/index.php/posts/add',
  543. 'argv' => ['/posts/add'],
  544. 'argc' => 1,
  545. ],
  546. ],
  547. [
  548. 'url' => 'posts/add',
  549. 'base' => '/site/index.php',
  550. 'webroot' => '/site/webroot/',
  551. ],
  552. ],
  553. [
  554. 'Apache - No rewrite, document root set to webroot, requesting path',
  555. [
  556. 'App' => [
  557. 'base' => false,
  558. 'baseUrl' => '/index.php',
  559. 'dir' => 'TestApp',
  560. 'webroot' => 'webroot',
  561. ],
  562. 'SERVER' => [
  563. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents/site/App/webroot',
  564. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/App/webroot/index.php',
  565. 'QUERY_STRING' => '',
  566. 'REQUEST_URI' => '/index.php/posts/index',
  567. 'SCRIPT_NAME' => '/index.php',
  568. 'PATH_INFO' => '/posts/index',
  569. 'PHP_SELF' => '/index.php/posts/index',
  570. ],
  571. ],
  572. [
  573. 'url' => 'posts/index',
  574. 'base' => '/index.php',
  575. 'webroot' => '/',
  576. ],
  577. ],
  578. [
  579. 'Apache - No rewrite, document root set to webroot, requesting root',
  580. [
  581. 'App' => [
  582. 'base' => false,
  583. 'baseUrl' => '/index.php',
  584. 'dir' => 'TestApp',
  585. 'webroot' => 'webroot',
  586. ],
  587. 'SERVER' => [
  588. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents/site/App/webroot',
  589. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/App/webroot/index.php',
  590. 'QUERY_STRING' => '',
  591. 'REQUEST_URI' => '/index.php',
  592. 'SCRIPT_NAME' => '/index.php',
  593. 'PATH_INFO' => '',
  594. 'PHP_SELF' => '/index.php',
  595. ],
  596. ],
  597. [
  598. 'url' => '',
  599. 'base' => '/index.php',
  600. 'webroot' => '/',
  601. ],
  602. ],
  603. [
  604. 'Apache - No rewrite, document root set above top level cake dir, requesting path',
  605. [
  606. 'App' => [
  607. 'base' => false,
  608. 'baseUrl' => '/site/index.php',
  609. 'dir' => 'TestApp',
  610. 'webroot' => 'webroot',
  611. ],
  612. 'SERVER' => [
  613. 'SERVER_NAME' => 'localhost',
  614. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  615. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/index.php',
  616. 'REQUEST_URI' => '/site/index.php/posts/index',
  617. 'SCRIPT_NAME' => '/site/index.php',
  618. 'PATH_INFO' => '/posts/index',
  619. 'PHP_SELF' => '/site/index.php/posts/index',
  620. ],
  621. ],
  622. [
  623. 'url' => 'posts/index',
  624. 'base' => '/site/index.php',
  625. 'webroot' => '/site/webroot/',
  626. ],
  627. ],
  628. [
  629. 'Apache - No rewrite, document root set above top level cake dir, request root, no PATH_INFO',
  630. [
  631. 'App' => [
  632. 'base' => false,
  633. 'baseUrl' => '/site/index.php',
  634. 'dir' => 'TestApp',
  635. 'webroot' => 'webroot',
  636. ],
  637. 'SERVER' => [
  638. 'SERVER_NAME' => 'localhost',
  639. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  640. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/index.php',
  641. 'REQUEST_URI' => '/site/index.php/',
  642. 'SCRIPT_NAME' => '/site/index.php',
  643. 'PHP_SELF' => '/site/index.php/',
  644. ],
  645. ],
  646. [
  647. 'url' => '',
  648. 'base' => '/site/index.php',
  649. 'webroot' => '/site/webroot/',
  650. ],
  651. ],
  652. [
  653. 'Apache - No rewrite, document root set above top level cake dir, request path, with GET',
  654. [
  655. 'App' => [
  656. 'base' => false,
  657. 'baseUrl' => '/site/index.php',
  658. 'dir' => 'TestApp',
  659. 'webroot' => 'webroot',
  660. ],
  661. 'GET' => ['a' => 'b', 'c' => 'd'],
  662. 'SERVER' => [
  663. 'SERVER_NAME' => 'localhost',
  664. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  665. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/index.php',
  666. 'REQUEST_URI' => '/site/index.php/posts/index?a=b&c=d',
  667. 'SCRIPT_NAME' => '/site/index.php',
  668. 'PATH_INFO' => '/posts/index',
  669. 'PHP_SELF' => '/site/index.php/posts/index',
  670. 'QUERY_STRING' => 'a=b&c=d',
  671. ],
  672. ],
  673. [
  674. 'urlParams' => ['a' => 'b', 'c' => 'd'],
  675. 'url' => 'posts/index',
  676. 'base' => '/site/index.php',
  677. 'webroot' => '/site/webroot/',
  678. ],
  679. ],
  680. [
  681. 'Apache - w/rewrite, document root set above top level cake dir, request root, no PATH_INFO',
  682. [
  683. 'App' => [
  684. 'base' => false,
  685. 'baseUrl' => false,
  686. 'dir' => 'TestApp',
  687. 'webroot' => 'webroot',
  688. ],
  689. 'SERVER' => [
  690. 'SERVER_NAME' => 'localhost',
  691. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  692. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/index.php',
  693. 'REQUEST_URI' => '/site/',
  694. 'SCRIPT_NAME' => '/site/webroot/index.php',
  695. 'PHP_SELF' => '/site/webroot/index.php',
  696. ],
  697. ],
  698. [
  699. 'url' => '',
  700. 'base' => '/site',
  701. 'webroot' => '/site/',
  702. ],
  703. ],
  704. [
  705. 'Apache - w/rewrite, document root above top level cake dir, request root, no PATH_INFO/REQUEST_URI',
  706. [
  707. 'App' => [
  708. 'base' => false,
  709. 'baseUrl' => false,
  710. 'dir' => 'TestApp',
  711. 'webroot' => 'webroot',
  712. ],
  713. 'SERVER' => [
  714. 'SERVER_NAME' => 'localhost',
  715. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  716. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/index.php',
  717. 'SCRIPT_NAME' => '/site/webroot/index.php',
  718. 'PHP_SELF' => '/site/webroot/index.php',
  719. 'PATH_INFO' => null,
  720. 'REQUEST_URI' => null,
  721. ],
  722. ],
  723. [
  724. 'url' => '',
  725. 'base' => '/site',
  726. 'webroot' => '/site/',
  727. ],
  728. ],
  729. [
  730. 'Apache - w/rewrite, document root set to webroot, request root, no PATH_INFO/REQUEST_URI',
  731. [
  732. 'App' => [
  733. 'base' => false,
  734. 'baseUrl' => false,
  735. 'dir' => 'TestApp',
  736. 'webroot' => 'webroot',
  737. ],
  738. 'SERVER' => [
  739. 'SERVER_NAME' => 'localhost',
  740. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents/site/webroot',
  741. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/webroot/index.php',
  742. 'SCRIPT_NAME' => '/index.php',
  743. 'PHP_SELF' => '/index.php',
  744. 'PATH_INFO' => null,
  745. 'REQUEST_URI' => null,
  746. ],
  747. ],
  748. [
  749. 'url' => '',
  750. 'base' => '',
  751. 'webroot' => '/',
  752. ],
  753. ],
  754. [
  755. 'Apache - w/rewrite, document root set above top level cake dir, request root, absolute REQUEST_URI',
  756. [
  757. 'App' => [
  758. 'base' => false,
  759. 'baseUrl' => false,
  760. 'dir' => 'TestApp',
  761. 'webroot' => 'webroot',
  762. ],
  763. 'SERVER' => [
  764. 'SERVER_NAME' => 'localhost',
  765. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  766. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/index.php',
  767. 'REQUEST_URI' => '/site/posts/index',
  768. 'SCRIPT_NAME' => '/site/webroot/index.php',
  769. 'PHP_SELF' => '/site/webroot/index.php',
  770. ],
  771. ],
  772. [
  773. 'url' => 'posts/index',
  774. 'base' => '/site',
  775. 'webroot' => '/site/',
  776. ],
  777. ],
  778. [
  779. 'Nginx - w/rewrite, document root set to webroot, request root, no PATH_INFO',
  780. [
  781. 'App' => [
  782. 'base' => false,
  783. 'baseUrl' => false,
  784. 'dir' => 'TestApp',
  785. 'webroot' => 'webroot',
  786. ],
  787. 'SERVER' => [
  788. 'SERVER_NAME' => 'localhost',
  789. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents/site/webroot',
  790. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/webroot/index.php',
  791. 'SCRIPT_NAME' => '/index.php',
  792. 'PHP_SELF' => '/index.php',
  793. 'PATH_INFO' => null,
  794. 'REQUEST_URI' => '/posts/add',
  795. ],
  796. ],
  797. [
  798. 'url' => 'posts/add',
  799. 'base' => '',
  800. 'webroot' => '/',
  801. 'urlParams' => [],
  802. ],
  803. ],
  804. [
  805. 'Nginx - w/rewrite, document root set above top level cake dir, request root, no PATH_INFO, base parameter set',
  806. [
  807. 'App' => [
  808. 'base' => false,
  809. 'baseUrl' => false,
  810. 'dir' => 'app',
  811. 'webroot' => 'webroot',
  812. ],
  813. 'SERVER' => [
  814. 'SERVER_NAME' => 'localhost',
  815. 'DOCUMENT_ROOT' => '/Library/WebServer/Documents',
  816. 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/App/webroot/index.php',
  817. 'SCRIPT_NAME' => '/site/app/webroot/index.php',
  818. 'PHP_SELF' => '/site/webroot/index.php',
  819. 'PATH_INFO' => null,
  820. 'REQUEST_URI' => '/site/posts/add',
  821. ],
  822. ],
  823. [
  824. 'url' => 'posts/add',
  825. 'base' => '/site',
  826. 'webroot' => '/site/',
  827. 'urlParams' => [],
  828. ],
  829. ],
  830. ];
  831. }
  832. /**
  833. * Test environment detection
  834. *
  835. * @param string $name
  836. * @param array $data
  837. * @param array $expected
  838. */
  839. #[DataProvider('environmentGenerator')]
  840. public function testEnvironmentDetection($name, $data, $expected): void
  841. {
  842. if (isset($data['App'])) {
  843. Configure::write('App', $data['App']);
  844. }
  845. $request = ServerRequestFactory::fromGlobals(
  846. $data['SERVER'] ?? null,
  847. $data['GET'] ?? null
  848. );
  849. $uri = $request->getUri();
  850. $this->assertSame('/' . $expected['url'], $uri->getPath(), 'Uri->getPath() is incorrect');
  851. $this->assertEquals($expected['base'], $request->getAttribute('base'), 'base is incorrect');
  852. $this->assertEquals($expected['webroot'], $request->getAttribute('webroot'), 'webroot is incorrect');
  853. if (isset($expected['urlParams'])) {
  854. $this->assertEquals($expected['urlParams'], $request->getQueryParams(), 'GET param mismatch');
  855. }
  856. }
  857. public function testFormUrlEncodedBodyParsing(): void
  858. {
  859. $data = [
  860. 'Article' => ['title'],
  861. ];
  862. $request = ServerRequestFactory::fromGlobals([
  863. 'REQUEST_METHOD' => 'PUT',
  864. 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=UTF-8',
  865. 'CAKEPHP_INPUT' => 'Article[]=title',
  866. ]);
  867. $this->assertEquals($data, $request->getData());
  868. $data = ['one' => 1, 'two' => 'three'];
  869. $request = ServerRequestFactory::fromGlobals([
  870. 'REQUEST_METHOD' => 'PUT',
  871. 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=UTF-8',
  872. 'CAKEPHP_INPUT' => 'one=1&two=three',
  873. ]);
  874. $this->assertEquals($data, $request->getData());
  875. $request = ServerRequestFactory::fromGlobals([
  876. 'REQUEST_METHOD' => 'DELETE',
  877. 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=UTF-8',
  878. 'CAKEPHP_INPUT' => 'Article[title]=Testing&action=update',
  879. ]);
  880. $expected = [
  881. 'Article' => ['title' => 'Testing'],
  882. 'action' => 'update',
  883. ];
  884. $this->assertEquals($expected, $request->getData());
  885. $data = [
  886. 'Article' => ['title'],
  887. 'Tag' => ['Tag' => [1, 2]],
  888. ];
  889. $request = ServerRequestFactory::fromGlobals([
  890. 'REQUEST_METHOD' => 'PATCH',
  891. 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=UTF-8',
  892. 'CAKEPHP_INPUT' => 'Article[]=title&Tag[Tag][]=1&Tag[Tag][]=2',
  893. ]);
  894. $this->assertEquals($data, $request->getData());
  895. }
  896. /**
  897. * Test method overrides coming in from POST data.
  898. */
  899. public function testMethodOverrides(): void
  900. {
  901. $post = ['_method' => 'POST'];
  902. $request = ServerRequestFactory::fromGlobals([], [], $post);
  903. $this->assertSame('POST', $request->getEnv('REQUEST_METHOD'));
  904. $post = ['_method' => 'DELETE'];
  905. $request = ServerRequestFactory::fromGlobals([], [], $post);
  906. $this->assertSame('DELETE', $request->getEnv('REQUEST_METHOD'));
  907. $request = ServerRequestFactory::fromGlobals(['HTTP_X_HTTP_METHOD_OVERRIDE' => 'PUT']);
  908. $this->assertSame('PUT', $request->getEnv('REQUEST_METHOD'));
  909. $request = ServerRequestFactory::fromGlobals(
  910. ['REQUEST_METHOD' => 'POST'],
  911. [],
  912. ['_method' => 'PUT']
  913. );
  914. $this->assertSame('PUT', $request->getEnv('REQUEST_METHOD'));
  915. $this->assertSame('POST', $request->getEnv('ORIGINAL_REQUEST_METHOD'));
  916. }
  917. /**
  918. * Test getServerParams
  919. */
  920. public function testGetServerParams(): void
  921. {
  922. $vars = [
  923. 'REQUEST_METHOD' => 'PUT',
  924. 'HTTPS' => 'on',
  925. ];
  926. $request = ServerRequestFactory::fromGlobals($vars);
  927. $expected = $vars + [
  928. 'CONTENT_TYPE' => null,
  929. 'HTTP_CONTENT_TYPE' => null,
  930. 'ORIGINAL_REQUEST_METHOD' => 'PUT',
  931. 'HTTP_HOST' => 'localhost',
  932. ];
  933. $this->assertSame($expected, $request->getServerParams());
  934. }
  935. /**
  936. * Tests that overriding the method to GET will clean all request
  937. * data, to better simulate a GET request.
  938. */
  939. public function testMethodOverrideEmptyParsedBody(): void
  940. {
  941. $body = ['_method' => 'GET', 'foo' => 'bar'];
  942. $request = ServerRequestFactory::fromGlobals(
  943. ['REQUEST_METHOD' => 'POST'],
  944. [],
  945. $body
  946. );
  947. $this->assertEmpty($request->getParsedBody());
  948. $request = ServerRequestFactory::fromGlobals(
  949. [
  950. 'REQUEST_METHOD' => 'POST',
  951. 'HTTP_X_HTTP_METHOD_OVERRIDE' => 'GET',
  952. ],
  953. [],
  954. ['foo' => 'bar']
  955. );
  956. $this->assertEmpty($request->getParsedBody());
  957. }
  958. /**
  959. * Tests the default file upload merging behavior.
  960. */
  961. public function testFromGlobalsWithFiles(): void
  962. {
  963. $this->assertNull(Configure::read('App.uploadedFilesAsObjects'));
  964. $files = [
  965. 'file' => [
  966. 'name' => 'file.txt',
  967. 'type' => 'text/plain',
  968. 'tmp_name' => __FILE__,
  969. 'error' => 0,
  970. 'size' => 1234,
  971. ],
  972. ];
  973. $request = ServerRequestFactory::fromGlobals(null, null, null, null, $files);
  974. /** @var \Laminas\Diactoros\UploadedFile $expected */
  975. $expected = $request->getData('file');
  976. $this->assertSame($files['file']['size'], $expected->getSize());
  977. $this->assertSame($files['file']['error'], $expected->getError());
  978. $this->assertSame($files['file']['name'], $expected->getClientFilename());
  979. $this->assertSame($files['file']['type'], $expected->getClientMediaType());
  980. }
  981. /**
  982. * Test processing files with `file` field names.
  983. */
  984. public function testFilesNested(): void
  985. {
  986. $files = [
  987. 'image_main' => [
  988. 'name' => ['file' => 'born on.txt'],
  989. 'type' => ['file' => 'text/plain'],
  990. 'tmp_name' => ['file' => __FILE__],
  991. 'error' => ['file' => 0],
  992. 'size' => ['file' => 17178],
  993. ],
  994. 0 => [
  995. 'name' => ['image' => 'scratch.text'],
  996. 'type' => ['image' => 'text/plain'],
  997. 'tmp_name' => ['image' => __FILE__],
  998. 'error' => ['image' => 0],
  999. 'size' => ['image' => 1490],
  1000. ],
  1001. 'pictures' => [
  1002. 'name' => [
  1003. 0 => ['file' => 'a-file.png'],
  1004. 1 => ['file' => 'a-moose.png'],
  1005. ],
  1006. 'type' => [
  1007. 0 => ['file' => 'image/png'],
  1008. 1 => ['file' => 'image/jpg'],
  1009. ],
  1010. 'tmp_name' => [
  1011. 0 => ['file' => __FILE__],
  1012. 1 => ['file' => __FILE__],
  1013. ],
  1014. 'error' => [
  1015. 0 => ['file' => 0],
  1016. 1 => ['file' => 0],
  1017. ],
  1018. 'size' => [
  1019. 0 => ['file' => 17188],
  1020. 1 => ['file' => 2010],
  1021. ],
  1022. ],
  1023. ];
  1024. $post = [
  1025. 'pictures' => [
  1026. 0 => ['name' => 'A cat'],
  1027. 1 => ['name' => 'A moose'],
  1028. ],
  1029. 0 => [
  1030. 'name' => 'A dog',
  1031. ],
  1032. ];
  1033. $request = ServerRequestFactory::fromGlobals(null, null, $post, null, $files);
  1034. $expected = [
  1035. 'image_main' => [
  1036. 'file' => new UploadedFile(
  1037. __FILE__,
  1038. 17178,
  1039. 0,
  1040. 'born on.txt',
  1041. 'text/plain'
  1042. ),
  1043. ],
  1044. 'pictures' => [
  1045. 0 => [
  1046. 'name' => 'A cat',
  1047. 'file' => new UploadedFile(
  1048. __FILE__,
  1049. 17188,
  1050. 0,
  1051. 'a-file.png',
  1052. 'image/png'
  1053. ),
  1054. ],
  1055. 1 => [
  1056. 'name' => 'A moose',
  1057. 'file' => new UploadedFile(
  1058. __FILE__,
  1059. 2010,
  1060. 0,
  1061. 'a-moose.png',
  1062. 'image/jpg'
  1063. ),
  1064. ],
  1065. ],
  1066. 0 => [
  1067. 'name' => 'A dog',
  1068. 'image' => new UploadedFile(
  1069. __FILE__,
  1070. 1490,
  1071. 0,
  1072. 'scratch.text',
  1073. 'text/plain'
  1074. ),
  1075. ],
  1076. ];
  1077. $this->assertEquals($expected, $request->getData());
  1078. $uploads = $request->getUploadedFiles();
  1079. $this->assertCount(3, $uploads);
  1080. $this->assertArrayHasKey(0, $uploads);
  1081. $this->assertSame('scratch.text', $uploads[0]['image']->getClientFilename());
  1082. $this->assertArrayHasKey('pictures', $uploads);
  1083. $this->assertSame('a-file.png', $uploads['pictures'][0]['file']->getClientFilename());
  1084. $this->assertSame('a-moose.png', $uploads['pictures'][1]['file']->getClientFilename());
  1085. $this->assertArrayHasKey('image_main', $uploads);
  1086. $this->assertSame('born on.txt', $uploads['image_main']['file']->getClientFilename());
  1087. }
  1088. /**
  1089. * Test processing a file input with no .'s in it.
  1090. */
  1091. public function testFilesFlat(): void
  1092. {
  1093. $files = [
  1094. 'birth_cert' => [
  1095. 'name' => 'born on.txt',
  1096. 'type' => 'application/octet-stream',
  1097. 'tmp_name' => __FILE__,
  1098. 'error' => 0,
  1099. 'size' => 123,
  1100. ],
  1101. ];
  1102. $request = ServerRequestFactory::fromGlobals([], [], [], [], $files);
  1103. $this->assertInstanceOf(UploadedFileInterface::class, $request->getData()['birth_cert']);
  1104. $uploads = $request->getUploadedFiles();
  1105. $this->assertCount(1, $uploads);
  1106. $this->assertArrayHasKey('birth_cert', $uploads);
  1107. $this->assertSame('born on.txt', $uploads['birth_cert']->getClientFilename());
  1108. $this->assertSame(0, $uploads['birth_cert']->getError());
  1109. $this->assertSame('application/octet-stream', $uploads['birth_cert']->getClientMediaType());
  1110. $this->assertSame(123, $uploads['birth_cert']->getSize());
  1111. }
  1112. /**
  1113. * Test that files in the 0th index work.
  1114. */
  1115. public function testFilesZeroithIndex(): void
  1116. {
  1117. $files = [
  1118. 0 => [
  1119. 'name' => 'cake_sqlserver_patch.patch',
  1120. 'type' => 'text/plain',
  1121. 'tmp_name' => __FILE__,
  1122. 'error' => 0,
  1123. 'size' => 6271,
  1124. ],
  1125. ];
  1126. $request = ServerRequestFactory::fromGlobals([], [], [], [], $files);
  1127. $this->assertInstanceOf(UploadedFileInterface::class, $request->getData()[0]);
  1128. $uploads = $request->getUploadedFiles();
  1129. $this->assertCount(1, $uploads);
  1130. $this->assertSame($files[0]['name'], $uploads[0]->getClientFilename());
  1131. }
  1132. /**
  1133. * Tests that file uploads are merged into the post data as objects instead of as arrays.
  1134. */
  1135. public function testFilesAsObjectsInRequestData(): void
  1136. {
  1137. $files = [
  1138. 'flat' => [
  1139. 'name' => 'flat.txt',
  1140. 'type' => 'text/plain',
  1141. 'tmp_name' => __FILE__,
  1142. 'error' => 0,
  1143. 'size' => 1,
  1144. ],
  1145. 'nested' => [
  1146. 'name' => ['file' => 'nested.txt'],
  1147. 'type' => ['file' => 'text/plain'],
  1148. 'tmp_name' => ['file' => __FILE__],
  1149. 'error' => ['file' => 0],
  1150. 'size' => ['file' => 12],
  1151. ],
  1152. 0 => [
  1153. 'name' => 'numeric.txt',
  1154. 'type' => 'text/plain',
  1155. 'tmp_name' => __FILE__,
  1156. 'error' => 0,
  1157. 'size' => 123,
  1158. ],
  1159. 1 => [
  1160. 'name' => ['file' => 'numeric-nested.txt'],
  1161. 'type' => ['file' => 'text/plain'],
  1162. 'tmp_name' => ['file' => __FILE__],
  1163. 'error' => ['file' => 0],
  1164. 'size' => ['file' => 1234],
  1165. ],
  1166. 'deep' => [
  1167. 'name' => [
  1168. 0 => ['file' => 'deep-1.txt'],
  1169. 1 => ['file' => 'deep-2.txt'],
  1170. ],
  1171. 'type' => [
  1172. 0 => ['file' => 'text/plain'],
  1173. 1 => ['file' => 'text/plain'],
  1174. ],
  1175. 'tmp_name' => [
  1176. 0 => ['file' => __FILE__],
  1177. 1 => ['file' => __FILE__],
  1178. ],
  1179. 'error' => [
  1180. 0 => ['file' => 0],
  1181. 1 => ['file' => 0],
  1182. ],
  1183. 'size' => [
  1184. 0 => ['file' => 12345],
  1185. 1 => ['file' => 123456],
  1186. ],
  1187. ],
  1188. ];
  1189. $post = [
  1190. 'flat' => ['existing'],
  1191. 'nested' => [
  1192. 'name' => 'nested',
  1193. 'file' => ['existing'],
  1194. ],
  1195. 'deep' => [
  1196. 0 => [
  1197. 'name' => 'deep 1',
  1198. 'file' => ['existing'],
  1199. ],
  1200. 1 => [
  1201. 'name' => 'deep 2',
  1202. ],
  1203. ],
  1204. 1 => [
  1205. 'name' => 'numeric nested',
  1206. ],
  1207. ];
  1208. $expected = [
  1209. 'flat' => new UploadedFile(
  1210. __FILE__,
  1211. 1,
  1212. 0,
  1213. 'flat.txt',
  1214. 'text/plain'
  1215. ),
  1216. 'nested' => [
  1217. 'name' => 'nested',
  1218. 'file' => new UploadedFile(
  1219. __FILE__,
  1220. 12,
  1221. 0,
  1222. 'nested.txt',
  1223. 'text/plain'
  1224. ),
  1225. ],
  1226. 'deep' => [
  1227. 0 => [
  1228. 'name' => 'deep 1',
  1229. 'file' => new UploadedFile(
  1230. __FILE__,
  1231. 12345,
  1232. 0,
  1233. 'deep-1.txt',
  1234. 'text/plain'
  1235. ),
  1236. ],
  1237. 1 => [
  1238. 'name' => 'deep 2',
  1239. 'file' => new UploadedFile(
  1240. __FILE__,
  1241. 123456,
  1242. 0,
  1243. 'deep-2.txt',
  1244. 'text/plain'
  1245. ),
  1246. ],
  1247. ],
  1248. 0 => new UploadedFile(
  1249. __FILE__,
  1250. 123,
  1251. 0,
  1252. 'numeric.txt',
  1253. 'text/plain'
  1254. ),
  1255. 1 => [
  1256. 'name' => 'numeric nested',
  1257. 'file' => new UploadedFile(
  1258. __FILE__,
  1259. 1234,
  1260. 0,
  1261. 'numeric-nested.txt',
  1262. 'text/plain'
  1263. ),
  1264. ],
  1265. ];
  1266. $request = ServerRequestFactory::fromGlobals([], [], $post, [], $files);
  1267. $this->assertEquals($expected, $request->getData());
  1268. }
  1269. /**
  1270. * Test passing invalid files list structure.
  1271. */
  1272. public function testFilesWithInvalidStructure(): void
  1273. {
  1274. $this->expectException(InvalidArgumentException::class);
  1275. $this->expectExceptionMessage('Invalid value in files specification');
  1276. ServerRequestFactory::fromGlobals([], [], [], [], [
  1277. [
  1278. 'invalid' => [
  1279. 'data',
  1280. ],
  1281. ],
  1282. ]);
  1283. }
  1284. public function testCreateServerRequest(): void
  1285. {
  1286. $factory = new ServerRequestFactory();
  1287. $request = $factory->createServerRequest('GET', 'https://cakephp.org/team', ['foo' => 'bar']);
  1288. $this->assertInstanceOf(ServerRequest::class, $request);
  1289. $this->assertSame('GET', $request->getMethod());
  1290. $this->assertSame('/team', $request->getRequestTarget());
  1291. $expected = ['foo' => 'bar', 'REQUEST_METHOD' => 'GET'];
  1292. $this->assertEquals($expected, $request->getServerParams());
  1293. }
  1294. }