Skip to content

Commit cd66fcc

Browse files
committed
Add request_parse_body() function
RFC: https://wiki.php.net/rfc/rfc1867-non-post This function allows populating the $_POST and $_FILES globals for non-post requests. This avoids manual parsing of RFC1867 requests. Fixes #55815 Closes phpGH-11472
1 parent 2f89438 commit cd66fcc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+995
-45
lines changed

UPGRADING

+5
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ PHP 8.4 UPGRADE NOTES
143143
2. New Features
144144
========================================
145145

146+
- Core:
147+
. Added request_parse_body() function that allows parsing RFC1867 (multipart)
148+
requests in non-POST HTTP requests.
149+
RFC: https://wiki.php.net/rfc/rfc1867-non-post
150+
146151
- Date:
147152
. Added static methods
148153
DateTime[Immutable]::createFromTimestamp(int|float $timestamp): static.

Zend/Optimizer/zend_func_infos.h

+1
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ static const func_info_t func_infos[] = {
599599
F1("fsockopen", MAY_BE_RESOURCE|MAY_BE_FALSE),
600600
FN("pfsockopen", MAY_BE_RESOURCE|MAY_BE_FALSE),
601601
F1("http_build_query", MAY_BE_STRING),
602+
F1("request_parse_body", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ARRAY),
602603
F1("image_type_to_mime_type", MAY_BE_STRING),
603604
F1("image_type_to_extension", MAY_BE_STRING|MAY_BE_FALSE),
604605
F1("getimagesize", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
request_parse_body() with multipart and invalid boundary
3+
--ENV--
4+
REQUEST_METHOD=PUT
5+
--POST_RAW--
6+
Content-Type: multipart/form-data; boundary="foobar
7+
empty
8+
--FILE--
9+
<?php
10+
11+
try {
12+
[$_POST, $_FILES] = request_parse_body();
13+
} catch (Throwable $e) {
14+
echo get_class($e), ': ', $e->getMessage(), "\n";
15+
}
16+
17+
var_dump($_POST, $_FILES);
18+
19+
?>
20+
--EXPECT--
21+
RequestParseBodyException: Invalid boundary in multipart/form-data POST data
22+
array(0) {
23+
}
24+
array(0) {
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
request_parse_body() with multipart
3+
--ENV--
4+
REQUEST_METHOD=PUT
5+
--POST_RAW--
6+
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
7+
-----------------------------84000087610663814162942123332
8+
Content-Disposition: form-data; name="post_field_name"
9+
10+
post field data
11+
-----------------------------84000087610663814162942123332
12+
Content-Disposition: form-data; name="file_name"; filename="original_file_name.txt"
13+
Content-Type: text/plain
14+
15+
file data
16+
-----------------------------84000087610663814162942123332--
17+
--FILE--
18+
<?php
19+
20+
[$_POST, $_FILES] = request_parse_body();
21+
22+
var_dump($_POST, $_FILES);
23+
24+
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
25+
move_uploaded_file($_FILES['file_name']['tmp_name'], $file_path);
26+
var_dump(file_get_contents($file_path));
27+
28+
?>
29+
--CLEAN--
30+
<?php
31+
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
32+
@unlink($file_path);
33+
?>
34+
--EXPECTF--
35+
array(1) {
36+
["post_field_name"]=>
37+
string(15) "post field data"
38+
}
39+
array(1) {
40+
["file_name"]=>
41+
array(6) {
42+
["name"]=>
43+
string(22) "original_file_name.txt"
44+
["full_path"]=>
45+
string(22) "original_file_name.txt"
46+
["type"]=>
47+
string(10) "text/plain"
48+
["tmp_name"]=>
49+
string(%d) "%s"
50+
["error"]=>
51+
int(0)
52+
["size"]=>
53+
int(9)
54+
}
55+
}
56+
string(9) "file data"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
request_parse_body() with multipart and garbled field
3+
--INI--
4+
max_file_uploads=1
5+
--ENV--
6+
REQUEST_METHOD=PUT
7+
--POST_RAW--
8+
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
9+
-----------------------------84000087610663814162942123332
10+
Content-Disposition: form-data;
11+
Content-Type: text/plain
12+
13+
post field data
14+
-----------------------------84000087610663814162942123332--
15+
--FILE--
16+
<?php
17+
18+
try {
19+
[$_POST, $_FILES] = request_parse_body();
20+
} catch (Throwable $e) {
21+
echo get_class($e), ': ', $e->getMessage(), "\n";
22+
}
23+
24+
var_dump($_POST, $_FILES);
25+
26+
?>
27+
--EXPECT--
28+
RequestParseBodyException: File Upload Mime headers garbled
29+
array(0) {
30+
}
31+
array(0) {
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
request_parse_body() with multipart and exceeding max files
3+
--INI--
4+
max_file_uploads=1
5+
--ENV--
6+
REQUEST_METHOD=PUT
7+
--POST_RAW--
8+
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
9+
-----------------------------84000087610663814162942123332
10+
Content-Disposition: form-data; name="file1"; filename="file1.txt"
11+
Content-Type: text/plain
12+
13+
file data
14+
-----------------------------84000087610663814162942123332
15+
Content-Disposition: form-data; name="file2"; filename="file2.txt"
16+
Content-Type: text/plain
17+
18+
file data
19+
-----------------------------84000087610663814162942123332--
20+
--FILE--
21+
<?php
22+
23+
try {
24+
[$_POST, $_FILES] = request_parse_body();
25+
} catch (Throwable $e) {
26+
echo get_class($e), ': ', $e->getMessage(), "\n";
27+
}
28+
29+
var_dump($_POST, $_FILES);
30+
31+
?>
32+
--EXPECT--
33+
RequestParseBodyException: Maximum number of allowable file uploads has been exceeded
34+
array(0) {
35+
}
36+
array(0) {
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
request_parse_body() with multipart and exceeding max input vars
3+
--INI--
4+
max_input_vars=1
5+
--ENV--
6+
REQUEST_METHOD=PUT
7+
--POST_RAW--
8+
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
9+
-----------------------------84000087610663814162942123332
10+
Content-Disposition: form-data; name="field1"
11+
12+
post field data
13+
-----------------------------84000087610663814162942123332
14+
Content-Disposition: form-data; name="field2"
15+
16+
post field data
17+
-----------------------------84000087610663814162942123332--
18+
--FILE--
19+
<?php
20+
21+
try {
22+
[$_POST, $_FILES] = request_parse_body();
23+
} catch (Throwable $e) {
24+
echo get_class($e), ': ', $e->getMessage(), "\n";
25+
}
26+
27+
var_dump($_POST, $_FILES);
28+
29+
?>
30+
--EXPECT--
31+
RequestParseBodyException: Input variables exceeded 1. To increase the limit change max_input_vars in php.ini.
32+
array(0) {
33+
}
34+
array(0) {
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
request_parse_body() with multipart and exceeding max parts
3+
--INI--
4+
max_multipart_body_parts=1
5+
--ENV--
6+
REQUEST_METHOD=PUT
7+
--POST_RAW--
8+
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
9+
-----------------------------84000087610663814162942123332
10+
Content-Disposition: form-data; name="post_field_name"
11+
12+
post field data
13+
-----------------------------84000087610663814162942123332
14+
Content-Disposition: form-data; name="file_name"; filename="original_file_name.txt"
15+
Content-Type: text/plain
16+
17+
file data
18+
-----------------------------84000087610663814162942123332--
19+
--FILE--
20+
<?php
21+
22+
try {
23+
[$_POST, $_FILES] = request_parse_body();
24+
} catch (Throwable $e) {
25+
echo get_class($e), ': ', $e->getMessage(), "\n";
26+
}
27+
28+
var_dump($_POST, $_FILES);
29+
30+
?>
31+
--EXPECT--
32+
RequestParseBodyException: Multipart body parts limit exceeded 1. To increase the limit change max_multipart_body_parts in php.ini.
33+
array(0) {
34+
}
35+
array(0) {
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
request_parse_body() with multipart and missing boundary
3+
--ENV--
4+
REQUEST_METHOD=PUT
5+
--POST_RAW--
6+
Content-Type: multipart/form-data
7+
empty
8+
--FILE--
9+
<?php
10+
11+
$stream = fopen('php://memory','r+');
12+
13+
try {
14+
[$_POST, $_FILES] = request_parse_body();
15+
} catch (Throwable $e) {
16+
echo get_class($e), ': ', $e->getMessage(), "\n";
17+
}
18+
19+
var_dump($_POST, $_FILES);
20+
21+
?>
22+
--EXPECT--
23+
RequestParseBodyException: Missing boundary in multipart/form-data POST data
24+
array(0) {
25+
}
26+
array(0) {
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
request_parse_body() invalid key
3+
--FILE--
4+
<?php
5+
6+
try {
7+
request_parse_body(options: ['foo' => 1]);
8+
} catch (Error $e) {
9+
echo get_class($e), ': ', $e->getMessage(), "\n";
10+
}
11+
12+
try {
13+
request_parse_body(options: [42 => 1]);
14+
} catch (Error $e) {
15+
echo get_class($e), ': ', $e->getMessage(), "\n";
16+
}
17+
18+
?>
19+
--EXPECT--
20+
ValueError: Invalid key "foo" in $options argument
21+
ValueError: Invalid integer key in $options argument
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
request_parse_body() invalid quantity
3+
--FILE--
4+
<?php
5+
6+
try {
7+
request_parse_body(options: [
8+
'upload_max_filesize' => '1GB',
9+
]);
10+
} catch (Throwable $e) {
11+
echo get_class($e), ': ', $e->getMessage(), "\n";
12+
}
13+
14+
?>
15+
--EXPECTF--
16+
Warning: Invalid quantity "1GB": unknown multiplier "B", interpreting as "1" for backwards compatibility in %s on line %d
17+
RequestParseBodyException: Request does not provide a content type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
request_parse_body() invalid value type
3+
--FILE--
4+
<?php
5+
6+
try {
7+
request_parse_body(options: [
8+
'max_input_vars' => [],
9+
]);
10+
} catch (Error $e) {
11+
echo get_class($e), ': ', $e->getMessage(), "\n";
12+
}
13+
14+
?>
15+
--EXPECT--
16+
ValueError: Invalid array value in $options argument
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
request_parse_body() max_file_uploads option
3+
--INI--
4+
max_file_uploads=10
5+
--ENV--
6+
REQUEST_METHOD=PUT
7+
--POST_RAW--
8+
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
9+
-----------------------------84000087610663814162942123332
10+
Content-Disposition: form-data; name="file1"; filename="file1.txt"
11+
Content-Type: text/plain
12+
13+
file data
14+
-----------------------------84000087610663814162942123332
15+
Content-Disposition: form-data; name="file2"; filename="file2.txt"
16+
Content-Type: text/plain
17+
18+
file data
19+
-----------------------------84000087610663814162942123332--
20+
--FILE--
21+
<?php
22+
23+
try {
24+
[$_POST, $_FILES] = request_parse_body([
25+
'max_file_uploads' => 1,
26+
]);
27+
} catch (Throwable $e) {
28+
echo get_class($e), ': ', $e->getMessage(), "\n";
29+
}
30+
31+
var_dump($_POST, $_FILES);
32+
33+
?>
34+
--EXPECT--
35+
RequestParseBodyException: Maximum number of allowable file uploads has been exceeded
36+
array(0) {
37+
}
38+
array(0) {
39+
}

0 commit comments

Comments
 (0)