Skip to content

Commit a0d191d

Browse files
committed
Fix non-selected files
- Converted the non-selected files into nulls.
1 parent 707b9cf commit a0d191d

File tree

4 files changed

+63
-48
lines changed

4 files changed

+63
-48
lines changed

src/Drivers/LaravelHttpServer.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Illuminate\Contracts\Debug\ExceptionHandler;
1717
use Illuminate\Contracts\Http\Kernel as HttpKernel;
1818
use Illuminate\Foundation\Testing\Concerns\WithoutExceptionHandlingHandler;
19-
use Illuminate\Http\Concerns\InteractsWithInput;
2019
use Illuminate\Http\Request;
2120
use Illuminate\Http\UploadedFile;
2221
use Illuminate\Routing\UrlGenerator;
@@ -28,7 +27,6 @@
2827
use Pest\Browser\Http\RequestBodyParser;
2928
use Pest\Browser\Playwright\Playwright;
3029
use Psr\Log\NullLogger;
31-
use Symfony\Component\HttpFoundation\File\UploadedFile as SymfonyUploadedFile;
3230
use Symfony\Component\Mime\MimeTypes;
3331
use Throwable;
3432

@@ -318,20 +316,22 @@ private function handleRequest(AmpRequest $request): Response
318316
}
319317

320318
/**
321-
* Taken from Laravel because we can't manipulate the test flag
322-
*
323-
* @param array<SymfonyUploadedFile[]|SymfonyUploadedFile> $files
324-
* @return array<UploadedFile[]|UploadedFile>
325-
*
326-
* @see InteractsWithInput
319+
* Convert the array to a Laravel file, to keep the test flag.
320+
* If the file is empty, we return the original array, so Laravel
321+
* would convert it to a null.
327322
*/
323+
// @phpstan-ignore-next-line
328324
private function convertUploadedFiles(array $files): array
329325
{
330326
// @phpstan-ignore-next-line
331-
return array_map(function (array|SymfonyUploadedFile $file) {
332-
return is_array($file)
327+
return array_map(function (array $file) {
328+
if (isset($file['error']) && $file['error'] === UPLOAD_ERR_NO_FILE) {
329+
return $file;
330+
}
331+
332+
return array_is_list($file)
333333
? $this->convertUploadedFiles($file)
334-
: UploadedFile::createFromBase($file, true);
334+
: new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], true);
335335
}, $files);
336336
}
337337

src/Http/MultipartParser.php

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use Amp\Http\Server\Request as AmpRequest;
88
use InvalidArgumentException;
9-
use Symfony\Component\HttpFoundation\File\UploadedFile;
109

1110
/**
1211
* Derived work from the MultipartParser in ReactPHP.
@@ -19,7 +18,7 @@
1918
final class MultipartParser
2019
{
2120
/**
22-
* @var array{"$_POST": array<array-key, mixed[]|string>, "$_FILES": array<array-key, UploadedFile[]|UploadedFile>}
21+
* @var array{"$_POST": array<array-key, mixed[]|string>, "$_FILES": array<array-key, array{tmp_file: string, error: int, name: string, type: string, size: int}[]|array{tmp_file: string, error: int, name: string, type: string, size: int}>}
2322
*/
2423
private array $superglobals = ['$_POST' => [], '$_FILES' => []];
2524

@@ -88,7 +87,7 @@ public function __construct(
8887
}
8988

9089
/**
91-
* @return array{array<mixed[]|string>, array<UploadedFile[]|UploadedFile>}
90+
* @return array{array<mixed[]|string>, array<array{tmp_file: string, error: int, name: string, type: string, size: int}[]|array{tmp_file: string, error: int, name: string, type: string, size: int}>}
9291
*/
9392
public function parse(AmpRequest $request, string $body): array
9493
{
@@ -219,8 +218,12 @@ private function parseFile(string $name, string $filename, ?string $contentType,
219218
);
220219
}
221220

222-
private function parseUploadedFile(string $filename, ?string $contentType, string $contents): ?UploadedFile
221+
/**
222+
* @return array{tmp_file: string, error: int, name: string, type: string, size: int}|null
223+
*/
224+
private function parseUploadedFile(string $filename, ?string $contentType, string $contents): ?array
223225
{
226+
$contentType ??= 'application/octet-stream';
224227
$size = strlen($contents);
225228

226229
// no file selected (zero size and empty filename)
@@ -230,13 +233,13 @@ private function parseUploadedFile(string $filename, ?string $contentType, strin
230233
return null;
231234
}
232235

233-
return new UploadedFile(
234-
'',
235-
$filename,
236-
$contentType,
237-
UPLOAD_ERR_NO_FILE,
238-
true,
239-
);
236+
return [
237+
'tmp_name' => '',
238+
'error' => UPLOAD_ERR_NO_FILE,
239+
'name' => $filename,
240+
'type' => $contentType,
241+
'size' => $size,
242+
];
240243
}
241244

242245
// ignore excessive number of file uploads
@@ -246,36 +249,36 @@ private function parseUploadedFile(string $filename, ?string $contentType, strin
246249

247250
// file exceeds "upload_max_filesize" ini setting
248251
if ($size > $this->uploadMaxFilesize) {
249-
return new UploadedFile(
250-
'',
251-
$filename,
252-
$contentType,
253-
UPLOAD_ERR_INI_SIZE,
254-
true,
255-
);
252+
return [
253+
'tmp_name' => '',
254+
'error' => UPLOAD_ERR_INI_SIZE,
255+
'name' => $filename,
256+
'type' => $contentType,
257+
'size' => $size,
258+
];
256259
}
257260

258261
// file exceeds MAX_FILE_SIZE value
259262
if ($this->maxFileSize !== null && $size > $this->maxFileSize) {
260-
return new UploadedFile(
261-
'',
262-
$filename,
263-
$contentType,
264-
UPLOAD_ERR_FORM_SIZE,
265-
true,
266-
);
263+
return [
264+
'tmp_name' => '',
265+
'error' => UPLOAD_ERR_FORM_SIZE,
266+
'name' => $filename,
267+
'type' => $contentType,
268+
'size' => $size,
269+
];
267270
}
268271

269272
$tempFileName = tempnam(sys_get_temp_dir(), 'php');
270273
file_put_contents($tempFileName, $contents);
271274

272-
return new UploadedFile(
273-
$tempFileName,
274-
$filename,
275-
$contentType,
276-
UPLOAD_ERR_OK,
277-
true,
278-
);
275+
return [
276+
'tmp_name' => $tempFileName,
277+
'error' => UPLOAD_ERR_OK,
278+
'name' => $filename,
279+
'type' => $contentType,
280+
'size' => $size,
281+
];
279282
}
280283

281284
private function parsePost(string $name, string $value): void

tests/Fixtures/example.pdf

45.7 KB
Binary file not shown.

tests/Unit/Drivers/Laravel/LaravelHttpServerTest.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
});
7272

7373
it('parse a multipart body with files', function (): void {
74+
Route::get('favicon.ico', static fn (): string => '');
7475
Route::get('/', static fn (): string => "
7576
<html>
7677
<head></head>
@@ -79,8 +80,14 @@
7980
<label for='name'>Your name</label>
8081
<input id='name' type='text' name='name'>
8182
82-
<label for='file'>Your file</label>
83-
<input id='file' type='file' name='file'>
83+
<label for='file1'>Your text file</label>
84+
<input id='file1' type='file' name='file1'>
85+
86+
<label for='file2'>Your binary file</label>
87+
<input id='file2' type='file' name='file2'>
88+
89+
<label for='file3'>Your empty file</label>
90+
<input id='file3' type='file' name='file3'>
8491
8592
<button type='submit'>Send</button>
8693
</form>
@@ -92,7 +99,9 @@
9299
<head></head>
93100
<body>
94101
<h1>Hello {$request->post('name')}</h1>
95-
<p>Uploaded file: {$request->file('file')->getClientOriginalName()}</p>
102+
<p>Text file: {$request->file('file1')?->getClientOriginalName()}</p>
103+
<p>Binary file: {$request->file('file2')?->getClientOriginalName()}</p>
104+
<p>Empty file: {$request->file('file3')?->getClientOriginalName()}</p>
96105
</body>
97106
</html>
98107
");
@@ -101,11 +110,14 @@
101110
$page->assertSee('Your name');
102111

103112
$page->fill('Your name', 'World');
104-
$page->attach('Your file', fixture('lorem-ipsum.txt'));
113+
$page->attach('Your text file', fixture('lorem-ipsum.txt'));
114+
$page->attach('Your binary file', fixture('example.pdf'));
105115
$page->submit();
106116

107117
$page->assertSee('Hello World');
108-
$page->assertSee('Uploaded file: lorem-ipsum.txt');
118+
$page->assertSee('Text file: lorem-ipsum.txt');
119+
$page->assertSee('Binary file: example.pdf');
120+
$page->assertSee('Empty file: ');
109121
});
110122

111123
it('parse a multipart body with nested fields', function (): void {

0 commit comments

Comments
 (0)