From c70b617a14f564a95027cf3779291f5d6dd5426a Mon Sep 17 00:00:00 2001 From: rahul05ranjan Date: Fri, 25 Sep 2026 17:01:44 +0530 Subject: [PATCH 1/6] fix: sync \ after \ is updated in SiteURIFactory SiteURIFactory updates \ (and \['QUERY_STRING']) when it detects the route path, but \ was left stale. Since PHP populates \ only once at the start of the request, getVar() (which reads \) returned outdated values, breaking \->withRequest() for GET parameters. Add Superglobals::syncRequest() to rebuild \ from \, \, and \ according to request_order/variables_order, and call it from SiteURIFactory after setGetArray(). Fixes #9872 --- system/HTTP/SiteURIFactory.php | 4 +-- system/Superglobals.php | 28 +++++++++++++++++++ .../SiteURIFactoryDetectRoutePathTest.php | 1 + tests/system/SuperglobalsTest.php | 28 +++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/system/HTTP/SiteURIFactory.php b/system/HTTP/SiteURIFactory.php index 11dccce6c540..a06a944d1331 100644 --- a/system/HTTP/SiteURIFactory.php +++ b/system/HTTP/SiteURIFactory.php @@ -171,7 +171,7 @@ private function parseRequestURI(): string // Update our global GET for values likely to have been changed parse_str($this->superglobals->server('QUERY_STRING'), $get); - $this->superglobals->setGetArray($get); + $this->superglobals->setGetArray($get)->syncRequest(); return URI::removeDotSegments($path); } @@ -203,7 +203,7 @@ private function parseQueryString(): string // Update our global GET for values likely to have been changed parse_str($this->superglobals->server('QUERY_STRING'), $get); - $this->superglobals->setGetArray($get); + $this->superglobals->setGetArray($get)->syncRequest(); return URI::removeDotSegments($path); } diff --git a/system/Superglobals.php b/system/Superglobals.php index ac0ea289bd77..407e98a90b9b 100644 --- a/system/Superglobals.php +++ b/system/Superglobals.php @@ -387,6 +387,34 @@ public function setRequestArray(array $array): self return $this; } + /** + * Rebuilds $_REQUEST from $_GET, $_POST, and $_COOKIE according to the + * `request_order` (or `variables_order`) ini setting. + * + * PHP populates $_REQUEST only once at the start of the request. When + * $_GET is modified later (e.g. by SiteURIFactory), $_REQUEST becomes + * stale. This method re-synchronizes $_REQUEST with the current values. + * + * @return self + */ + public function syncRequest(): self + { + $requestOrder = ini_get('request_order') ?: ini_get('variables_order') ?: 'GP'; + + $request = []; + + foreach (str_split($requestOrder) as $type) { + match ($type) { + 'G' => $request = array_merge($request, $this->get), + 'P' => $request = array_merge($request, $this->post), + 'C' => $request = array_merge($request, $this->cookie), + default => null, + }; + } + + return $this->setRequestArray($request); + } + /** * Get all $_FILES values. * diff --git a/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php b/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php index 4bc29fbecc4d..314fcf77ee80 100644 --- a/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php +++ b/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php @@ -256,6 +256,7 @@ public function testQueryStringWithQueryString(): void $this->assertSame($expected, $factory->detectRoutePath('QUERY_STRING')); $this->assertSame('code=good', $_SERVER['QUERY_STRING']); // @phpstan-ignore codeigniter.superglobalsOffsetAccess (checks the live superglobal, not the snapshot service) $this->assertSame(['code' => 'good'], $_GET); + $this->assertSame(['code' => 'good'], $_REQUEST); // @phpstan-ignore codeigniter.superglobalsOffsetAccess (checks the live superglobal, not the snapshot service) } public function testQueryStringEmpty(): void diff --git a/tests/system/SuperglobalsTest.php b/tests/system/SuperglobalsTest.php index e4b8b23b2b28..3007cf74ad2f 100644 --- a/tests/system/SuperglobalsTest.php +++ b/tests/system/SuperglobalsTest.php @@ -302,6 +302,34 @@ public function testRequestSetArray(): void $this->assertSame($data, $_REQUEST); } + public function testSyncRequestRebuildsRequestFromGetPostCookie(): void + { + $this->superglobals->setGetArray(['get_key' => 'get_value']); + $this->superglobals->setPostArray(['post_key' => 'post_value']); + $this->superglobals->setCookieArray(['cookie_key' => 'cookie_value']); + + $this->superglobals->syncRequest(); + + $this->assertSame('get_value', $this->superglobals->request('get_key')); + $this->assertSame('post_value', $this->superglobals->request('post_key')); + $this->assertSame('cookie_value', $this->superglobals->request('cookie_key')); + $this->assertSame('get_value', $_REQUEST['get_key']); // @phpstan-ignore codeigniter.superglobalsOffsetAccess (checks the live superglobal, not the snapshot service) + } + + public function testSyncRequestReflectsGetChanges(): void + { + $this->superglobals->setGetArray(['key' => 'old']); + $this->superglobals->syncRequest(); + + $this->assertSame('old', $this->superglobals->request('key')); + + // Simulate SiteURIFactory updating $_GET after the request started. + $this->superglobals->setGetArray(['key' => 'new']); + $this->superglobals->syncRequest(); + + $this->assertSame('new', $this->superglobals->request('key')); + } + // $_FILES tests public function testFilesGetArray(): void { From 4034adf950de4a5e8c84b2f880ebfe045c905b4a Mon Sep 17 00:00:00 2001 From: rahul05ranjan Date: Sat, 26 Sep 2026 18:06:04 +0530 Subject: [PATCH 2/6] fix: make getVar() read merged superglobals instead of stale $_REQUEST $_REQUEST is populated only once at the start of the request, so it becomes stale when SiteURIFactory updates $_GET during URI parsing. getVar() previously read the stale $_REQUEST, breaking withRequest() for GET parameters. Instead of mutating $_REQUEST (the approach rejected in #10205), this change makes getVar() return a merged view of $_GET, $_POST, and $_COOKIE according to request_order, leaving $_REQUEST untouched. - Add Superglobals::getRequestData() returning the merged data. - Extract RequestTrait::fetchFromArray() to reuse the filtering logic. - Update getVar() to use getRequestData() + fetchFromArray(). - Revert the SiteURIFactory syncRequest() calls. - Update tests. Fixes #9872 --- system/HTTP/IncomingRequest.php | 14 +++++++--- system/HTTP/RequestTrait.php | 26 +++++++++++++++---- system/HTTP/SiteURIFactory.php | 4 +-- system/Superglobals.php | 14 +++++----- tests/system/HTTP/IncomingRequestTest.php | 6 ++--- .../SiteURIFactoryDetectRoutePathTest.php | 1 - tests/system/SuperglobalsTest.php | 19 ++++++-------- tests/system/Validation/ValidationTest.php | 6 ++--- 8 files changed, 55 insertions(+), 35 deletions(-) diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index cc66e35f4ddc..b9fb942ae23a 100644 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -368,9 +368,10 @@ public function getDefaultLocale(): string } /** - * Fetch an item from JSON input stream with fallback to $_REQUEST object. This is the simplest way - * to grab data from the request object and can be used in lieu of the - * other get* methods in most cases. + * Fetch an item from JSON input stream with fallback to the merged + * $_GET, $_POST, and $_COOKIE data. This is the simplest way to grab data + * from the request object and can be used in lieu of the other get* + * methods in most cases. * * @param list|string|null $index * @param int|null $filter Filter constant @@ -387,7 +388,12 @@ public function getVar($index = null, $filter = null, $flags = null) return $this->getJsonVar($index, false, $filter, $flags); } - return $this->fetchGlobal('request', $index, $filter, $flags); + // $_REQUEST is populated only once at the start of the request, so it + // can become stale when $_GET is modified later (e.g. by SiteURIFactory). + // Merge the current superglobals instead of reading the stale $_REQUEST. + $data = service('superglobals')->getRequestData(); + + return $this->fetchFromArray($data, $index, $filter, $flags); } /** diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index 973757c8559e..1d10d18b3658 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -291,6 +291,22 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f $this->populateGlobals($name); } + return $this->fetchFromArray($this->globals[$name], $index, $filter, $flags); + } + + /** + * Fetches one or more items from an array, applying the same filtering + * and index resolution as fetchGlobal(). + * + * @param array $data + * @param int|list|string|null $index + * @param int|null $filter Filter constant + * @param array|int|null $flags Options + * + * @return mixed + */ + private function fetchFromArray(array $data, $index = null, ?int $filter = null, $flags = null) + { // Null filters cause null values to return. $filter ??= FILTER_UNSAFE_RAW; $flags = is_array($flags) ? $flags : (is_numeric($flags) ? (int) $flags : 0); @@ -299,9 +315,9 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f if ($index === null) { $values = []; - foreach ($this->globals[$name] as $key => $value) { + foreach ($data as $key => $value) { $values[$key] = is_array($value) - ? $this->fetchGlobal($name, $key, $filter, $flags) + ? $this->fetchFromArray($data, $key, $filter, $flags) : filter_var($value, $filter, $flags); } @@ -313,7 +329,7 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f $output = []; foreach ($index as $key) { - $output[$key] = $this->fetchGlobal($name, $key, $filter, $flags); + $output[$key] = $this->fetchFromArray($data, $key, $filter, $flags); } return $output; @@ -321,7 +337,7 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f // Does the index contain array notation? if (is_string($index) && ($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) { - $value = $this->globals[$name]; + $value = $data; for ($i = 0; $i < $count; $i++) { $key = trim($matches[0][$i], '[]'); @@ -338,7 +354,7 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f } } - $value ??= $this->globals[$name][$index] ?? null; + $value ??= $data[$index] ?? null; if (is_array($value) && ( diff --git a/system/HTTP/SiteURIFactory.php b/system/HTTP/SiteURIFactory.php index a06a944d1331..11dccce6c540 100644 --- a/system/HTTP/SiteURIFactory.php +++ b/system/HTTP/SiteURIFactory.php @@ -171,7 +171,7 @@ private function parseRequestURI(): string // Update our global GET for values likely to have been changed parse_str($this->superglobals->server('QUERY_STRING'), $get); - $this->superglobals->setGetArray($get)->syncRequest(); + $this->superglobals->setGetArray($get); return URI::removeDotSegments($path); } @@ -203,7 +203,7 @@ private function parseQueryString(): string // Update our global GET for values likely to have been changed parse_str($this->superglobals->server('QUERY_STRING'), $get); - $this->superglobals->setGetArray($get)->syncRequest(); + $this->superglobals->setGetArray($get); return URI::removeDotSegments($path); } diff --git a/system/Superglobals.php b/system/Superglobals.php index 407e98a90b9b..c8bc5ada863c 100644 --- a/system/Superglobals.php +++ b/system/Superglobals.php @@ -388,16 +388,18 @@ public function setRequestArray(array $array): self } /** - * Rebuilds $_REQUEST from $_GET, $_POST, and $_COOKIE according to the - * `request_order` (or `variables_order`) ini setting. + * Returns the merged $_GET, $_POST, and $_COOKIE data according to the + * `request_order` (or `variables_order`) ini setting, without mutating + * $_REQUEST. * * PHP populates $_REQUEST only once at the start of the request. When * $_GET is modified later (e.g. by SiteURIFactory), $_REQUEST becomes - * stale. This method re-synchronizes $_REQUEST with the current values. + * stale. This method returns the current merged values so callers can + * read up-to-date request data without relying on the stale $_REQUEST. * - * @return self + * @return array */ - public function syncRequest(): self + public function getRequestData(): array { $requestOrder = ini_get('request_order') ?: ini_get('variables_order') ?: 'GP'; @@ -412,7 +414,7 @@ public function syncRequest(): self }; } - return $this->setRequestArray($request); + return $request; } /** diff --git a/tests/system/HTTP/IncomingRequestTest.php b/tests/system/HTTP/IncomingRequestTest.php index 464b46e2ce2c..add88bfaef3a 100644 --- a/tests/system/HTTP/IncomingRequestTest.php +++ b/tests/system/HTTP/IncomingRequestTest.php @@ -70,7 +70,7 @@ private function createRequest(?App $config = null, false|string|null $body = nu public function testCanGrabRequestVars(): void { - service('superglobals')->setRequest('TEST', '5'); + service('superglobals')->setGet('TEST', '5'); $this->assertSame('5', $this->request->getVar('TEST')); $this->assertNull($this->request->getVar('TESTY')); @@ -525,8 +525,8 @@ public function testGetVarWorksWithJsonAndGetParams(): void $config->baseURL = 'http://example.com/'; // GET method - service('superglobals')->setRequest('foo', 'bar'); - service('superglobals')->setRequest('fizz', 'buzz'); + service('superglobals')->setGet('foo', 'bar'); + service('superglobals')->setGet('fizz', 'buzz'); $request = $this->createRequest($config); $request = $request->withMethod('GET'); diff --git a/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php b/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php index 314fcf77ee80..4bc29fbecc4d 100644 --- a/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php +++ b/tests/system/HTTP/SiteURIFactoryDetectRoutePathTest.php @@ -256,7 +256,6 @@ public function testQueryStringWithQueryString(): void $this->assertSame($expected, $factory->detectRoutePath('QUERY_STRING')); $this->assertSame('code=good', $_SERVER['QUERY_STRING']); // @phpstan-ignore codeigniter.superglobalsOffsetAccess (checks the live superglobal, not the snapshot service) $this->assertSame(['code' => 'good'], $_GET); - $this->assertSame(['code' => 'good'], $_REQUEST); // @phpstan-ignore codeigniter.superglobalsOffsetAccess (checks the live superglobal, not the snapshot service) } public function testQueryStringEmpty(): void diff --git a/tests/system/SuperglobalsTest.php b/tests/system/SuperglobalsTest.php index 3007cf74ad2f..7eca2beb64fc 100644 --- a/tests/system/SuperglobalsTest.php +++ b/tests/system/SuperglobalsTest.php @@ -302,32 +302,29 @@ public function testRequestSetArray(): void $this->assertSame($data, $_REQUEST); } - public function testSyncRequestRebuildsRequestFromGetPostCookie(): void + public function testGetRequestDataMergesGetPostCookie(): void { $this->superglobals->setGetArray(['get_key' => 'get_value']); $this->superglobals->setPostArray(['post_key' => 'post_value']); $this->superglobals->setCookieArray(['cookie_key' => 'cookie_value']); - $this->superglobals->syncRequest(); + $data = $this->superglobals->getRequestData(); - $this->assertSame('get_value', $this->superglobals->request('get_key')); - $this->assertSame('post_value', $this->superglobals->request('post_key')); - $this->assertSame('cookie_value', $this->superglobals->request('cookie_key')); - $this->assertSame('get_value', $_REQUEST['get_key']); // @phpstan-ignore codeigniter.superglobalsOffsetAccess (checks the live superglobal, not the snapshot service) + $this->assertSame('get_value', $data['get_key']); + $this->assertSame('post_value', $data['post_key']); + $this->assertSame('cookie_value', $data['cookie_key']); } - public function testSyncRequestReflectsGetChanges(): void + public function testGetRequestDataReflectsGetChanges(): void { $this->superglobals->setGetArray(['key' => 'old']); - $this->superglobals->syncRequest(); - $this->assertSame('old', $this->superglobals->request('key')); + $this->assertSame('old', $this->superglobals->getRequestData()['key']); // Simulate SiteURIFactory updating $_GET after the request started. $this->superglobals->setGetArray(['key' => 'new']); - $this->superglobals->syncRequest(); - $this->assertSame('new', $this->superglobals->request('key')); + $this->assertSame('new', $this->superglobals->getRequestData()['key']); } // $_FILES tests diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 49baeaba0c79..70adb1a16d18 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1289,7 +1289,7 @@ public function testRulesForSingleRuleWithAsteriskWillReturnNoError(): void $config = new App(); $config->baseURL = 'http://example.com/'; - service('superglobals')->setRequestArray([ + service('superglobals')->setPostArray([ 'id_user' => [ 1, 3, @@ -1316,7 +1316,7 @@ public function testRulesForSingleRuleWithAsteriskWillReturnError(): void $config = new App(); $config->baseURL = 'http://example.com/'; - service('superglobals')->setRequestArray([ + service('superglobals')->setPostArray([ 'id_user' => [ '1dfd', 3, @@ -1366,7 +1366,7 @@ public function testRulesForSingleRuleWithSingleValue(): void $config = new App(); $config->baseURL = 'http://example.com/'; - service('superglobals')->setRequestArray([ + service('superglobals')->setPostArray([ 'id_user' => 'gh', ]); From a8fddea5871cb6dc324d28b4e866afdd96955018 Mon Sep 17 00:00:00 2001 From: rahul05ranjan Date: Sat, 26 Sep 2026 18:49:23 +0530 Subject: [PATCH 3/6] fix: make fetchFromArray protected and avoid short ternary - fetchFromArray() must be protected so IncomingRequest (a subclass) can call it. - Replace the short ternary in getRequestData() with explicit checks to satisfy the static analysis rules. - Drop the cookie assertion from the test since request_order defaults to GP (no cookies). --- system/HTTP/RequestTrait.php | 2 +- system/Superglobals.php | 8 +++++++- tests/system/SuperglobalsTest.php | 4 +--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index 1d10d18b3658..1dd635be0ac8 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -305,7 +305,7 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f * * @return mixed */ - private function fetchFromArray(array $data, $index = null, ?int $filter = null, $flags = null) + protected function fetchFromArray(array $data, $index = null, ?int $filter = null, $flags = null) { // Null filters cause null values to return. $filter ??= FILTER_UNSAFE_RAW; diff --git a/system/Superglobals.php b/system/Superglobals.php index c8bc5ada863c..3d7abda0680c 100644 --- a/system/Superglobals.php +++ b/system/Superglobals.php @@ -401,7 +401,13 @@ public function setRequestArray(array $array): self */ public function getRequestData(): array { - $requestOrder = ini_get('request_order') ?: ini_get('variables_order') ?: 'GP'; + $requestOrder = ini_get('request_order'); + if ($requestOrder === false || $requestOrder === '') { + $requestOrder = ini_get('variables_order'); + } + if ($requestOrder === false || $requestOrder === '') { + $requestOrder = 'GP'; + } $request = []; diff --git a/tests/system/SuperglobalsTest.php b/tests/system/SuperglobalsTest.php index 7eca2beb64fc..547493878dab 100644 --- a/tests/system/SuperglobalsTest.php +++ b/tests/system/SuperglobalsTest.php @@ -302,17 +302,15 @@ public function testRequestSetArray(): void $this->assertSame($data, $_REQUEST); } - public function testGetRequestDataMergesGetPostCookie(): void + public function testGetRequestDataMergesGetAndPost(): void { $this->superglobals->setGetArray(['get_key' => 'get_value']); $this->superglobals->setPostArray(['post_key' => 'post_value']); - $this->superglobals->setCookieArray(['cookie_key' => 'cookie_value']); $data = $this->superglobals->getRequestData(); $this->assertSame('get_value', $data['get_key']); $this->assertSame('post_value', $data['post_key']); - $this->assertSame('cookie_value', $data['cookie_key']); } public function testGetRequestDataReflectsGetChanges(): void From 416505de8779e388fa5d726962154c5e2a8605e4 Mon Sep 17 00:00:00 2001 From: rahul05ranjan Date: Sat, 26 Sep 2026 21:29:56 +0530 Subject: [PATCH 4/6] style: fix PHP CS Fixer alignment issues - Align phpdoc @param annotations in RequestTrait::fetchFromArray. - Align match arm => operators in Superglobals::getRequestData. --- system/HTTP/RequestTrait.php | 8 ++++---- system/Superglobals.php | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index 1dd635be0ac8..455cd02e942c 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -298,10 +298,10 @@ public function fetchGlobal(string $name, $index = null, ?int $filter = null, $f * Fetches one or more items from an array, applying the same filtering * and index resolution as fetchGlobal(). * - * @param array $data - * @param int|list|string|null $index - * @param int|null $filter Filter constant - * @param array|int|null $flags Options + * @param array $data + * @param int|list|string|null $index + * @param int|null $filter Filter constant + * @param array|int|null $flags Options * * @return mixed */ diff --git a/system/Superglobals.php b/system/Superglobals.php index 3d7abda0680c..7ff67fb52416 100644 --- a/system/Superglobals.php +++ b/system/Superglobals.php @@ -413,9 +413,9 @@ public function getRequestData(): array foreach (str_split($requestOrder) as $type) { match ($type) { - 'G' => $request = array_merge($request, $this->get), - 'P' => $request = array_merge($request, $this->post), - 'C' => $request = array_merge($request, $this->cookie), + 'G' => $request = array_merge($request, $this->get), + 'P' => $request = array_merge($request, $this->post), + 'C' => $request = array_merge($request, $this->cookie), default => null, }; } From 72d0f28b9c4e09600d488768fbbbac418791c234 Mon Sep 17 00:00:00 2001 From: rahul05ranjan Date: Sun, 27 Sep 2026 14:44:06 +0530 Subject: [PATCH 5/6] test: cover cookie merge, request_order precedence, and stale $_REQUEST - Make Superglobals::getRequestData() accept an optional request_order override so precedence and cookie branches can be tested deterministically. - Add tests for cookie merging, order-sensitive overwrite behavior, and unknown order types. - Add a regression test proving getVar() reflects $_GET changes even when $_REQUEST is stale. --- system/Superglobals.php | 10 +++++-- tests/system/HTTP/IncomingRequestTest.php | 11 ++++++++ tests/system/SuperglobalsTest.php | 34 +++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/system/Superglobals.php b/system/Superglobals.php index 7ff67fb52416..17d3573b2d25 100644 --- a/system/Superglobals.php +++ b/system/Superglobals.php @@ -397,14 +397,20 @@ public function setRequestArray(array $array): self * stale. This method returns the current merged values so callers can * read up-to-date request data without relying on the stale $_REQUEST. * + * @param string|null $requestOrder Overrides the ini setting for testing. + * * @return array */ - public function getRequestData(): array + public function getRequestData(?string $requestOrder = null): array { - $requestOrder = ini_get('request_order'); + if ($requestOrder === null) { + $requestOrder = ini_get('request_order'); + } + if ($requestOrder === false || $requestOrder === '') { $requestOrder = ini_get('variables_order'); } + if ($requestOrder === false || $requestOrder === '') { $requestOrder = 'GP'; } diff --git a/tests/system/HTTP/IncomingRequestTest.php b/tests/system/HTTP/IncomingRequestTest.php index add88bfaef3a..8d73a9745c67 100644 --- a/tests/system/HTTP/IncomingRequestTest.php +++ b/tests/system/HTTP/IncomingRequestTest.php @@ -76,6 +76,17 @@ public function testCanGrabRequestVars(): void $this->assertNull($this->request->getVar('TESTY')); } + public function testGetVarReflectsGetChangesWhenRequestIsStale(): void + { + // Simulate the state after SiteURIFactory updates $_GET: $_REQUEST + // still holds the original value while $_GET has been refreshed. + service('superglobals') + ->setGetArray(['code' => 'good']) + ->setRequestArray(['code' => 'stale']); + + $this->assertSame('good', $this->request->getVar('code')); + } + public function testCanGrabGetVars(): void { service('superglobals')->setGet('TEST', '5'); diff --git a/tests/system/SuperglobalsTest.php b/tests/system/SuperglobalsTest.php index 547493878dab..a4c24081d1a7 100644 --- a/tests/system/SuperglobalsTest.php +++ b/tests/system/SuperglobalsTest.php @@ -325,6 +325,40 @@ public function testGetRequestDataReflectsGetChanges(): void $this->assertSame('new', $this->superglobals->getRequestData()['key']); } + public function testGetRequestDataMergesCookie(): void + { + $this->superglobals->setGetArray(['get_key' => 'get_value']); + $this->superglobals->setPostArray(['post_key' => 'post_value']); + $this->superglobals->setCookieArray(['cookie_key' => 'cookie_value']); + + $data = $this->superglobals->getRequestData('GPC'); + + $this->assertSame('get_value', $data['get_key']); + $this->assertSame('post_value', $data['post_key']); + $this->assertSame('cookie_value', $data['cookie_key']); + } + + public function testGetRequestDataRespectsOrder(): void + { + $this->superglobals->setGetArray(['shared' => 'get']); + $this->superglobals->setPostArray(['shared' => 'post']); + $this->superglobals->setCookieArray(['shared' => 'cookie']); + + // Later sources overwrite earlier ones, matching PHP's request_order. + $this->assertSame('post', $this->superglobals->getRequestData('GP')['shared']); + $this->assertSame('cookie', $this->superglobals->getRequestData('GPC')['shared']); + $this->assertSame('get', $this->superglobals->getRequestData('PG')['shared']); + } + + public function testGetRequestDataIgnoresUnknownOrderTypes(): void + { + $this->superglobals->setGetArray(['get_key' => 'get_value']); + + $data = $this->superglobals->getRequestData('GX'); + + $this->assertSame(['get_key' => 'get_value'], $data); + } + // $_FILES tests public function testFilesGetArray(): void { From 983c2e33c57c1a263fb84dbefd7c8369eb9e1fce Mon Sep 17 00:00:00 2001 From: rahul05ranjan Date: Mon, 28 Sep 2026 18:31:24 +0530 Subject: [PATCH 6/6] style: use null coalescing assignment in getRequestData() Rector's IfToNullCoalescingAssignRector flags the if-null block. --- system/Superglobals.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/Superglobals.php b/system/Superglobals.php index 17d3573b2d25..1691c65a57a6 100644 --- a/system/Superglobals.php +++ b/system/Superglobals.php @@ -403,9 +403,7 @@ public function setRequestArray(array $array): self */ public function getRequestData(?string $requestOrder = null): array { - if ($requestOrder === null) { - $requestOrder = ini_get('request_order'); - } + $requestOrder ??= ini_get('request_order'); if ($requestOrder === false || $requestOrder === '') { $requestOrder = ini_get('variables_order');