zendframework / zend-form

Form component from Zend Framework
BSD 3-Clause "New" or "Revised" License
69 stars 87 forks source link

PSR7 in upload form ->setData() #227

Closed samsonasik closed 5 years ago

samsonasik commented 5 years ago

I'm trying latest zend-validator 2.11.0 and latest zend-inputfilter 2.9.0 which have psr7 support. I'm trying the following Form::setData() and Form::isvalid(). So, the following code:

            // $request is an instance of \Psr\Http\Message\ServerRequestInterface
            $postAndFileData = \array_merge_recursive(
                $request->getParsedBody(),
                $request->getUploadedFiles()
            );

           $form->setData($postAndFileData);
           $form->isValid();

I got the following errors:

basename() expects parameter 1 to be string, object given

I have the following form specs:


use Zend\Filter\File\RenameUpload;
use Zend\Validator\File\MimeType;
use Zend\Validator\File\Size;

    public function getInputFilterSpecification()
    {
        return [
            [
                'name' => 'filename',
                'required' => true,
                'filters' => [
                    [
                        'name' => RenameUpload::class,
                        'options' => [
                            'target'               => \getcwd() . '/public/uploads',
                            'use_upload_extension' => true,
                            'use_upload_name'      => true,
                            'overwrite'            => true,
                            'randomize'            => false,
                        ],
                    ],
                ],
                'validators' => [
                    [
                        'name' => Size::class,
                        'options' => [
                            'max' => '10MB',
                        ],
                    ],
                    [
                        'name' => MimeType::class,
                        'options' => [
                            'mimeType' => [
                                'image/jpg',
                                'image/jpeg',
                                'image/png',
                            ],
                        ]
                     ]
                ],
            ],
        ];
    }

When I try remove the validators, the filters got errors:

No PSR-17 Psr\Http\Message\StreamFactoryInterface present; cannot filter file. Please pass the stream_file_factory option with a Psr\Http\Message\StreamFactoryInterface instance when creating the filter for use with PSR-7.

The current workaround is by using the zend-psr7bridge :

$zendRequest        = Psr7ServerRequest::toZend($request);
$postAndFileData = \array_merge_recursive(
                $zendRequest->getPost()->toArray(),
                $zendRequest->getFiles()->toArray(),
);
froschdesign commented 5 years ago

@samsonasik First: the problem is not zend-form.

The current validators like Size or MimeType can not handle the PSR7 uploads.

And then there are two options for zend-filter missing:

  1. stream_factory:
    https://github.com/zendframework/zend-filter/blob/135670d33804113070c4f243e961250b248e9e04/src/File/RenameUpload.php#L412-L420
  2. upload_file_factory:
    https://github.com/zendframework/zend-filter/blob/135670d33804113070c4f243e961250b248e9e04/src/File/RenameUpload.php#L424-L432

Compare with documentation of zend-filter: "Handle a PSR-7 uploaded file"

samsonasik commented 5 years ago

@froschdesign ok, thank you.

froschdesign commented 5 years ago

@samsonasik For the moment, you still need to use the workaround.

froschdesign commented 5 years ago

@samsonasik Please run a Composer update and check again! (https://github.com/zendframework/zend-validator/releases/tag/release-2.12.0)

samsonasik commented 5 years ago

@froschdesign I can verify that upload and rename itself working now. However, when calling $form->getData(), with zend-diactoros request,, it got the following data:

array (size=1)
  'filename' => 
    object(Zend\Diactoros\UploadedFile)[966]
      private 'clientFilename' => string 'Untitled-1.png' (length=14)
      private 'clientMediaType' => string 'image/png' (length=9)
      private 'error' => int 0
      private 'file' => null
      private 'moved' => boolean false
      private 'size' => int 761087
      private 'stream' => 
        object(Zend\Diactoros\Stream)[967]
          protected 'resource' => resource(12, stream)
          protected 'stream' => string '/Users/samsonasik/www/expressive-fileprg-tutorial/public/uploads/Untitled-1_5c51c49e1d0855_97287659.png' (length=103)

When using Psr7ServerRequest::toZend($request), it got the following:

array (size=1)
  'filename' => 
    array (size=5)
      'name' => string 'Untitled-1.png' (length=14)
      'type' => string 'image/png' (length=9)
      'size' => int 761087
      'tmp_name' => string '/Users/samsonasik/www/expressive-fileprg-tutorial/public/uploads/Untitled-1_5c51c49e1d0855_97287659.png' (length=103)
      'error' => int 0

Is there something todo in the Form->getData() for it so the values of form->getData() consistent for it (using array values)?

froschdesign commented 5 years ago

@samsonasik zend-form does nothing here. The return value is created by the filter, in your case the RenameUpload filter.

https://github.com/zendframework/zend-filter/blob/135670d33804113070c4f243e961250b248e9e04/src/File/RenameUpload.php#L199-L202

My suggestion: You should create a new feature request for the RenameUpload filter that the user can specify the return type for the filter.

samsonasik commented 5 years ago

@froschdesign Thank you. I do this to convert it to array for now:

use Psr\Http\Message\UploadedFileInterface;

$data = $form->getData();

\array_walk_recursive($data, function (& $value) {
    if ($value instanceof UploadedFileInterface) {
        $value = [
            'name'     => $value->getClientFilename(),
            'type'     => $value->getClientMediaType(),
            'size'     => $value->getSize(),
            'tmp_name' => $value->getStream()->getMetadata('uri'),
            'error'    => $value->getError(),
        ];
    }
});