[Form] Added test for last commit by kriswallsmith and improved dealing with original names

The form component should now guarantee to always pass an UploadedFile object to your model. There you can call getOriginalName() to retrieve the original name of the uploaded file. For security reasons, the real file name is a generated hash value.
This commit is contained in:
Bernhard Schussek 2011-05-04 16:58:39 +02:00
parent 4c6f26f008
commit bf1dfbbe99
11 changed files with 188 additions and 186 deletions

View File

@ -8,4 +8,5 @@
<?php echo $view['form']->widget($form['token']) ?>
<?php echo $view['form']->widget($form['name']) ?>
<?php echo $view['form']->widget($form['originalName']) ?>
</div>

View File

@ -221,6 +221,7 @@
{{ form_widget(form.file) }}
{{ form_widget(form.token) }}
{{ form_widget(form.name) }}
{{ form_widget(form.originalName) }}
</div>
{% endspaceless %}
{% endblock file_widget %}

View File

@ -26,8 +26,6 @@ class FileToArrayTransformer implements DataTransformerInterface
if (null === $file || '' === $file) {
return array(
'file' => '',
'token' => '',
'name' => '',
);
}
@ -37,8 +35,6 @@ class FileToArrayTransformer implements DataTransformerInterface
return array(
'file' => $file,
'token' => '',
'name' => '',
);
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Core\EventListener;
use Symfony\Component\Form\Events;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Event\FilterDataEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\File\File;
@ -42,17 +43,19 @@ class FixFileUploadListener implements EventSubscriberInterface
$form = $event->getForm();
$data = $event->getData();
if (!is_array($data)) {
return;
if (null === $data) {
$data = array();
}
// TODO should be disableable
if (!is_array($data)) {
throw new UnexpectedTypeException($data, 'array');
}
// TESTME
$data = array_merge(array(
$data = array_replace(array(
'file' => '',
'token' => '',
'name' => '',
'originalName' => '',
), $data);
// Newly uploaded file
@ -61,14 +64,15 @@ class FixFileUploadListener implements EventSubscriberInterface
$directory = $this->storage->getTempDir($data['token']);
$data['file']->move($directory);
$data['name'] = $data['file']->getName();
$data['originalName'] = $data['file']->getOriginalName();
}
// Existing uploaded file
if (!$data['file'] && $data['token'] && $data['name']) {
$path = $this->storage->getTempDir($data['token']) . DIRECTORY_SEPARATOR . $data ['name'];
$path = $this->storage->getTempDir($data['token']) . DIRECTORY_SEPARATOR . $data['name'];
if (file_exists($path)) {
$data['file'] = new File($path);
$data['file'] = new UploadedFile($path, $data['originalName'], null, null, null, true);
}
}

View File

@ -45,7 +45,8 @@ class FileType extends AbstractType
->addEventSubscriber(new FixFileUploadListener($this->storage), 10)
->add('file', 'field')
->add('token', 'hidden')
->add('name', 'hidden');
->add('name', 'hidden')
->add('originalName', 'hidden');
}
public function buildViewBottomUp(FormView $view, FormInterface $form)

View File

@ -50,6 +50,8 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
function getData();
function getNormData();
function getClientData();
function isBound();

View File

@ -71,7 +71,8 @@ class UploadedFile extends File
* @throws FileException If file_uploads is disabled
* @throws FileNotFoundException If the file does not exist
*/
public function __construct($path, $originalName, $mimeType, $size, $error, $moved = false)
public function __construct($path, $originalName, $mimeType = null,
$size = null, $error = null, $moved = false)
{
if (!ini_get('file_uploads')) {
throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path')));

View File

@ -664,8 +664,9 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
./input[@type="file"][@id="na&me_file"]
/following-sibling::input[@type="hidden"][@id="na&me_token"]
/following-sibling::input[@type="hidden"][@id="na&me_name"]
/following-sibling::input[@type="hidden"][@id="na&me_originalName"]
]
[count(./input)=3]
[count(./input)=4]
'
);
}

View File

@ -0,0 +1,167 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Component\Form\Extension\Core\EventListener;
use Symfony\Component\Form\Event\FilterDataEvent;
use Symfony\Component\Form\Extension\Core\EventListener\FixFileUploadListener;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FixFileUploadListenerTest extends \PHPUnit_Framework_TestCase
{
private $storage;
private $destination;
public function setUp()
{
$this->storage = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\TemporaryStorage')
->disableOriginalConstructor()
->getMock();
}
public function testValidNewlyUploadedFile()
{
$passedToken = null;
$this->storage->expects($this->any())
->method('getTempDir')
->will($this->returnCallback(function ($token) use (&$passedToken) {
$passedToken = $token;
return __DIR__.DIRECTORY_SEPARATOR.'tmp';
}));
$file = $this->createUploadedFileMock('randomhash', 'original.jpg', true);
$file->expects($this->once())
->method('move')
->with(__DIR__.DIRECTORY_SEPARATOR.'tmp');
$data = array(
'file' => $file,
'token' => '',
'name' => '',
'originalName' => '',
);
$form = $this->getMock('Symfony\Tests\Component\Form\FormInterface');
$event = new FilterDataEvent($form, $data);
$filter = new FixFileUploadListener($this->storage);
$filter->onBindClientData($event);
$this->assertEquals(array(
'file' => $file,
'name' => 'randomhash',
'originalName' => 'original.jpg',
'token' => $passedToken,
), $event->getData());
}
public function testExistingUploadedFile()
{
$test = $this;
$this->storage->expects($this->any())
->method('getTempDir')
->will($this->returnCallback(function ($token) use ($test) {
$test->assertSame('abcdef', $token);
return __DIR__.DIRECTORY_SEPARATOR.'Fixtures';
}));
$data = array(
'file' => '',
'token' => 'abcdef',
'name' => 'randomhash',
'originalName' => 'original.jpg',
);
$form = $this->getMock('Symfony\Tests\Component\Form\FormInterface');
$event = new FilterDataEvent($form, $data);
$filter = new FixFileUploadListener($this->storage);
$filter->onBindClientData($event);
$this->assertEquals(array(
'file' => new UploadedFile(
__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'randomhash',
'original.jpg',
null,
null,
null,
true // already moved
),
'name' => 'randomhash',
'originalName' => 'original.jpg',
'token' => 'abcdef',
), $event->getData());
}
public function testNullAndExistingFile()
{
$existingData = array(
'file' => new UploadedFile(
__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'randomhash',
'original.jpg',
null,
null,
null,
true // already moved
),
'name' => 'randomhash',
'originalName' => 'original.jpg',
'token' => 'abcdef',
);
$form = $this->getMock('Symfony\Tests\Component\Form\FormInterface');
$form->expects($this->any())
->method('getNormData')
->will($this->returnValue($existingData));
$event = new FilterDataEvent($form, null);
$filter = new FixFileUploadListener($this->storage);
$filter->onBindClientData($event);
$this->assertSame($existingData, $event->getData());
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testExpectNullOrArray()
{
$form = $this->getMock('Symfony\Tests\Component\Form\FormInterface');
$event = new FilterDataEvent($form, 'foobar');
$filter = new FixFileUploadListener($this->storage);
$filter->onBindClientData($event);
}
private function createUploadedFileMock($name, $originalName, $valid)
{
$file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile')
->disableOriginalConstructor()
->getMock();
$file->expects($this->any())
->method('getName')
->will($this->returnValue($name));
$file->expects($this->any())
->method('getOriginalName')
->will($this->returnValue($originalName));
$file->expects($this->any())
->method('isValid')
->will($this->returnValue($valid));
return $file;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 B

View File

@ -1,172 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\FileField;
use Symfony\Component\HttpFoundation\File\File;
class FileTypeTest extends TypeTestCase
{
public static $tmpFiles = array();
protected static $tmpDir;
protected $form;
public static function setUpBeforeClass()
{
self::$tmpDir = sys_get_temp_dir().DIRECTORY_SEPARATOR.'symfony-test';
if (!file_exists(self::$tmpDir)) {
mkdir(self::$tmpDir, 0777, true);
}
}
protected function setUp()
{
parent::setUp();
$this->form = $this->factory->create('file');
}
protected function tearDown()
{
foreach (self::$tmpFiles as $key => $file) {
@unlink($file);
unset(self::$tmpFiles[$key]);
}
}
public function createTmpFile($path)
{
self::$tmpFiles[] = $path;
file_put_contents($path, 'foobar');
}
public function testSubmitUploadsNewFiles()
{
$tmpDir = self::$tmpDir;
$generatedToken = '';
$this->storage->expects($this->atLeastOnce())
->method('getTempDir')
->will($this->returnCallback(function ($token) use ($tmpDir, &$generatedToken) {
// A 6-digit token is generated by FileUploader and passed
// to getTempDir()
$generatedToken = $token;
return $tmpDir;
}));
$file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile')
->disableOriginalConstructor()
->getMock();
$file->expects($this->once())
->method('move')
->with($this->equalTo($tmpDir));
$file->expects($this->any())
->method('isValid')
->will($this->returnValue(true));
$file->expects($this->any())
->method('getName')
->will($this->returnValue('original_name.jpg'));
$file->expects($this->any())
->method('getPath')
->will($this->returnValue($tmpDir.'/original_name.jpg'));
$this->form->bind(array(
'file' => $file,
'token' => '',
'name' => '',
));
$this->assertRegExp('/^\d{6}$/', $generatedToken);
$this->assertEquals(array(
'file' => $file,
'token' => $generatedToken,
'name' => 'original_name.jpg',
), $this->form->getClientData());
$this->assertEquals($tmpDir.'/original_name.jpg', $this->form->getData());
}
public function testSubmitKeepsUploadedFilesOnErrors()
{
$tmpDir = self::$tmpDir;
$tmpPath = $tmpDir . DIRECTORY_SEPARATOR . 'original_name.jpg';
$this->createTmpFile($tmpPath);
$this->storage->expects($this->atLeastOnce())
->method('getTempDir')
->with($this->equalTo('123456'))
->will($this->returnValue($tmpDir));
$this->form->bind(array(
'file' => null,
'token' => '123456',
'name' => 'original_name.jpg',
));
$this->assertTrue(file_exists($tmpPath));
$file = new File($tmpPath);
$this->assertEquals(array(
'file' => $file,
'token' => '123456',
'name' => 'original_name.jpg',
), $this->form->getClientData());
$this->assertEquals(realpath($tmpPath), realpath($this->form->getData()));
}
public function testSubmitEmpty()
{
$this->storage->expects($this->never())
->method('getTempDir');
$this->form->bind(array(
'file' => '',
'token' => '',
'name' => '',
));
$this->assertEquals(array(
'file' => '',
'token' => '',
'name' => '',
), $this->form->getClientData());
$this->assertEquals(null, $this->form->getData());
}
public function testSubmitEmptyKeepsExistingFiles()
{
$tmpPath = self::$tmpDir . DIRECTORY_SEPARATOR . 'original_name.jpg';
$this->createTmpFile($tmpPath);
$file = new File($tmpPath);
$this->storage->expects($this->never())
->method('getTempDir');
$this->form->setData($tmpPath);
$this->form->bind(array(
'file' => '',
'token' => '',
'name' => '',
));
$this->assertEquals(array(
'file' => $file,
'token' => '',
'name' => '',
), $this->form->getClientData());
$this->assertEquals(realpath($tmpPath), realpath($this->form->getData()));
}
}