Skip to content

Commit

Permalink
[Form][HttpFoundation] Improved File and UploadedFile class
Browse files Browse the repository at this point in the history
  • Loading branch information
Bernhard Schussek authored and fabpot committed Jan 3, 2011
1 parent 708c780 commit 8513082
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 25 deletions.
17 changes: 14 additions & 3 deletions src/Symfony/Component/Form/FileField.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ protected function preprocessData(array $data)
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;
Expand Down Expand Up @@ -123,13 +124,23 @@ protected function denormalize($data)
}

/**
* 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'));
}

/**
Expand Down
80 changes: 77 additions & 3 deletions src/Symfony/Component/HttpFoundation/File/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -533,6 +563,26 @@ public function getPath()
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.
*
Expand Down Expand Up @@ -564,16 +614,40 @@ public function size()
}

/**
* 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);
}
}
56 changes: 43 additions & 13 deletions src/Symfony/Component/HttpFoundation/File/UploadedFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,34 @@
*/
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;

/**
Expand All @@ -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)
{
Expand All @@ -62,13 +86,7 @@ public function __construct($path, $originalName, $mimeType, $size, $error)
}

/**
* 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()
{
Expand Down Expand Up @@ -105,21 +123,33 @@ public function getError()
}

/**
* 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));
}

$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);
}
}
}
14 changes: 10 additions & 4 deletions tests/Symfony/Tests/Component/Form/FileFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -44,15 +45,20 @@ public function createTmpFile($path)

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')
Expand Down
38 changes: 36 additions & 2 deletions tests/Symfony/Tests/Component/HttpFoundation/File/FileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ public function testGetPathReturnsAbsolutePath()
$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());
Expand Down Expand Up @@ -62,13 +76,33 @@ public function testSizeReturnsFileSize()
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($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->move($targetPath);
$file->rename('test.target.gif');

$this->assertTrue(file_exists($targetPath));
$this->assertFalse(file_exists($path));
Expand Down

0 comments on commit 8513082

Please sign in to comment.