mirror of https://github.com/flightphp/core
parent
ca46fd041d
commit
106955a82e
@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use flight\net\Request;
|
||||
use flight\util\Collection;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class RequestBodyParserTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$_SERVER = [];
|
||||
$_REQUEST = [];
|
||||
$_GET = [];
|
||||
$_POST = [];
|
||||
$_COOKIE = [];
|
||||
$_FILES = [];
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
unset($_REQUEST);
|
||||
unset($_SERVER);
|
||||
unset($_GET);
|
||||
unset($_POST);
|
||||
unset($_COOKIE);
|
||||
unset($_FILES);
|
||||
}
|
||||
|
||||
private function createRequestConfig(string $method, string $contentType, string $body, &$tmpfile = null): array
|
||||
{
|
||||
$tmpfile = tmpfile();
|
||||
$stream_path = stream_get_meta_data($tmpfile)['uri'];
|
||||
file_put_contents($stream_path, $body);
|
||||
|
||||
return [
|
||||
'url' => '/',
|
||||
'base' => '/',
|
||||
'method' => $method,
|
||||
'referrer' => '',
|
||||
'ip' => '127.0.0.1',
|
||||
'ajax' => false,
|
||||
'scheme' => 'http',
|
||||
'user_agent' => 'Test',
|
||||
'type' => $contentType,
|
||||
'length' => strlen($body),
|
||||
'secure' => false,
|
||||
'accept' => '',
|
||||
'proxy_ip' => '',
|
||||
'host' => 'localhost',
|
||||
'servername' => 'localhost',
|
||||
'stream_path' => $stream_path,
|
||||
'data' => new Collection(),
|
||||
'query' => new Collection(),
|
||||
'cookies' => new Collection(),
|
||||
'files' => new Collection()
|
||||
];
|
||||
}
|
||||
|
||||
private function assertUrlEncodedParsing(string $method): void
|
||||
{
|
||||
$body = 'foo=bar&baz=qux&key=value';
|
||||
$tmpfile = tmpfile();
|
||||
$stream_path = stream_get_meta_data($tmpfile)['uri'];
|
||||
file_put_contents($stream_path, $body);
|
||||
|
||||
$config = [
|
||||
'url' => '/',
|
||||
'base' => '/',
|
||||
'method' => $method,
|
||||
'referrer' => '',
|
||||
'ip' => '127.0.0.1',
|
||||
'ajax' => false,
|
||||
'scheme' => 'http',
|
||||
'user_agent' => 'Test',
|
||||
'type' => 'application/x-www-form-urlencoded',
|
||||
'length' => strlen($body),
|
||||
'secure' => false,
|
||||
'accept' => '',
|
||||
'proxy_ip' => '',
|
||||
'host' => 'localhost',
|
||||
'servername' => 'localhost',
|
||||
'stream_path' => $stream_path,
|
||||
'data' => new Collection(),
|
||||
'query' => new Collection(),
|
||||
'cookies' => new Collection(),
|
||||
'files' => new Collection()
|
||||
];
|
||||
|
||||
$request = new Request($config);
|
||||
|
||||
$expectedData = [
|
||||
'foo' => 'bar',
|
||||
'baz' => 'qux',
|
||||
'key' => 'value'
|
||||
];
|
||||
$this->assertEquals($expectedData, $request->data->getData());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
private function createMultipartBody(string $boundary, array $fields, array $files = []): string
|
||||
{
|
||||
$body = '';
|
||||
|
||||
// Add form fields
|
||||
foreach ($fields as $name => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$body .= "--{$boundary}\r\n";
|
||||
$body .= "Content-Disposition: form-data; name=\"{$name}\"\r\n";
|
||||
$body .= "\r\n";
|
||||
$body .= "{$item}\r\n";
|
||||
}
|
||||
} else {
|
||||
$body .= "--{$boundary}\r\n";
|
||||
$body .= "Content-Disposition: form-data; name=\"{$name}\"\r\n";
|
||||
$body .= "\r\n";
|
||||
$body .= "{$value}\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Add files
|
||||
foreach ($files as $name => $file) {
|
||||
$body .= "--{$boundary}\r\n";
|
||||
$body .= "Content-Disposition: form-data; name=\"{$name}\"; filename=\"{$file['filename']}\"\r\n";
|
||||
$body .= "Content-Type: {$file['type']}\r\n";
|
||||
$body .= "\r\n";
|
||||
$body .= "{$file['content']}\r\n";
|
||||
}
|
||||
|
||||
$body .= "--{$boundary}--\r\n";
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
public function testParseUrlEncodedBodyForPutMethod(): void
|
||||
{
|
||||
$this->assertUrlEncodedParsing('PUT');
|
||||
}
|
||||
|
||||
public function testParseUrlEncodedBodyForPatchMethod(): void
|
||||
{
|
||||
$this->assertUrlEncodedParsing('PATCH');
|
||||
}
|
||||
|
||||
public function testParseUrlEncodedBodyForDeleteMethod(): void
|
||||
{
|
||||
$this->assertUrlEncodedParsing('DELETE');
|
||||
}
|
||||
|
||||
public function testParseMultipartFormDataWithFiles(): void
|
||||
{
|
||||
$boundary = 'boundary123456789';
|
||||
$fields = ['title' => 'Test Document'];
|
||||
$files = [
|
||||
'file' => [
|
||||
'filename' => 'file.txt',
|
||||
'type' => 'text/plain',
|
||||
'content' => 'This is test file content'
|
||||
]
|
||||
];
|
||||
|
||||
$body = $this->createMultipartBody($boundary, $fields, $files);
|
||||
$config = $this->createRequestConfig('PUT', "multipart/form-data; boundary={$boundary}", $body, $tmpfile);
|
||||
$request = new Request($config);
|
||||
|
||||
$this->assertEquals(['title' => 'Test Document'], $request->data->getData());
|
||||
|
||||
$file = $request->getUploadedFiles()['file'];
|
||||
$this->assertEquals('file.txt', $file->getClientFilename());
|
||||
$this->assertEquals('text/plain', $file->getClientMediaType());
|
||||
$this->assertEquals(strlen('This is test file content'), $file->getSize());
|
||||
$this->assertEquals(UPLOAD_ERR_OK, $file->getError());
|
||||
$this->assertNotNull($file->getTempName());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
public function testParseMultipartFormDataWithQuotedBoundary(): void
|
||||
{
|
||||
$boundary = 'boundary123456789';
|
||||
$fields = ['foo' => 'bar'];
|
||||
|
||||
$body = $this->createMultipartBody($boundary, $fields);
|
||||
$config = $this->createRequestConfig('PATCH', "multipart/form-data; boundary=\"{$boundary}\"", $body, $tmpfile);
|
||||
$request = new Request($config);
|
||||
|
||||
$this->assertEquals($fields, $request->data->getData());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
public function testParseMultipartFormDataWithArrayFields(): void
|
||||
{
|
||||
$boundary = 'boundary123456789';
|
||||
$fields = ['name[]' => ['foo', 'bar']];
|
||||
$expectedData = ['name' => ['foo', 'bar']];
|
||||
|
||||
$body = $this->createMultipartBody($boundary, $fields);
|
||||
$config = $this->createRequestConfig('PUT', "multipart/form-data; boundary={$boundary}", $body, $tmpfile);
|
||||
$request = new Request($config);
|
||||
|
||||
$this->assertEquals($expectedData, $request->data->getData());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
public function testParseEmptyBody(): void
|
||||
{
|
||||
$config = $this->createRequestConfig('PUT', 'application/x-www-form-urlencoded', '', $tmpfile);
|
||||
$request = new Request($config);
|
||||
|
||||
$this->assertEquals([], $request->data->getData());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
public function testParseInvalidMultipartWithoutBoundary(): void
|
||||
{
|
||||
$originalData = ['foo foo' => 'bar bar', 'baz baz' => 'qux'];
|
||||
$body = http_build_query($originalData);
|
||||
$expectedData = ['foo_foo' => 'bar bar', 'baz_baz' => 'qux'];
|
||||
|
||||
$config = $this->createRequestConfig('PUT', 'multipart/form-data', $body, $tmpfile); // no boundary
|
||||
$request = new Request($config);
|
||||
|
||||
// should fall back to URL encoding and parse correctly
|
||||
$this->assertEquals($expectedData, $request->data->getData());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
public function testParseMultipartWithLargeFile(): void
|
||||
{
|
||||
$boundary = 'boundary123456789';
|
||||
$largeContent = str_repeat('A', 10000); // 10KB content
|
||||
$files = [
|
||||
'file' => [
|
||||
'filename' => 'large.txt',
|
||||
'type' => 'text/plain',
|
||||
'content' => $largeContent
|
||||
]
|
||||
];
|
||||
|
||||
$body = $this->createMultipartBody($boundary, [], $files);
|
||||
$config = $this->createRequestConfig('PUT', "multipart/form-data; boundary={$boundary}", $body, $tmpfile);
|
||||
$request = new Request($config);
|
||||
|
||||
$file = $request->getUploadedFiles()['file'];
|
||||
$this->assertArrayHasKey('file', $request->getUploadedFiles());
|
||||
$this->assertEquals('large.txt', $file->getClientFilename());
|
||||
$this->assertEquals(10000, $file->getSize());
|
||||
$this->assertEquals(UPLOAD_ERR_OK, $file->getError());
|
||||
$this->assertNotNull($file->getTempName());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
public function testGetMethodDoesNotTriggerParsing(): void
|
||||
{
|
||||
$body = 'foo=bar&baz=qux&key=value';
|
||||
$config = $this->createRequestConfig('GET', 'application/x-www-form-urlencoded', $body, $tmpfile);
|
||||
$request = new Request($config);
|
||||
|
||||
// GET method should not trigger parsing
|
||||
$this->assertEquals([], $request->data->getData());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
|
||||
public function testPostMethodDoesNotTriggerParsing(): void
|
||||
{
|
||||
$body = 'foo=bar&baz=qux&key=value';
|
||||
$config = $this->createRequestConfig('POST', 'application/x-www-form-urlencoded', $body, $tmpfile);
|
||||
$request = new Request($config);
|
||||
|
||||
// POST method should not trigger this parsing (uses $_POST instead)
|
||||
$this->assertEquals([], $request->data->getData());
|
||||
|
||||
fclose($tmpfile);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue