diff --git a/src/StreamingBodyParser/BodyBufferedSink.php b/src/StreamingBodyParser/BodyBufferedSink.php new file mode 100644 index 00000000..b0b1122a --- /dev/null +++ b/src/StreamingBodyParser/BodyBufferedSink.php @@ -0,0 +1,28 @@ +on('body', function ($rawBody) use (&$body) { + $body .= $rawBody; + }); + $parser->on('end', function () use ($deferred, &$body) { + $deferred->resolve($body); + }); + + return $deferred->promise(); + } +} \ No newline at end of file diff --git a/src/StreamingBodyParser/BufferedSink.php b/src/StreamingBodyParser/BufferedSink.php new file mode 100644 index 00000000..e8a2d5e0 --- /dev/null +++ b/src/StreamingBodyParser/BufferedSink.php @@ -0,0 +1,23 @@ + BodyBufferedSink::createPromise($parser), + 'post' => PostBufferedSink::createPromise($parser), + 'files' => FilesBufferedSink::createPromise($parser), + ]; + + return Promise\all($promises); + } +} diff --git a/src/StreamingBodyParser/FilesBufferedSink.php b/src/StreamingBodyParser/FilesBufferedSink.php new file mode 100644 index 00000000..fc5d1948 --- /dev/null +++ b/src/StreamingBodyParser/FilesBufferedSink.php @@ -0,0 +1,35 @@ +on('file', function ($name, File $file) use (&$files) { + StreamBufferedSink::createPromise($file->getStream())->done(function ($buffer) use ($name, $file, &$files) { + $files[] = [ + 'name' => $name, + 'file' => $file, + 'buffer' => $buffer, + ]; + }); + }); + $parser->on('end', function () use ($deferred, &$files) { + $deferred->resolve($files); + }); + + return $deferred->promise(); + } +} diff --git a/src/StreamingBodyParser/PostBufferedSink.php b/src/StreamingBodyParser/PostBufferedSink.php new file mode 100644 index 00000000..c4148e51 --- /dev/null +++ b/src/StreamingBodyParser/PostBufferedSink.php @@ -0,0 +1,62 @@ +on('post', function ($key, $value) use (&$postFields) { + self::extractPost($postFields, $key, $value); + }); + $parser->on('end', function () use ($deferred, &$postFields) { + $deferred->resolve($postFields); + }); + + return $deferred->promise(); + } + + public static function extractPost(&$postFields, $key, $value) + { + $chunks = explode('[', $key); + if (count($chunks) == 1) { + $postFields[$key] = $value; + return; + } + + $chunkKey = $chunks[0]; + if (!isset($postFields[$chunkKey])) { + $postFields[$chunkKey] = []; + } + + $parent = &$postFields; + for ($i = 1; $i < count($chunks); $i++) { + $previousChunkKey = $chunkKey; + if (!isset($parent[$previousChunkKey])) { + $parent[$previousChunkKey] = []; + } + $parent = &$parent[$previousChunkKey]; + $chunkKey = $chunks[$i]; + + if ($chunkKey == ']') { + $parent[] = $value; + return; + } + + $chunkKey = rtrim($chunkKey, ']'); + if ($i == count($chunks) - 1) { + $parent[$chunkKey] = $value; + return; + } + } + } +} diff --git a/tests/StreamingBodyParser/BodyBufferedSinkTest.php b/tests/StreamingBodyParser/BodyBufferedSinkTest.php new file mode 100644 index 00000000..e656cba5 --- /dev/null +++ b/tests/StreamingBodyParser/BodyBufferedSinkTest.php @@ -0,0 +1,40 @@ +emit('body', ['abc']); + $parser->emit('end'); + + $result = Block\await($deferredStream, Factory::create(), 10); + + $this->assertSame('abc', $result); + } + + public function testDeferredStreamMultipleCalls() + { + $parser = new DummyParser(new Request('get', 'http://example.com')); + $deferredStream = BodyBufferedSink::createPromise($parser); + + $parser->emit('body', ['abc']); + $parser->emit('body', ['def']); + $parser->emit('end'); + + $result = Block\await($deferredStream, Factory::create(), 10); + + $this->assertSame('abcdef', $result); + } +} diff --git a/tests/StreamingBodyParser/BufferedSinkTest.php b/tests/StreamingBodyParser/BufferedSinkTest.php new file mode 100644 index 00000000..19bef1a3 --- /dev/null +++ b/tests/StreamingBodyParser/BufferedSinkTest.php @@ -0,0 +1,100 @@ +assertSame([ + 'post' => [], + 'files' => [], + 'body' => '', + ], $result); + } + + public function testDeferredStream() + { + $parser = new DummyParser(new Request('get', 'http://example.com')); + $deferredStream = BufferedSink::createPromise($parser); + $parser->emit('post', ['foo', 'bar']); + $parser->emit('post', ['array[]', 'foo']); + $parser->emit('post', ['array[]', 'bar']); + $parser->emit('post', ['dem[two]', 'bar']); + $parser->emit('post', ['dom[two][]', 'bar']); + + $stream = new ThroughStream(); + $file = new File('bar.ext', 'text', $stream); + $parser->emit('file', ['foo', $file]); + $stream->end('foo.bar'); + + $parser->emit('body', ['abc']); + + $parser->emit('end'); + + $result = Block\await($deferredStream, Factory::create(), 10); + $this->assertSame([ + 'foo' => 'bar', + 'array' => [ + 'foo', + 'bar', + ], + 'dem' => [ + 'two' => 'bar', + ], + 'dom' => [ + 'two' => [ + 'bar', + ], + ], + ], $result['post']); + + $this->assertSame('foo', $result['files'][0]['name']); + $this->assertSame('bar.ext', $result['files'][0]['file']->getFilename()); + $this->assertSame('text', $result['files'][0]['file']->getContentType()); + $this->assertSame('foo.bar', $result['files'][0]['buffer']); + + $this->assertSame('abc', $result['body']); + } + + public function testExtractPost() + { + $postFields = []; + BufferedSink::extractPost($postFields, 'dem', 'value'); + BufferedSink::extractPost($postFields, 'dom[one][two][]', 'value_a'); + BufferedSink::extractPost($postFields, 'dom[one][two][]', 'value_b'); + BufferedSink::extractPost($postFields, 'dam[]', 'value_a'); + BufferedSink::extractPost($postFields, 'dam[]', 'value_b'); + BufferedSink::extractPost($postFields, 'dum[sum]', 'value'); + $this->assertSame([ + 'dem' => 'value', + 'dom' => [ + 'one' => [ + 'two' => [ + 'value_a', + 'value_b', + ], + ], + ], + 'dam' => [ + 'value_a', + 'value_b', + ], + 'dum' => [ + 'sum' => 'value', + ], + ], $postFields); + } +} diff --git a/tests/StreamingBodyParser/FilesBufferedSinkTest.php b/tests/StreamingBodyParser/FilesBufferedSinkTest.php new file mode 100644 index 00000000..aa42f912 --- /dev/null +++ b/tests/StreamingBodyParser/FilesBufferedSinkTest.php @@ -0,0 +1,35 @@ +emit('file', ['foo', $file]); + $stream->end('foo.bar'); + + $parser->emit('end'); + + $result = Block\await($deferredStream, Factory::create(), 10); + + $this->assertSame('foo', $result['files'][0]['name']); + $this->assertSame('bar.ext', $result['files'][0]['file']->getFilename()); + $this->assertSame('text', $result['files'][0]['file']->getContentType()); + $this->assertSame('foo.bar', $result['files'][0]['buffer']); + } +} diff --git a/tests/StreamingBodyParser/PostBufferedSinkTest.php b/tests/StreamingBodyParser/PostBufferedSinkTest.php new file mode 100644 index 00000000..3b03554c --- /dev/null +++ b/tests/StreamingBodyParser/PostBufferedSinkTest.php @@ -0,0 +1,72 @@ +emit('post', ['foo', 'bar']); + $parser->emit('post', ['array[]', 'foo']); + $parser->emit('post', ['array[]', 'bar']); + $parser->emit('post', ['dem[two]', 'bar']); + $parser->emit('post', ['dom[two][]', 'bar']); + + $parser->emit('end'); + + $result = Block\await($deferredStream, Factory::create(), 10); + $this->assertSame([ + 'foo' => 'bar', + 'array' => [ + 'foo', + 'bar', + ], + 'dem' => [ + 'two' => 'bar', + ], + 'dom' => [ + 'two' => [ + 'bar', + ], + ], + ], $result); + } + + public function testExtractPost() + { + $postFields = []; + PostBufferedSink::extractPost($postFields, 'dem', 'value'); + PostBufferedSink::extractPost($postFields, 'dom[one][two][]', 'value_a'); + PostBufferedSink::extractPost($postFields, 'dom[one][two][]', 'value_b'); + PostBufferedSink::extractPost($postFields, 'dam[]', 'value_a'); + PostBufferedSink::extractPost($postFields, 'dam[]', 'value_b'); + PostBufferedSink::extractPost($postFields, 'dum[sum]', 'value'); + $this->assertSame([ + 'dem' => 'value', + 'dom' => [ + 'one' => [ + 'two' => [ + 'value_a', + 'value_b', + ], + ], + ], + 'dam' => [ + 'value_a', + 'value_b', + ], + 'dum' => [ + 'sum' => 'value', + ], + ], $postFields); + } +}