mirror of https://github.com/flightphp/core
				
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							286 lines
						
					
					
						
							9.1 KiB
						
					
					
				
			
		
		
	
	
							286 lines
						
					
					
						
							9.1 KiB
						
					
					
				<?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);
 | 
						|
    }
 | 
						|
} |