Update UploadedFile's tests for SAPI and use of *_uploaded_file functions

This commit is contained in:
PJ Dietz 2015-05-02 10:11:08 -04:00
parent 257f2b7610
commit a93b37a548
3 changed files with 117 additions and 36 deletions

View File

@ -19,11 +19,35 @@ class UploadedFile implements UploadedFileInterface
private $tmpName;
/**
* @param string $name
* @param string $type
* @param int $size
* @param string $tmpName
* @param int $error
* Create a new Uri. The arguments correspond with keys from arrays
* provided by $_FILES. For example, given this structure for $_FILES:
*
* array(
* 'avatar' => arrary(
* 'name' => 'my-avatar.png',
* 'type' => 'image/png',
* 'size' => 90996,
* 'tmp_name' => 'phpUxcOty',
* 'error' => 0
* )
* )
*
* ...use this call:
*
* new UploadedFile(
* $_FILES['avatar']['name'],
* $_FILES['avatar']['type'],
* $_FILES['avatar']['size'],
* $_FILES['avatar']['tmp_name'],
* $_FILES['avatar']['error']
* );
*
* @param string $name Name of the file; provided by the client
* @param string $type Media type of the file; provided by the client
* @param int $size The file size in bytes.
* @param string $tmpName Local filesystem name of the file
* @param int $error One of PHP's UPLOAD_ERR_XXX constants.
* @see http://php.net/manual/en/features.file-upload.errors.php
*/
public function __construct($name, $type, $size, $tmpName, $error)
{
@ -44,31 +68,34 @@ class UploadedFile implements UploadedFileInterface
* Retrieve a stream representing the uploaded file.
*
* This method returns a StreamInterface instance, representing the
* uploaded file. The purpose of this method is to allow utilizing native PHP
* uploaded file. The purpose of this method is to allow using native PHP
* stream functionality to manipulate the file upload, such as
* stream_copy_to_stream() (though the result will need to be decorated in a
* native PHP stream wrapper to work with such functions).
* stream_copy_to_stream() (though the result will need to be decorated in
* a native PHP stream wrapper to work with such functions).
*
* If the moveTo() method has been called previously, this method will raise
* an exception.
* If the moveTo() method has been called previously, this method will
* raise an exception.
*
* @return StreamInterface Stream representation of the uploaded file.
* @throws \RuntimeException in cases when no stream is available or can be
* created.
* @throws \RuntimeException in cases when no stream is available or can
* be created.
*/
public function getStream()
{
if ($this->moved) {
throw new \RuntimeException("File has already been moved");
}
if (php_sapi_name() !== "cli" && !is_uploaded_file($this->tmpName)) {
throw new \RuntimeException("File is not an uploaded file.");
}
return $this->stream;
}
/**
* Move the uploaded file to a new location.
*
* Use this method as an alternative to move_uploaded_file(). This method is
* guaranteed to work in both SAPI and non-SAPI environments.
* Use this method as an alternative to move_uploaded_file(). This method
* is guaranteed to work in both SAPI and non-SAPI environments.
*
* The original file or stream will be removed on completion.
*
@ -87,14 +114,10 @@ class UploadedFile implements UploadedFileInterface
if ($this->tmpName === null || !file_exists($this->tmpName)) {
throw new \RuntimeException("File " . $this->tmpName . " does not exist.");
}
$sapi = php_sapi_name();
$whitelist = ["apache", "apache2filter", "apache2handler", "cgi", "fpm-fcgi", "cgi-fcgi"];
if (in_array($sapi, $whitelist)) {
// @codeCoverageIgnoreStart
move_uploaded_file($this->tmpName, $path);
} else {
// @codeCoverageIgnoreEnd
if (php_sapi_name() === "cli") {
rename($this->tmpName, $path);
} else {
move_uploaded_file($this->tmpName, $path);
}
$this->moved = true;
}

View File

@ -0,0 +1,24 @@
<?php
namespace WellRESTed\Message;
class UploadedFileState
{
public static $php_sapi_name;
public static $is_uploaded_file;
}
function php_sapi_name()
{
return UploadedFileState::$php_sapi_name;
}
function move_uploaded_file($source, $target)
{
return rename($source, $target);
}
function is_uploaded_file($file)
{
return UploadedFileState::$is_uploaded_file;
}

View File

@ -3,8 +3,13 @@
namespace WellRESTed\Test\Message;
use WellRESTed\Message\UploadedFile;
use WellRESTed\Message\UploadedFileState;
// Hides several php core functions for testing.
require_once __DIR__ . "/../../../src/UploadedFileState.php";
/**
* @coversDefaultClass WellRESTed\Message\UploadedFile
* @uses WellRESTed\Message\UploadedFile
* @uses WellRESTed\Message\Stream
* @uses WellRESTed\Message\NullStream
@ -17,6 +22,7 @@ class UploadedFileTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
parent::setUp();
UploadedFileState::$php_sapi_name = "cli";
$this->tmpName = tempnam(sys_get_temp_dir(), "tst");
$this->movePath = tempnam(sys_get_temp_dir(), "tst");
}
@ -36,8 +42,8 @@ class UploadedFileTest extends \PHPUnit_Framework_TestCase
// getStream
/**
* @covers WellRESTed\Message\UploadedFile::__construct
* @covers WellRESTed\Message\UploadedFile::getStream
* @covers ::__construct
* @covers ::getStream
*/
public function testGetStreamReturnsStreamInterface()
{
@ -46,22 +52,21 @@ class UploadedFileTest extends \PHPUnit_Framework_TestCase
}
/**
* @covers WellRESTed\Message\UploadedFile::__construct
* @covers WellRESTed\Message\UploadedFile::getStream
* @covers ::__construct
* @covers ::getStream
*/
public function testGetStreamReturnsStreamWrappingUploadedFile()
{
$content = "Hello, World!";
file_put_contents($this->tmpName, $content);
$file = new UploadedFile("", "", 0, $this->tmpName, "");
$stream = $file->getStream();
$this->assertEquals($content, (string) $stream);
}
/**
* @covers WellRESTed\Message\UploadedFile::__construct
* @covers WellRESTed\Message\UploadedFile::getStream
* @covers ::__construct
* @covers ::getStream
*/
public function testGetStreamReturnsEmptyStreamForNoFile()
{
@ -70,27 +75,56 @@ class UploadedFileTest extends \PHPUnit_Framework_TestCase
}
/**
* @covers WellRESTed\Message\UploadedFile::__construct
* @covers WellRESTed\Message\UploadedFile::getStream
* @covers ::__construct
* @covers ::getStream
* @expectedException \RuntimeException
*/
public function testGetStreamThrowsExceptionAfterMoveTo()
{
$content = "Hello, World!";
file_put_contents($this->tmpName, $content);
$file = new UploadedFile("", "", 0, $this->tmpName, "");
$file->moveTo($this->movePath);
$file->getStream();
}
/**
* @covers ::__construct
* @covers ::getStream
* @expectedException \RuntimeException
*/
public function testGetStreamThrowsExceptionForNonUploadedFile()
{
UploadedFileState::$php_sapi_name = "apache";
UploadedFileState::$is_uploaded_file = false;
$file = new UploadedFile("", "", 0, "", 0);
$file->getStream();
}
// ------------------------------------------------------------------------
// move
// moveTo
/**
* @covers WellRESTed\Message\UploadedFile::moveTo
* @covers ::moveTo
*/
public function testMoveToRelocatesUploadedFileToDestiationIfExists()
public function testMoveToSapiRelocatesUploadedFileToDestiationIfExists()
{
UploadedFileState::$php_sapi_name = "fpm-fcgi";
$content = "Hello, World!";
file_put_contents($this->tmpName, $content);
$originalMd5 = md5_file($this->tmpName);
$file = new UploadedFile("", "", 0, $this->tmpName, "");
$file->moveTo($this->movePath);
$this->assertEquals($originalMd5, md5_file($this->movePath));
}
/**
* @covers ::moveTo
*/
public function testMoveToNonSapiRelocatesUploadedFileToDestiationIfExists()
{
$content = "Hello, World!";
file_put_contents($this->tmpName, $content);
@ -103,10 +137,10 @@ class UploadedFileTest extends \PHPUnit_Framework_TestCase
}
/**
* @covers WellRESTed\Message\UploadedFile::moveTo
* @covers ::moveTo
* @expectedException \RuntimeException
*/
public function testThrowsExcpetionOnSubsequentCallToMoveTo()
public function testMoveToThrowsExcpetionOnSubsequentCall()
{
$content = "Hello, World!";
file_put_contents($this->tmpName, $content);