Skip to content

Commit

Permalink
Merge pull request #8186 from kenjis/fix-HTTP-verb-cases
Browse files Browse the repository at this point in the history
fix: remove deprecated upper functionality in `Request::getMethod()`
  • Loading branch information
kenjis authored Nov 19, 2023
2 parents 8b72997 + fe6babf commit 4a5cb90
Show file tree
Hide file tree
Showing 55 changed files with 439 additions and 241 deletions.
2 changes: 1 addition & 1 deletion app/Config/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class Filters extends BaseConfig
* particular HTTP method (GET, POST, etc.).
*
* Example:
* 'post' => ['foo', 'bar']
* 'POST' => ['foo', 'bar']
*
* If you use this, you should disable auto-routing because auto-routing
* permits any HTTP method to access a controller. Accessing the controller
Expand Down
2 changes: 1 addition & 1 deletion app/Views/errors/html/error_exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@
</tr>
<tr>
<td>HTTP Method</td>
<td><?= esc(strtoupper($request->getMethod())) ?></td>
<td><?= esc($request->getMethod()) ?></td>
</tr>
<tr>
<td>IP Address</td>
Expand Down
5 changes: 0 additions & 5 deletions phpstan-baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -1926,11 +1926,6 @@
'count' => 2,
'path' => __DIR__ . '/system/Filters/Filters.php',
];
$ignoreErrors[] = [
'message' => '#^Expression on left side of \\?\\? is not nullable\\.$#',
'count' => 1,
'path' => __DIR__ . '/system/Filters/Filters.php',
];
$ignoreErrors[] = [
'message' => '#^Only booleans are allowed in a negated boolean, array given\\.$#',
'count' => 1,
Expand Down
5 changes: 3 additions & 2 deletions system/CodeIgniter.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use CodeIgniter\HTTP\DownloadResponse;
use CodeIgniter\HTTP\Exceptions\RedirectException;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\Method;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\Request;
use CodeIgniter\HTTP\ResponsableInterface;
Expand Down Expand Up @@ -1027,7 +1028,7 @@ public function storePreviousURL($uri)
public function spoofRequestMethod()
{
// Only works with POSTED forms
if (strtolower($this->request->getMethod()) !== 'post') {
if ($this->request->getMethod() !== Method::POST) {
return;
}

Expand All @@ -1038,7 +1039,7 @@ public function spoofRequestMethod()
}

// Only allows PUT, PATCH, DELETE
if (in_array(strtoupper($method), ['PUT', 'PATCH', 'DELETE'], true)) {
if (in_array($method, [Method::PUT, Method::PATCH, Method::DELETE], true)) {
$this->request = $this->request->setMethod($method);
}
}
Expand Down
2 changes: 1 addition & 1 deletion system/Debug/Exceptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public function exceptionHandler(Throwable $exception)

if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes, true)) {
$uri = $this->request->getPath() === '' ? '/' : $this->request->getPath();
$routeInfo = '[Method: ' . strtoupper($this->request->getMethod()) . ', Route: ' . $uri . ']';
$routeInfo = '[Method: ' . $this->request->getMethod() . ', Route: ' . $uri . ']';

log_message('critical', "{message}\n{routeInfo}\nin {exFile} on line {exLine}.\n{trace}", [
'message' => $exception->getMessage(),
Expand Down
2 changes: 1 addition & 1 deletion system/Debug/Toolbar.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public function run(float $startTime, float $totalTime, RequestInterface $reques
$data = [];
// Data items used within the view.
$data['url'] = current_url();
$data['method'] = strtoupper($request->getMethod());
$data['method'] = $request->getMethod();
$data['isAJAX'] = $request->isAJAX();
$data['startTime'] = $startTime;
$data['totalTime'] = $totalTime * 1000;
Expand Down
22 changes: 20 additions & 2 deletions system/Filters/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -494,10 +494,28 @@ protected function processMethods()
return;
}

// Request method won't be set for CLI-based requests
$method = strtolower($this->request->getMethod()) ?? 'cli';
$method = $this->request->getMethod();

$found = false;

if (array_key_exists($method, $this->config->methods)) {
$found = true;
}
// Checks lowercase HTTP method for backward compatibility.
// @deprecated 4.5.0
// @TODO remove this in the future.
elseif (array_key_exists(strtolower($method), $this->config->methods)) {
@trigger_error(
'Setting lowercase HTTP method key "' . strtolower($method) . '" is deprecated.'
. ' Use uppercase HTTP method like "' . strtoupper($method) . '".',
E_USER_DEPRECATED
);

$found = true;
$method = strtolower($method);
}

if ($found) {
if (config(Feature::class)->oldFilterOrder) {
$this->filters['before'] = array_merge($this->filters['before'], $this->config->methods[$method]);
} else {
Expand Down
2 changes: 1 addition & 1 deletion system/HTTP/CLIRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class CLIRequest extends Request
*
* @var string
*/
protected $method = 'cli';
protected $method = 'CLI';

/**
* Constructor
Expand Down
33 changes: 10 additions & 23 deletions system/HTTP/CURLRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function __construct(App $config, URI $uri, ?ResponseInterface $response
throw HTTPException::forMissingCurl(); // @codeCoverageIgnore
}

parent::__construct('GET', $uri);
parent::__construct(Method::GET, $uri);

$this->responseOrig = $response ?? new Response(config(App::class));
$this->baseURI = $uri->useRawQueryString();
Expand All @@ -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
{
Expand Down Expand Up @@ -177,55 +177,55 @@ protected function resetOptions()
*/
public function get(string $url, array $options = []): ResponseInterface
{
return $this->request('get', $url, $options);
return $this->request(Method::GET, $url, $options);
}

/**
* Convenience method for sending a DELETE request.
*/
public function delete(string $url, array $options = []): ResponseInterface
{
return $this->request('delete', $url, $options);
return $this->request('DELETE', $url, $options);
}

/**
* Convenience method for sending a HEAD request.
*/
public function head(string $url, array $options = []): ResponseInterface
{
return $this->request('head', $url, $options);
return $this->request('HEAD', $url, $options);
}

/**
* Convenience method for sending an OPTIONS request.
*/
public function options(string $url, array $options = []): ResponseInterface
{
return $this->request('options', $url, $options);
return $this->request('OPTIONS', $url, $options);
}

/**
* Convenience method for sending a PATCH request.
*/
public function patch(string $url, array $options = []): ResponseInterface
{
return $this->request('patch', $url, $options);
return $this->request('PATCH', $url, $options);
}

/**
* Convenience method for sending a POST request.
*/
public function post(string $url, array $options = []): ResponseInterface
{
return $this->request('post', $url, $options);
return $this->request(Method::POST, $url, $options);
}

/**
* Convenience method for sending a PUT request.
*/
public function put(string $url, array $options = []): ResponseInterface
{
return $this->request('put', $url, $options);
return $this->request(Method::PUT, $url, $options);
}

/**
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;

Expand All @@ -458,7 +445,7 @@ protected function applyMethod(string $method, array $curlOptions): array
return $this->applyBody($curlOptions);
}

if ($method === 'PUT' || $method === 'POST') {
if ($method === Method::PUT || $method === Method::POST) {
// See http://tools.ietf.org/html/rfc7230#section-3.3.2
if ($this->header('content-length') === null && ! isset($this->config['multipart'])) {
$this->setHeader('Content-Length', '0');
Expand Down
7 changes: 4 additions & 3 deletions system/HTTP/IncomingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -396,17 +396,18 @@ public function negotiate(string $type, array $supported, bool $strictMatch = fa
/**
* Checks this request type.
*
* @param string $type HTTP verb or 'json' or 'ajax'
* @param string $type HTTP verb or 'json' or 'ajax'.
* HTTP verb should be case-sensitive, but this is case-insensitive.
* @phpstan-param string|'get'|'post'|'put'|'delete'|'head'|'patch'|'options'|'json'|'ajax' $type
*/
public function is(string $type): bool
{
$valueUpper = strtoupper($type);

$httpMethods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH', 'OPTIONS'];
$httpMethods = Method::all();

if (in_array($valueUpper, $httpMethods, true)) {
return strtoupper($this->getMethod()) === $valueUpper;
return $this->getMethod() === $valueUpper;
}

if ($valueUpper === 'JSON') {
Expand Down
95 changes: 95 additions & 0 deletions system/HTTP/Method.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

/**
* HTTP Method List
*/
class Method
{
/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT
*/
public const CONNECT = 'CONNECT';

/**
* Idempotent
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE
*/
public const DELETE = 'DELETE';

/**
* Safe, Idempotent, Cacheable
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET
*/
public const GET = 'GET';

/**
* Safe, Idempotent, Cacheable
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD
*/
public const HEAD = 'HEAD';

/**
* Safe, Idempotent
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
*/
public const OPTIONS = 'OPTIONS';

/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH
*/
public const PATCH = 'PATCH';

/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
*/
public const POST = 'POST';

/**
* Idempotent
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT
*/
public const PUT = 'PUT';

/**
* Safe, Idempotent
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/TRACE
*/
public const TRACE = 'TRACE';

/**
* Returns all HTTP methods.
*
* @return list<string>
*/
public static function all(): array
{
return [
self::CONNECT,
self::DELETE,
self::GET,
self::HEAD,
self::OPTIONS,
self::PATCH,
self::POST,
self::PUT,
self::TRACE,
];
}
}
10 changes: 4 additions & 6 deletions system/HTTP/OutgoingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 (always uppercase)
*/
public function getMethod(bool $upper = false): string
public function getMethod(): string
{
return ($upper) ? strtoupper($this->method) : strtolower($this->method);
return $this->method;
}

/**
Expand Down
9 changes: 3 additions & 6 deletions system/HTTP/OutgoingRequestInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 4a5cb90

Please sign in to comment.