diff --git a/src/PSR7/Validators/BodyValidator/MultipartValidator.php b/src/PSR7/Validators/BodyValidator/MultipartValidator.php index fb26f86d..e7e83ace 100644 --- a/src/PSR7/Validators/BodyValidator/MultipartValidator.php +++ b/src/PSR7/Validators/BodyValidator/MultipartValidator.php @@ -9,6 +9,7 @@ use cebe\openapi\spec\MediaType; use cebe\openapi\spec\Schema; use cebe\openapi\spec\Type as CebeType; +use InvalidArgumentException; use League\OpenAPIValidation\PSR7\Exception\NoPath; use League\OpenAPIValidation\PSR7\Exception\Validation\InvalidBody; use League\OpenAPIValidation\PSR7\Exception\Validation\InvalidHeaders; @@ -22,11 +23,14 @@ use League\OpenAPIValidation\Schema\SchemaValidator; use Psr\Http\Message\MessageInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\UploadedFileInterface; use Riverline\MultiPartParser\Converters\PSR7; use Riverline\MultiPartParser\StreamedPart; use RuntimeException; use const JSON_ERROR_NONE; +use function array_replace; use function in_array; +use function is_array; use function json_decode; use function json_last_error; use function json_last_error_msg; @@ -222,19 +226,17 @@ private function detectEncondingContentType(Encoding $encoding, StreamedPart $pa /** * ServerRequest does not have a plain HTTP body which we can parse. Instead, it has a parsed values in * getParsedBody() (POST data) and getUploadedFiles (FILES data) - * - * @param MediaType[] $mediaTypeSpecs */ private function validateServerRequestMultipart( OperationAddress $addr, ServerRequestInterface $message, Schema $schema ) : void { - $body = $message->getParsedBody(); + $body = (array) $message->getParsedBody(); - foreach ($message->getUploadedFiles() as $name => $file) { - $body[$name] = '~~~binary~~~'; - } + $files = $this->normalizeFiles($message->getUploadedFiles()); + + $body = array_replace($body, $files); $validator = new SchemaValidator($this->detectValidationStrategy($message)); try { @@ -263,4 +265,26 @@ private function validateServerRequestMultipart( // ...headers are parsed already by webserver... } } + + /** + * @param UploadedFileInterface[]|array[] $files + * + * @return mixed[] + */ + private function normalizeFiles(array $files) : array + { + $normalized = []; + + foreach ($files as $name => $file) { + if ($file instanceof UploadedFileInterface) { + $normalized[$name] = '~~~binary~~~'; + } elseif (is_array($file)) { + $normalized[$name] = $this->normalizeFiles($file); + } else { + throw new InvalidArgumentException('Invalid file tree in request'); + } + } + + return $normalized; + } } diff --git a/tests/FromCommunity/Issue62Test.php b/tests/FromCommunity/Issue62Test.php new file mode 100644 index 00000000..ac43fc8c --- /dev/null +++ b/tests/FromCommunity/Issue62Test.php @@ -0,0 +1,65 @@ +fromYaml($yaml)->getServerRequestValidator(); + + $psrRequest = (new ServerRequest('post', 'http://localhost:8000/api/v1/clam/scan')) + ->withUploadedFiles( + ServerRequest::normalizeFiles( + [ + 'upload' => [ + new UploadedFile('body1', 5, 0, 'upload_1.txt', 'text/plain'), + new UploadedFile('body2', 5, 0, 'upload_2.txt', 'text/plain'), + ], + ] + ) + ) + ->withMethod('PUT') + ->withHeader('Content-Type', 'multipart/form-data'); + + $validator->validate($psrRequest); + $this->addToAssertionCount(1); + } +}