Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UploadedFileFactory - Failing to create a readable UploadedFile from string #100

Merged
merged 1 commit into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Factory/UploadedFileFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ public function createUploadedFile(
): UploadedFileInterface {
$file = $stream->getMetadata('uri');

if (!is_string($file) || !is_readable($file)) {
if (!is_string($file) || !$stream->isReadable()) {
throw new InvalidArgumentException('File is not readable.');
}

if ($size === null) {
$size = $stream->getSize();
}

return new UploadedFile($file, $clientFilename, $clientMediaType, $size, $error);
return new UploadedFile($stream, $clientFilename, $clientMediaType, $size, $error);
}
}
30 changes: 22 additions & 8 deletions src/UploadedFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,36 @@ class UploadedFile implements UploadedFileInterface
protected $moved = false;

/**
* @param string $file The full path to the uploaded file provided by the client.
* @param string|null $name The file name.
* @param string|null $type The file media type.
* @param int|null $size The file size in bytes.
* @param int $error The UPLOAD_ERR_XXX code representing the status of the upload.
* @param bool $sapi Indicates if the upload is in a SAPI environment.
* @param string|StreamInterface $fileNameOrStream The full path to the uploaded file provided by the client,
* or a StreamInterface instance.
* @param string|null $name The file name.
* @param string|null $type The file media type.
* @param int|null $size The file size in bytes.
* @param int $error The UPLOAD_ERR_XXX code representing the status of the upload.
* @param bool $sapi Indicates if the upload is in a SAPI environment.
*/
public function __construct(
string $file,
$fileNameOrStream,
?string $name = null,
?string $type = null,
?int $size = null,
int $error = UPLOAD_ERR_OK,
bool $sapi = false
) {
$this->file = $file;
if ($fileNameOrStream instanceof StreamInterface) {
$file = $fileNameOrStream->getMetadata('uri');
if (!is_string($file)) {
throw new InvalidArgumentException('No URI associated with the stream.');
}
$this->file = $file;
$this->stream = $fileNameOrStream;
} elseif (is_string($fileNameOrStream)) {
$this->file = $fileNameOrStream;
} else {
throw new InvalidArgumentException(
'Please provide a string (full path to the uploaded file) or an instance of StreamInterface.'
);
}
$this->name = $name;
$this->type = $type;
$this->size = $size;
Expand Down
38 changes: 32 additions & 6 deletions tests/Factory/UploadedFileFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,19 @@ protected function prophesizeStreamInterfaceWithGetMetadataMethod(string $argKey
*/
public function testCreateUploadedFileWithInvalidUri()
{
$this->factory->createUploadedFile(
$this->prophesizeStreamInterfaceWithGetMetadataMethod('uri', null)
);
// Prophesize a `\Psr\Http\Message\StreamInterface` with a `getMetadata` method prophecy.
$streamProphecy = $this->prophesize(StreamInterface::class);

/** @noinspection PhpUndefinedMethodInspection */
$streamProphecy
->getMetadata('uri')
->willReturn(null)
->shouldBeCalled();

/** @var StreamInterface $stream */
$stream = $streamProphecy->reveal();

$this->factory->createUploadedFile($stream);
}

/**
Expand All @@ -79,8 +89,24 @@ public function testCreateUploadedFileWithInvalidUri()
*/
public function testCreateUploadedFileWithNonReadableFile()
{
$this->factory->createUploadedFile(
$this->prophesizeStreamInterfaceWithGetMetadataMethod('uri', 'non-readable')
);
// Prophesize a `\Psr\Http\Message\StreamInterface` with a `getMetadata` and `isReadable` method prophecies.
$streamProphecy = $this->prophesize(StreamInterface::class);

/** @noinspection PhpUndefinedMethodInspection */
$streamProphecy
->getMetadata('uri')
->willReturn('non-readable')
->shouldBeCalled();

/** @noinspection PhpUndefinedMethodInspection */
$streamProphecy
->isReadable()
->willReturn(false)
->shouldBeCalled();

/** @var StreamInterface $stream */
$stream = $streamProphecy->reveal();

$this->factory->createUploadedFile($stream);
}
}
73 changes: 73 additions & 0 deletions tests/UploadedFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@

use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;
use ReflectionProperty;
use RuntimeException;
use Slim\Psr7\Environment;
use Slim\Psr7\Factory\StreamFactory;
use Slim\Psr7\Factory\UploadedFileFactory;
use Slim\Psr7\Stream;
use Slim\Psr7\UploadedFile;

Expand Down Expand Up @@ -305,6 +309,75 @@ public function testMoveToStream()
$this->assertFileNotExists($fileName);
}

public function testFileUploadWithTempStream()
{
$streamFactory = function (...$args) {
return (new StreamFactory())->createStream(...$args);
};
$uploadedFileFactory = function (...$args) {
return (new UploadedFileFactory())->createUploadedFile(...$args);
};
$this->runFileUploadWithTempStreamTest($streamFactory, $uploadedFileFactory);
}

/**
* This test sequence has been inspired by UploadedFileFactoryTestCase from http-interop/http-factory-tests package.
*
* @param callable $streamFactory
* @param callable $uploadedFileFactory
*/
private function runFileUploadWithTempStreamTest(callable $streamFactory, callable $uploadedFileFactory)
{
$content = 'this is your capitan speaking';
$error = UPLOAD_ERR_OK;
$clientFilename = 'test.txt';
$clientMediaType = 'text/plain';

$stream = call_user_func($streamFactory, $content);
$file = call_user_func(
$uploadedFileFactory,
$stream,
strlen($content),
$error,
$clientFilename,
$clientMediaType
);

$this->assertInstanceOf(UploadedFileInterface::class, $file);
$this->assertSame($content, (string)$file->getStream());
$this->assertSame(strlen($content), $file->getSize());
$this->assertSame($error, $file->getError());
$this->assertSame($clientFilename, $file->getClientFilename());
$this->assertSame($clientMediaType, $file->getClientMediaType());
}

/**
* @expectedException InvalidArgumentException
*/
public function testCreateUploadedFileWithInvalidArguments()
{
new UploadedFile(42); // a random value that is neither a string nor an instance of StreamInterface
}

/**
* @expectedException InvalidArgumentException
*/
public function testCreateUploadedFileWithInvalidUri()
{
$streamProphecy = $this->prophesize(StreamInterface::class);

/** @noinspection PhpUndefinedMethodInspection */
$streamProphecy
->getMetadata('uri')
->willReturn(null)
->shouldBeCalled();
$stream = $streamProphecy->reveal();

// Test with a StreamInterface that returns `null`
// when `$stream->getMetadata('uri')` is called (which is an invalid case).
new UploadedFile($stream);
}

public function providerCreateFromGlobals()
{
return [
Expand Down