diff --git a/app/Config/Feature.php b/app/Config/Feature.php index 39a37676183c..c8f2cafcccd0 100644 --- a/app/Config/Feature.php +++ b/app/Config/Feature.php @@ -18,4 +18,17 @@ class Feature extends BaseConfig * Use filter execution order in 4.4 or before. */ public bool $oldFilterOrder = false; + + /** + * Use lowercase HTTP method names like "get", "post" in Config\Filters::$methods. + * + * But the HTTP method is case-sensitive. So using lowercase is wrong. + * We should disable this and use uppercase names like "GET", "POST", etc. + * + * The method token is case-sensitive because it might be used as a gateway + * to object-based systems with case-sensitive method names. By convention, + * standardized methods are defined in all-uppercase US-ASCII letters. + * https://www.rfc-editor.org/rfc/rfc9110#name-overview + */ + public bool $lowerCaseFilterMethods = true; } diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 68cb5a9f5497..3eecebfbed31 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -1027,7 +1027,7 @@ public function storePreviousURL($uri) public function spoofRequestMethod() { // Only works with POSTED forms - if (strtolower($this->request->getMethod()) !== 'post') { + if ($this->request->getMethod() !== 'POST') { return; } @@ -1038,7 +1038,7 @@ public function spoofRequestMethod() } // Only allows PUT, PATCH, DELETE - if (in_array(strtoupper($method), ['PUT', 'PATCH', 'DELETE'], true)) { + if (in_array($method, ['PUT', 'PATCH', 'DELETE'], true)) { $this->request = $this->request->setMethod($method); } } diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 8a28aeb0118e..84100ed08879 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -494,8 +494,9 @@ protected function processMethods() return; } - // Request method won't be set for CLI-based requests - $method = strtolower($this->request->getMethod()) ?? 'cli'; + if (config(Feature::class)->lowerCaseFilterMethods) { + $method = strtolower($this->request->getMethod()); + } if (array_key_exists($method, $this->config->methods)) { if (config(Feature::class)->oldFilterOrder) { diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 878e8a1d66c6..2b12694d86b9 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -56,7 +56,7 @@ class CLIRequest extends Request * * @var string */ - protected $method = 'cli'; + protected $method = 'CLI'; /** * Constructor diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index c8b3b9fe9f92..48605b1e8274 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -130,7 +130,7 @@ public function __construct(App $config, URI $uri, ?ResponseInterface $response * Sends an HTTP request to the specified $url. If this is a relative * URL, it will be merged with $this->baseURI to form a complete URL. * - * @param string $method + * @param string $method HTTP method */ public function request($method, string $url, array $options = []): ResponseInterface { @@ -177,7 +177,7 @@ protected function resetOptions() */ public function get(string $url, array $options = []): ResponseInterface { - return $this->request('get', $url, $options); + return $this->request('GET', $url, $options); } /** @@ -185,7 +185,7 @@ public function get(string $url, array $options = []): ResponseInterface */ public function delete(string $url, array $options = []): ResponseInterface { - return $this->request('delete', $url, $options); + return $this->request('DELETE', $url, $options); } /** @@ -193,7 +193,7 @@ public function delete(string $url, array $options = []): ResponseInterface */ public function head(string $url, array $options = []): ResponseInterface { - return $this->request('head', $url, $options); + return $this->request('HEAD', $url, $options); } /** @@ -201,7 +201,7 @@ public function head(string $url, array $options = []): ResponseInterface */ public function options(string $url, array $options = []): ResponseInterface { - return $this->request('options', $url, $options); + return $this->request('OPTIONS', $url, $options); } /** @@ -209,7 +209,7 @@ public function options(string $url, array $options = []): ResponseInterface */ public function patch(string $url, array $options = []): ResponseInterface { - return $this->request('patch', $url, $options); + return $this->request('PATCH', $url, $options); } /** @@ -217,7 +217,7 @@ public function patch(string $url, array $options = []): ResponseInterface */ public function post(string $url, array $options = []): ResponseInterface { - return $this->request('post', $url, $options); + return $this->request('POST', $url, $options); } /** @@ -225,7 +225,7 @@ public function post(string $url, array $options = []): ResponseInterface */ public function put(string $url, array $options = []): ResponseInterface { - return $this->request('put', $url, $options); + return $this->request('PUT', $url, $options); } /** @@ -339,17 +339,6 @@ protected function prepareURL(string $url): string ); } - /** - * Get the request method. Overrides the Request class' method - * since users expect a different answer here. - * - * @param bool|false $upper Whether to return in upper or lower case. - */ - public function getMethod(bool $upper = false): string - { - return ($upper) ? strtoupper($this->method) : strtolower($this->method); - } - /** * Fires the actual cURL request. * @@ -446,8 +435,6 @@ protected function applyRequestHeaders(array $curlOptions = []): array */ protected function applyMethod(string $method, array $curlOptions): array { - $method = strtoupper($method); - $this->method = $method; $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index dbb3a31bea9b..f80337658e43 100755 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -406,7 +406,7 @@ public function is(string $type): bool $httpMethods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH', 'OPTIONS']; if (in_array($valueUpper, $httpMethods, true)) { - return strtoupper($this->getMethod()) === $valueUpper; + return $this->getMethod() === $valueUpper; } if ($valueUpper === 'JSON') { diff --git a/system/HTTP/OutgoingRequest.php b/system/HTTP/OutgoingRequest.php index 698cde263bc1..5738b1aea2e7 100644 --- a/system/HTTP/OutgoingRequest.php +++ b/system/HTTP/OutgoingRequest.php @@ -66,15 +66,13 @@ private function getHostFromUri(URI $uri): string } /** - * Get the request method. + * Retrieves the HTTP method of the request. * - * @param bool $upper Whether to return in upper or lower case. - * - * @deprecated The $upper functionality will be removed and this will revert to its PSR-7 equivalent + * @return string Returns the request method. */ - public function getMethod(bool $upper = false): string + public function getMethod(): string { - return ($upper) ? strtoupper($this->method) : strtolower($this->method); + return $this->method; } /** diff --git a/system/HTTP/OutgoingRequestInterface.php b/system/HTTP/OutgoingRequestInterface.php index 3839b64fd8e1..b4a62dfde73f 100644 --- a/system/HTTP/OutgoingRequestInterface.php +++ b/system/HTTP/OutgoingRequestInterface.php @@ -21,14 +21,11 @@ interface OutgoingRequestInterface extends MessageInterface { /** - * Get the request method. - * An extension of PSR-7's getMethod to allow casing. + * Retrieves the HTTP method of the request. * - * @param bool $upper Whether to return in upper or lower case. - * - * @deprecated The $upper functionality will be removed and this will revert to its PSR-7 equivalent + * @return string Returns the request method. */ - public function getMethod(bool $upper = false): string; + public function getMethod(): string; /** * Return an instance with the provided HTTP method. diff --git a/system/HTTP/Request.php b/system/HTTP/Request.php index baf60c0305b6..462cb5731952 100644 --- a/system/HTTP/Request.php +++ b/system/HTTP/Request.php @@ -49,20 +49,6 @@ public function __construct($config = null) // @phpstan-ignore-line } } - /** - * Get the request method. - * - * @param bool $upper Whether to return in upper or lower case. - * - * @deprecated 4.0.5 The $upper functionality will be removed and this will revert to its PSR-7 equivalent - * - * @codeCoverageIgnore - */ - public function getMethod(bool $upper = false): string - { - return ($upper) ? strtoupper($this->method) : strtolower($this->method); - } - /** * Sets the request method. Used when spoofing the request. * diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index 505d1f5e5a7b..948e9c2dcd5d 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -733,7 +733,7 @@ public function testSpoofRequestMethodCanUsePUT(): void $this->codeigniter->run(); ob_get_clean(); - $this->assertSame('put', Services::incomingrequest()->getMethod()); + $this->assertSame('PUT', Services::incomingrequest()->getMethod()); } public function testSpoofRequestMethodCannotUseGET(): void @@ -758,7 +758,7 @@ public function testSpoofRequestMethodCannotUseGET(): void $this->codeigniter->run(); ob_get_clean(); - $this->assertSame('post', Services::incomingrequest()->getMethod()); + $this->assertSame('POST', Services::incomingrequest()->getMethod()); } /** diff --git a/tests/system/HTTP/CLIRequestTest.php b/tests/system/HTTP/CLIRequestTest.php index e15f0fca4f87..3c6293131d70 100644 --- a/tests/system/HTTP/CLIRequestTest.php +++ b/tests/system/HTTP/CLIRequestTest.php @@ -525,8 +525,7 @@ public function testGetIPAddressDefault(): void public function testMethodReturnsRightStuff(): void { // Defaults method to CLI now. - $this->assertSame('cli', $this->request->getMethod()); - $this->assertSame('CLI', $this->request->getMethod(true)); + $this->assertSame('CLI', $this->request->getMethod()); } public function testMethodIsCliReturnsAlwaysTrue(): void diff --git a/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php b/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php index d8e96424dae5..c9710616e2d2 100644 --- a/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php +++ b/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php @@ -103,7 +103,7 @@ public function testGetSetsCorrectMethod(): void { $this->request->get('http://example.com'); - $this->assertSame('get', $this->request->getMethod()); + $this->assertSame('GET', $this->request->getMethod()); $options = $this->request->curl_options; @@ -115,7 +115,7 @@ public function testDeleteSetsCorrectMethod(): void { $this->request->delete('http://example.com'); - $this->assertSame('delete', $this->request->getMethod()); + $this->assertSame('DELETE', $this->request->getMethod()); $options = $this->request->curl_options; @@ -127,7 +127,7 @@ public function testHeadSetsCorrectMethod(): void { $this->request->head('http://example.com'); - $this->assertSame('head', $this->request->getMethod()); + $this->assertSame('HEAD', $this->request->getMethod()); $options = $this->request->curl_options; @@ -139,7 +139,7 @@ public function testOptionsSetsCorrectMethod(): void { $this->request->options('http://example.com'); - $this->assertSame('options', $this->request->getMethod()); + $this->assertSame('OPTIONS', $this->request->getMethod()); $options = $this->request->curl_options; @@ -281,7 +281,7 @@ public function testPatchSetsCorrectMethod(): void { $this->request->patch('http://example.com'); - $this->assertSame('patch', $this->request->getMethod()); + $this->assertSame('PATCH', $this->request->getMethod()); $options = $this->request->curl_options; @@ -293,7 +293,7 @@ public function testPostSetsCorrectMethod(): void { $this->request->post('http://example.com'); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $options = $this->request->curl_options; @@ -305,7 +305,7 @@ public function testPutSetsCorrectMethod(): void { $this->request->put('http://example.com'); - $this->assertSame('put', $this->request->getMethod()); + $this->assertSame('PUT', $this->request->getMethod()); $options = $this->request->curl_options; @@ -322,19 +322,19 @@ public function testCustomMethodSetsCorrectMethod(): void $options = $this->request->curl_options; $this->assertArrayHasKey(CURLOPT_CUSTOMREQUEST, $options); - $this->assertSame('CUSTOM', $options[CURLOPT_CUSTOMREQUEST]); + $this->assertSame('custom', $options[CURLOPT_CUSTOMREQUEST]); } public function testRequestMethodGetsSanitized(): void { $this->request->request('', 'http://example.com'); - $this->assertSame('custom', $this->request->getMethod()); + $this->assertSame('Custom', $this->request->getMethod()); $options = $this->request->curl_options; $this->assertArrayHasKey(CURLOPT_CUSTOMREQUEST, $options); - $this->assertSame('CUSTOM', $options[CURLOPT_CUSTOMREQUEST]); + $this->assertSame('Custom', $options[CURLOPT_CUSTOMREQUEST]); } public function testRequestSetsBasicCurlOptions(): void @@ -951,7 +951,7 @@ public function testPostFormEncoded(): void 'form_params' => $params, ]); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $options = $this->request->curl_options; @@ -974,7 +974,7 @@ public function testPostFormMultipart(): void 'multipart' => $params, ]); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $options = $this->request->curl_options; @@ -1022,7 +1022,7 @@ public function testJSONData(): void 'json' => $params, ]); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $expected = json_encode($params); $this->assertSame( diff --git a/tests/system/HTTP/CURLRequestTest.php b/tests/system/HTTP/CURLRequestTest.php index 186284da6ba6..2ca18aa87282 100644 --- a/tests/system/HTTP/CURLRequestTest.php +++ b/tests/system/HTTP/CURLRequestTest.php @@ -103,7 +103,7 @@ public function testGetSetsCorrectMethod(): void { $this->request->get('http://example.com'); - $this->assertSame('get', $this->request->getMethod()); + $this->assertSame('GET', $this->request->getMethod()); $options = $this->request->curl_options; @@ -115,7 +115,7 @@ public function testDeleteSetsCorrectMethod(): void { $this->request->delete('http://example.com'); - $this->assertSame('delete', $this->request->getMethod()); + $this->assertSame('DELETE', $this->request->getMethod()); $options = $this->request->curl_options; @@ -127,7 +127,7 @@ public function testHeadSetsCorrectMethod(): void { $this->request->head('http://example.com'); - $this->assertSame('head', $this->request->getMethod()); + $this->assertSame('HEAD', $this->request->getMethod()); $options = $this->request->curl_options; @@ -139,7 +139,7 @@ public function testOptionsSetsCorrectMethod(): void { $this->request->options('http://example.com'); - $this->assertSame('options', $this->request->getMethod()); + $this->assertSame('OPTIONS', $this->request->getMethod()); $options = $this->request->curl_options; @@ -264,7 +264,7 @@ public function testPatchSetsCorrectMethod(): void { $this->request->patch('http://example.com'); - $this->assertSame('patch', $this->request->getMethod()); + $this->assertSame('PATCH', $this->request->getMethod()); $options = $this->request->curl_options; @@ -276,7 +276,7 @@ public function testPostSetsCorrectMethod(): void { $this->request->post('http://example.com'); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $options = $this->request->curl_options; @@ -288,7 +288,7 @@ public function testPutSetsCorrectMethod(): void { $this->request->put('http://example.com'); - $this->assertSame('put', $this->request->getMethod()); + $this->assertSame('PUT', $this->request->getMethod()); $options = $this->request->curl_options; @@ -305,19 +305,19 @@ public function testCustomMethodSetsCorrectMethod(): void $options = $this->request->curl_options; $this->assertArrayHasKey(CURLOPT_CUSTOMREQUEST, $options); - $this->assertSame('CUSTOM', $options[CURLOPT_CUSTOMREQUEST]); + $this->assertSame('custom', $options[CURLOPT_CUSTOMREQUEST]); } public function testRequestMethodGetsSanitized(): void { $this->request->request('', 'http://example.com'); - $this->assertSame('custom', $this->request->getMethod()); + $this->assertSame('Custom', $this->request->getMethod()); $options = $this->request->curl_options; $this->assertArrayHasKey(CURLOPT_CUSTOMREQUEST, $options); - $this->assertSame('CUSTOM', $options[CURLOPT_CUSTOMREQUEST]); + $this->assertSame('Custom', $options[CURLOPT_CUSTOMREQUEST]); } public function testRequestSetsBasicCurlOptions(): void @@ -934,7 +934,7 @@ public function testPostFormEncoded(): void 'form_params' => $params, ]); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $options = $this->request->curl_options; @@ -957,7 +957,7 @@ public function testPostFormMultipart(): void 'multipart' => $params, ]); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $options = $this->request->curl_options; @@ -1005,7 +1005,7 @@ public function testJSONData(): void 'json' => $params, ]); - $this->assertSame('post', $this->request->getMethod()); + $this->assertSame('POST', $this->request->getMethod()); $expected = json_encode($params); $this->assertSame($expected, $this->request->getBody()); diff --git a/tests/system/HTTP/IncomingRequestTest.php b/tests/system/HTTP/IncomingRequestTest.php index 9f6114f4875e..1681d946afc0 100644 --- a/tests/system/HTTP/IncomingRequestTest.php +++ b/tests/system/HTTP/IncomingRequestTest.php @@ -813,7 +813,7 @@ public function testGetFile(): void public function testSpoofing(): void { $this->request->setMethod('WINK'); - $this->assertSame('wink', $this->request->getMethod()); + $this->assertSame('WINK', $this->request->getMethod()); } /** diff --git a/tests/system/HTTP/RequestTest.php b/tests/system/HTTP/RequestTest.php index be20b4ff8f5b..269156fc1982 100644 --- a/tests/system/HTTP/RequestTest.php +++ b/tests/system/HTTP/RequestTest.php @@ -639,7 +639,6 @@ public function testGetIPAddressThruProxyOutofSubnet(): void public function testMethodReturnsRightStuff(): void { // Defaults method to GET now. - $this->assertSame('get', $this->request->getMethod()); - $this->assertSame('GET', $this->request->getMethod(true)); + $this->assertSame('GET', $this->request->getMethod()); } } diff --git a/tests/system/Test/ControllerTestTraitTest.php b/tests/system/Test/ControllerTestTraitTest.php index ab45c6372d41..ac470ce3ce56 100644 --- a/tests/system/Test/ControllerTestTraitTest.php +++ b/tests/system/Test/ControllerTestTraitTest.php @@ -150,7 +150,7 @@ public function testRequestPassthrough(): void ->execute('popper'); $req = $result->request(); - $this->assertSame('get', $req->getMethod()); + $this->assertSame('GET', $req->getMethod()); } public function testFailureResponse(): void diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 0b7ad6faddd1..3ed0471fcbf1 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -848,7 +848,7 @@ public function testRawInput(): void $rules = [ 'role' => 'required|min_length[5]', ]; - $result = $this->validation->withRequest($request->withMethod('patch'))->setRules($rules)->run(); + $result = $this->validation->withRequest($request->withMethod('PATCH'))->setRules($rules)->run(); $this->assertTrue($result); $this->assertSame([], $this->validation->getErrors()); @@ -873,7 +873,7 @@ public function testJsonInput(): void 'role' => 'required|min_length[5]', ]; $result = $this->validation - ->withRequest($request->withMethod('patch')) + ->withRequest($request->withMethod('PATCH')) ->setRules($rules) ->run(); @@ -910,7 +910,7 @@ public function testJsonInputObjectArray(): void 'p' => 'required|array_count[2]', ]; $result = $this->validation - ->withRequest($request->withMethod('patch')) + ->withRequest($request->withMethod('PATCH')) ->setRules($rules) ->run(); @@ -1093,7 +1093,7 @@ public function testRulesForArrayField(array $body, array $rules, array $results $request = new IncomingRequest($config, new URI(), http_build_query($body), new UserAgent()); $this->validation->setRules($rules); - $this->validation->withRequest($request->withMethod('post'))->run($body); + $this->validation->withRequest($request->withMethod('POST'))->run($body); $this->assertSame($results, $this->validation->getErrors()); } @@ -1176,7 +1176,7 @@ public function testRulesForSingleRuleWithAsteriskWillReturnNoError(): void 'name_user.*' => 'alpha_numeric', ]); - $this->validation->withRequest($request->withMethod('post'))->run(); + $this->validation->withRequest($request->withMethod('POST'))->run(); $this->assertSame([], $this->validation->getErrors()); } @@ -1211,7 +1211,7 @@ public function testRulesForSingleRuleWithAsteriskWillReturnError(): void 'contacts.friends.*.name' => 'required', ]); - $this->validation->withRequest($request->withMethod('post'))->run(); + $this->validation->withRequest($request->withMethod('POST'))->run(); $this->assertSame([ 'id_user.0' => 'The id_user.* field must contain only numbers.', 'name_user.0' => 'The name_user.* field may only contain alphabetical characters.', @@ -1245,7 +1245,7 @@ public function testRulesForSingleRuleWithSingleValue(): void 'id_user' => 'numeric', ]); - $this->validation->withRequest($request->withMethod('post'))->run(); + $this->validation->withRequest($request->withMethod('POST'))->run(); $this->assertSame([ 'id_user' => 'The id_user field must contain only numbers.', ], $this->validation->getErrors());