Skip to content

Commit 93d5adf

Browse files
[HttpFoundation] Add Request::$allowedHttpMethodOverride to list which HTTP methods can be overridden
1 parent 9b72d76 commit 93d5adf

File tree

3 files changed

+57
-4
lines changed

3 files changed

+57
-4
lines changed

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add `#[WithHttpStatus]` to define status codes: 404 for `SignedUriException` and 403 for `ExpiredSignedUriException`
88
* Add support for the `QUERY` HTTP method
99
* Add support for structured MIME suffix
10+
* Add `Request::$allowedHttpMethodOverride` to list which HTTP methods can be overridden
1011
* Deprecate using `Request::sendHeaders()` after headers have already been sent; use a `StreamedResponse` instead
1112
* Deprecate method `Request::get()`, use properties `->attributes`, `query` or `request` directly instead
1213

src/Symfony/Component/HttpFoundation/Request.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ class Request
6464
public const METHOD_CONNECT = 'CONNECT';
6565
public const METHOD_QUERY = 'QUERY';
6666

67+
/**
68+
* The HTTP methods that can be overridden.
69+
*
70+
* @var string[]|null
71+
*/
72+
public static ?array $allowedHttpMethodOverride = null;
73+
6774
/**
6875
* @var string[]
6976
*/
@@ -1187,7 +1194,7 @@ public function getMethod(): string
11871194

11881195
$this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
11891196

1190-
if ('POST' !== $this->method) {
1197+
if ('POST' !== $this->method || !(self::$allowedHttpMethodOverride ?? true)) {
11911198
return $this->method;
11921199
}
11931200

@@ -1203,11 +1210,11 @@ public function getMethod(): string
12031210

12041211
$method = strtoupper($method);
12051212

1206-
if (\in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE', 'QUERY'], true)) {
1207-
return $this->method = $method;
1213+
if (self::$allowedHttpMethodOverride && !\in_array($method, self::$allowedHttpMethodOverride, true)) {
1214+
return $this->method;
12081215
}
12091216

1210-
if (!preg_match('/^[A-Z]++$/D', $method)) {
1217+
if (\strlen($method) !== strspn($method, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')) {
12111218
throw new SuspiciousOperationException('Invalid HTTP method override.');
12121219
}
12131220

src/Symfony/Component/HttpFoundation/Tests/RequestTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ protected function tearDown(): void
3434
{
3535
Request::setTrustedProxies([], -1);
3636
Request::setTrustedHosts([]);
37+
Request::$allowedHttpMethodOverride = null;
3738
}
3839

3940
public function testInitialize()
@@ -256,6 +257,50 @@ public function testCreate()
256257
$this->assertEquals('http://test.com/foo', $request->getUri());
257258
}
258259

260+
public function testHttpMethodOverrideRespectsAllowedListWithHeader()
261+
{
262+
$request = Request::create('http://example.com/', 'POST');
263+
$request->headers->set('X-HTTP-METHOD-OVERRIDE', 'PATCH');
264+
265+
Request::$allowedHttpMethodOverride = ['PUT', 'PATCH'];
266+
267+
$this->assertSame('PATCH', $request->getMethod());
268+
}
269+
270+
public function testHttpMethodOverrideDisallowedSkipsOverrideWithHeader()
271+
{
272+
$request = Request::create('http://example.com/', 'POST');
273+
$request->headers->set('X-HTTP-METHOD-OVERRIDE', 'DELETE');
274+
275+
Request::$allowedHttpMethodOverride = ['PUT', 'PATCH'];
276+
277+
$this->assertSame('POST', $request->getMethod());
278+
}
279+
280+
public function testHttpMethodOverrideDisabledWithEmptyAllowedList()
281+
{
282+
$request = Request::create('http://example.com/', 'POST');
283+
$request->headers->set('X-HTTP-METHOD-OVERRIDE', 'PUT');
284+
285+
Request::$allowedHttpMethodOverride = [];
286+
287+
$this->assertSame('POST', $request->getMethod());
288+
}
289+
290+
public function testHttpMethodOverrideRespectsAllowedListWithParameter()
291+
{
292+
Request::enableHttpMethodParameterOverride();
293+
Request::$allowedHttpMethodOverride = ['PUT'];
294+
295+
try {
296+
$request = Request::create('http://example.com/', 'POST', ['_method' => 'PUT']);
297+
298+
$this->assertSame('PUT', $request->getMethod());
299+
} finally {
300+
(new \ReflectionProperty(Request::class, 'httpMethodParameterOverride'))->setValue(null, false);
301+
}
302+
}
303+
259304
public function testCreateWithRequestUri()
260305
{
261306
$request = Request::create('http://test.com:80/foo');

0 commit comments

Comments
 (0)