[Form][HttpFoundation] Improved File and UploadedFile class

This commit is contained in:
Bernhard Schussek 2011-01-03 15:47:20 +01:00 committed by Fabien Potencier
parent 708c780213
commit 8513082007
5 changed files with 180 additions and 25 deletions

View File

@ -84,7 +84,8 @@ class FileField extends FieldGroup
throw new FormException('A PHP extension stopped the file upload (UPLOAD_ERR_EXTENSION)');
case UPLOAD_ERR_OK:
default:
$data['file']->move($this->getTmpPath($data['token']));
$data['file']->move($this->getTmpDir());
$data['file']->rename($this->getTmpName($data['token']));
$data['original_name'] = $data['file']->getOriginalName();
$data['file'] = '';
break;
@ -123,13 +124,23 @@ class FileField extends FieldGroup
}
/**
* Returns the absolute temporary file path for the given token
* Returns the absolute temporary path to the uploaded file
*
* @param string $token
*/
protected function getTmpPath($token)
{
return realpath($this->getOption('tmp_dir')) . '/' . $this->getTmpName($token);
return $this->getTmpDir() . DIRECTORY_SEPARATOR . $this->getTmpName($token);
}
/**
* Returns the temporary directory where files are stored
*
* @param string $token
*/
protected function getTmpDir()
{
return realpath($this->getOption('tmp_dir'));
}
/**

View File

@ -438,12 +438,42 @@ class File
'x-world/x-vrml' => 'wrl',
);
/**
* Stores the absolute path to the document root directory
* @var string
*/
static protected $documentRoot;
/**
* The absolute path to the file without dots
* @var string
*/
protected $path;
/**
* Sets the path t the document root directory
*
* @param string $documentRoot
*/
static public function setDocumentRoot($documentRoot)
{
if (!is_dir($documentRoot)) {
throw new \LogicException($documentRoot . ' is no directory');
}
self::$documentRoot = realpath($documentRoot);
}
/**
* Returns the path to the document root directory
*
* @return string
*/
static public function getDocumentRoot()
{
return self::$documentRoot;
}
/**
* Constructs a new file from the given path.
*
@ -533,6 +563,26 @@ class File
return $this->path;
}
/**
* Returns the path relative to the document root
*
* You can set the document root using the static method setDocumentRoot().
* If the file is outside of the document root, this method returns an
* empty string.
*
* @return string The relative file path
*/
public function getWebPath()
{
$root = self::$documentRoot;
if (strpos($this->path, $root) === false) {
return '';
}
return str_replace(array($root, DIRECTORY_SEPARATOR), array('', '/'), $this->path);
}
/**
* Returns the mime type of the file.
*
@ -564,16 +614,40 @@ class File
}
/**
* Moves the file to a new location.
* Moves the file to a new directory and gives it a new filename
*
* @param string $newPath
* @param string $directory The new directory
* @param string $filename The new file name
* @throws FileException When the file could not be moved
*/
public function move($newPath)
protected function doMove($directory, $filename)
{
$newPath = $directory . DIRECTORY_SEPARATOR . $filename;
if (!rename($this->getPath(), $newPath)) {
throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath));
}
$this->path = realpath($newPath);
}
/**
* Moves the file to a new location.
*
* @param string $directory
*/
public function move($directory)
{
$this->doMove($directory, $this->getName());
}
/**
* Renames the file
*
* @param string $name The new file name
*/
public function rename($name)
{
$this->doMove($this->getDirectory(), $name);
}
}

View File

@ -20,10 +20,34 @@ use Symfony\Component\HttpFoundation\File\Exception\FileException;
*/
class UploadedFile extends File
{
/**
* The original name of the uploaded file
* @var string
*/
protected $originalName;
/**
* The mime type provided by the uploader
* @var string
*/
protected $mimeType;
/**
* The file size provided by the uploader
* @var integer
*/
protected $size;
/**
* The UPLOAD_ERR_XXX constant provided by the uploader
* @var integer
*/
protected $error;
/**
* Whether the uploaded file has already been moved
* @var boolean
*/
protected $moved = false;
/**
@ -35,7 +59,7 @@ class UploadedFile extends File
* @param string $type The type of the file as provided by PHP
* @param integer $size The file size
* @param string $error The error constant of the upload. Should be
* one of PHP's UPLOAD_XXX constants.
* one of PHP's UPLOAD_ERR_XXX constants.
*/
public function __construct($path, $originalName, $mimeType, $size, $error)
{
@ -62,13 +86,7 @@ class UploadedFile extends File
}
/**
* Returns the mime type of the file.
*
* The mime type is guessed using the functions finfo(), mime_content_type()
* and the system binary "file" (in this order), depending on which of those
* is available on the current operating system.
*
* @returns string The guessed mime type, e.g. "application/pdf"
* @inheritDoc
*/
public function getMimeType()
{
@ -105,13 +123,13 @@ class UploadedFile extends File
}
/**
* Moves the file to a new location.
*
* @param string $newPath
* @inheritDoc
*/
public function move($newPath)
protected function doMove($directory, $filename)
{
if (!$this->moved) {
$newPath = $directory . DIRECTORY_SEPARATOR . $filename;
if (!move_uploaded_file($this->getPath(), $newPath)) {
throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath));
}
@ -119,7 +137,19 @@ class UploadedFile extends File
$this->moved = true;
$this->path = realpath($newPath);
} else {
parent::move($newPath);
parent::doMove($directory, $filename);
}
}
/**
* @inheritDoc
*/
public function move($directory)
{
if (!$this->moved) {
$this->doMove($directory, $this->originalName);
} else {
parent::move($directory);
}
}
}

View File

@ -3,6 +3,7 @@
namespace Symfony\Tests\Component\Form;
use Symfony\Component\Form\FileField;
use Symfony\Component\HttpFoundation\File\File;
class FileFieldTest extends \PHPUnit_Framework_TestCase
{
@ -44,15 +45,20 @@ class FileFieldTest extends \PHPUnit_Framework_TestCase
public function testBindUploadsNewFiles()
{
$tmpPath = realpath(self::$tmpDir) . '/' . md5(session_id() . '$secret$' . '12345');
$tmpDir = realpath(self::$tmpDir);
$tmpName = md5(session_id() . '$secret$' . '12345');
$tmpPath = $tmpDir . DIRECTORY_SEPARATOR . $tmpName;
$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);
->with($this->equalTo($tmpDir));
$file->expects($this->once())
->method('rename')
->with($this->equalTo($tmpName))
->will($this->returnCallback(function ($directory) use ($that, $tmpPath) {
$that->createTmpFile($tmpPath);
}));
$file->expects($this->any())
->method('getOriginalName')

View File

@ -20,6 +20,20 @@ class FileTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'test.gif', $this->file->getPath());
}
public function testGetWebPathReturnsPathRelativeToDocumentRoot()
{
File::setDocumentRoot(__DIR__);
$this->assertEquals('/Fixtures/test.gif', $this->file->getWebPath());
}
public function testGetWebPathReturnsEmptyPathIfOutsideDocumentRoot()
{
File::setDocumentRoot(__DIR__.'/Fixtures/directory');
$this->assertEquals('', $this->file->getWebPath());
}
public function testGetNameReturnsNameWithExtension()
{
$this->assertEquals('test.gif', $this->file->getName());
@ -62,13 +76,33 @@ class FileTest extends \PHPUnit_Framework_TestCase
public function testMove()
{
$path = __DIR__.'/Fixtures/test.copy.gif';
$targetPath = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'test.target.gif';
$targetDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'directory';
$targetPath = $targetDir.DIRECTORY_SEPARATOR.'test.copy.gif';
@unlink($path);
@unlink($targetPath);
copy(__DIR__.'/Fixtures/test.gif', $path);
$file = new File($path);
$file->move($targetPath);
$file->move($targetDir);
$this->assertTrue(file_exists($targetPath));
$this->assertFalse(file_exists($path));
$this->assertEquals($targetPath, $file->getPath());
@unlink($path);
@unlink($targetPath);
}
public function testRename()
{
$path = __DIR__.'/Fixtures/test.copy.gif';
$targetPath = __DIR__.'/Fixtures/test.target.gif';
@unlink($path);
@unlink($targetPath);
copy(__DIR__.'/Fixtures/test.gif', $path);
$file = new File($path);
$file->rename('test.target.gif');
$this->assertTrue(file_exists($targetPath));
$this->assertFalse(file_exists($path));