'/', '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); } }