Skip to content

Commit cccbc5e

Browse files
committed
allow UploadedFiles to use streams that are not based on files
1 parent a7f1de8 commit cccbc5e

File tree

2 files changed

+74
-12
lines changed

2 files changed

+74
-12
lines changed

src/Psr7/Message/UploadedFile.php

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
use Psr\Http\Message\StreamInterface;
77
use Psr\Http\Message\UploadedFileInterface;
88
use RuntimeException;
9+
use Throwable;
910

1011
class UploadedFile implements UploadedFileInterface
1112
{
1213
protected bool $moved = false;
13-
protected string $filePath;
1414

1515
/**
1616
* @param StreamInterface $stream
@@ -27,11 +27,6 @@ public function __construct(
2727
protected ?string $clientMediaType = null
2828
)
2929
{
30-
$file = $this->stream->getMetadata('uri');
31-
if ($file === null) {
32-
throw new InvalidArgumentException('Stream uri not available');
33-
}
34-
$this->filePath = $file;
3530
}
3631

3732
/**
@@ -50,9 +45,36 @@ public function moveTo(string $targetPath): void
5045
if ($this->moved) {
5146
throw new RuntimeException('Uploaded file already moved');
5247
}
53-
if (!@rename($this->filePath, $targetPath)) {
54-
throw new RuntimeException('Uploaded file could not be moved');
48+
49+
$filePath = $this->stream->getMetadata('uri');
50+
if ($filePath !== null) {
51+
if (!@rename($filePath, $targetPath)) {
52+
throw new RuntimeException('Uploaded file could not be moved');
53+
}
54+
$this->moved = true;
55+
return;
5556
}
57+
58+
if ($this->stream->tell() !== 0) {
59+
if (!$this->stream->isSeekable()) {
60+
throw new RuntimeException('Stream needs to be rewound, but is not seekable');
61+
}
62+
$this->stream->rewind();
63+
}
64+
65+
$target = fopen($targetPath, 'wb');
66+
if ($target === false) {
67+
throw new RuntimeException('Target path could not be opened');
68+
}
69+
70+
try {
71+
while (!$this->stream->eof()) {
72+
fwrite($target, $this->stream->read(4096));
73+
}
74+
} finally {
75+
fclose($target);
76+
}
77+
5678
$this->moved = true;
5779
}
5880

tests/UploadedFileTest.php

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Tests;
44

55
use Aternos\CurlPsr\Psr17\Psr17Factory;
6+
use Aternos\CurlPsr\Psr7\Stream\StringStream;
67
use InvalidArgumentException;
78
use PHPUnit\Framework\TestCase;
89
use Psr\Http\Message\StreamInterface;
@@ -66,11 +67,50 @@ public function testThrowWhenMovedToInvalidTarget(): void
6667
$uploadedFile->moveTo($target);
6768
}
6869

69-
public function testThrowIfStreamIsNotAFile(): void
70+
public function testWriteStreamContentIfStreamIsNotAFile(): void
7071
{
7172
$stream = $this->factory->createStream('test');
72-
$this->expectException(InvalidArgumentException::class);
73-
$this->expectExceptionMessage('Stream uri not available');
74-
$this->factory->createUploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, "file.txt", "text/plain");
73+
$upload = $this->factory->createUploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, "file.txt", "text/plain");
74+
75+
$this->target = tempnam(sys_get_temp_dir(), 'test');
76+
$upload->moveTo($this->target);
77+
78+
$this->assertFileExists($this->target);
79+
$this->assertEquals('test', file_get_contents($this->target));
80+
}
81+
82+
public function testThrowIfWriteTargetCannotBeOpened(): void
83+
{
84+
$stream = $this->factory->createStream('test');
85+
$upload = $this->factory->createUploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, "file.txt", "text/plain");
86+
87+
$this->expectException(RuntimeException::class);
88+
$this->expectExceptionMessage('Target path could not be opened');
89+
$upload->moveTo(sys_get_temp_dir() . "/non-existing-dir/file.txt");
90+
}
91+
92+
public function testRewindStreamIfNecessary(): void
93+
{
94+
$stream = $this->factory->createStream('test');
95+
$stream->read(1);
96+
$upload = $this->factory->createUploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, "file.txt", "text/plain");
97+
98+
$this->target = tempnam(sys_get_temp_dir(), 'test');
99+
$upload->moveTo($this->target);
100+
101+
$this->assertFileExists($this->target);
102+
$this->assertEquals('test', file_get_contents($this->target));
103+
}
104+
105+
public function testThrowIfStreamNeedsRewindButIsNotSeekable(): void
106+
{
107+
$stream = new StringStream('test', false);
108+
$stream->read(1);
109+
$upload = $this->factory->createUploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, "file.txt", "text/plain");
110+
$this->target = tempnam(sys_get_temp_dir(), 'test');
111+
112+
$this->expectException(RuntimeException::class);
113+
$this->expectExceptionMessage('Stream needs to be rewound, but is not seekable');
114+
$upload->moveTo($this->target);
75115
}
76116
}

0 commit comments

Comments
 (0)