[Form] Improved FileField to store files in a temporary location in case validation fails

This commit is contained in:
Bernhard Schussek 2010-11-23 01:18:08 +01:00 committed by Fabien Potencier
parent f2f0d044c3
commit e0aa3f30a8
4 changed files with 234 additions and 5 deletions

View File

@ -1,5 +1,8 @@
<input type="file"
id="<?php echo $field->getId() ?>"
name="<?php echo $field->getName() ?>"
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
/>
id="<?php echo $field['file']->getId() ?>"
name="<?php echo $field['file']->getName() ?>"
<?php if ($field['file']->isDisabled()): ?>disabled="disabled"<?php endif ?>
/>
<?php echo $view['form']->render($field['token']) ?>
<?php echo $view['form']->render($field['original_name']) ?>

View File

@ -135,5 +135,9 @@
{% endblock percent_field %}
{% block file_field %}
{% set group = field %}
{% set field = group.file %}
<input type="file" {% display field_attributes %} />
{{ group.token|render }}
{{ group.original_name|render }}
{% endblock file_field %}

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\Form;
use Symfony\Component\HttpFoundation\File\File;
/*
* This file is part of the Symfony framework.
*
@ -14,8 +16,103 @@ namespace Symfony\Component\Form;
/**
* A file field to upload files.
*/
class FileField extends Field
class FileField extends FieldGroup
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addRequiredOption('secret');
$this->addOption('tmp_dir', sys_get_temp_dir());
parent::configure();
$this->add(new Field('file'));
$this->add(new HiddenField('token'));
$this->add(new HiddenField('original_name'));
}
/**
* Moves the file to a temporary location to prevent its deletion when
* the PHP process dies
*
* This way the file can survive if the form does not validate and is
* resubmitted.
*
* @see Symfony\Component\Form\FieldGroup::preprocessData()
*/
protected function preprocessData(array $data)
{
if ($data['file']) {
$data['file']->move($this->getTmpPath($data['token']));
$data['original_name'] = $data['file']->getOriginalName();
$data['file'] = '';
}
return $data;
}
/**
* Turns a file path into an array of field values
*
* @see Symfony\Component\Form\Field::normalize()
*/
protected function normalize($path)
{
srand(microtime(true));
return array(
'file' => '',
'token' => rand(100000, 999999),
'original_name' => '',
);
}
/**
* Turns an array of field values into a file path
*
* @see Symfony\Component\Form\Field::denormalize()
*/
protected function denormalize($data)
{
$path = $this->getTmpPath($data['token']);
return file_exists($path) ? $path : $this->getData();
}
/**
* Returns the absolute temporary file path for the given token
*
* @param string $token
*/
protected function getTmpPath($token)
{
return realpath($this->getOption('tmp_dir')) . '/' . $this->getTmpName($token);
}
/**
* Returns the temporary file name for the given token
*
* @param string $token
*/
protected function getTmpName($token)
{
return md5(session_id() . $this->getOption('secret') . $token);
}
/**
* Returns the original name of the uploaded file
*
* @return string
*/
public function getOriginalName()
{
$data = $this->getNormalizedData();
return $data['original_name'];
}
/**
* {@inheritDoc}
*/

View File

@ -0,0 +1,125 @@
<?php
namespace Symfony\Tests\Component\Form;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Form\FileField;
class FileFieldTest extends \PHPUnit_Framework_TestCase
{
public static $tmpFiles = array();
protected static $tmpDir;
public static function setUpBeforeClass()
{
self::$tmpDir = sys_get_temp_dir();
// we need a session ID
@session_start();
}
public 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 testBindUploadsNewFiles()
{
$field = new FileField('file', array(
'secret' => '$secret$',
'tmp_dir' => self::$tmpDir,
));
$tmpPath = self::$tmpDir . '/' . md5(session_id() . '$secret$' . '12345');
$that = $this;
$file = $this->getMock('Symfony\Component\HttpFoundation\File\UploadedFile', array(), array(), '', false);
$file->expects($this->once())
->method('move')
->with($this->equalTo($tmpPath))
->will($this->returnCallback(function ($path) use ($that) {
$that->createTmpFile($path);
}));
$file->expects($this->any())
->method('getOriginalName')
->will($this->returnValue('original_name.jpg'));
$field->bind(array(
'file' => $file,
'token' => '12345',
'original_name' => '',
));
$this->assertTrue(file_exists($tmpPath));
$this->assertEquals(array(
'file' => '',
'token' => '12345',
'original_name' => 'original_name.jpg',
), $field->getDisplayedData());
$this->assertEquals($tmpPath, $field->getData());
}
public function testBindKeepsUploadedFilesOnErrors()
{
$field = new FileField('file', array(
'secret' => '$secret$',
'tmp_dir' => self::$tmpDir,
));
$tmpPath = self::$tmpDir . '/' . md5(session_id() . '$secret$' . '12345');
$this->createTmpFile($tmpPath);
$field->bind(array(
'file' => '',
'token' => '12345',
'original_name' => 'original_name.jpg',
));
$this->assertTrue(file_exists($tmpPath));
$this->assertEquals(array(
'file' => '',
'token' => '12345',
'original_name' => 'original_name.jpg',
), $field->getDisplayedData());
$this->assertEquals($tmpPath, $field->getData());
}
public function testBindKeepsOldFileIfNotOverwritten()
{
$field = new FileField('file', array(
'secret' => '$secret$',
'tmp_dir' => self::$tmpDir,
));
$oldPath = tempnam(sys_get_temp_dir(), 'FileFieldTest');
$this->createTmpFile($oldPath);
$field->setData($oldPath);
$this->assertEquals($oldPath, $field->getData());
$field->bind(array(
'file' => '',
'token' => '12345',
'original_name' => '',
));
$this->assertTrue(file_exists($oldPath));
$this->assertEquals(array(
'file' => '',
'token' => '12345',
'original_name' => '',
), $field->getDisplayedData());
$this->assertEquals($oldPath, $field->getData());
}
}