[BrowserKit] Nested file array prevents uploading file
This commit is contained in:
parent
88b89c9b45
commit
e15f05e03f
@ -73,18 +73,9 @@ class HttpBrowser extends AbstractBrowser
|
|||||||
}
|
}
|
||||||
|
|
||||||
$fields = $request->getParameters();
|
$fields = $request->getParameters();
|
||||||
$hasFile = false;
|
|
||||||
foreach ($request->getFiles() as $name => $file) {
|
|
||||||
if (!isset($file['tmp_name'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$hasFile = true;
|
if ($uploadedFiles = $this->getUploadedFiles($request->getFiles())) {
|
||||||
$fields[$name] = DataPart::fromPath($file['tmp_name'], $file['name']);
|
$part = new FormDataPart($uploadedFiles);
|
||||||
}
|
|
||||||
|
|
||||||
if ($hasFile) {
|
|
||||||
$part = new FormDataPart($fields);
|
|
||||||
|
|
||||||
return [$part->bodyToIterable(), $part->getPreparedHeaders()->toArray()];
|
return [$part->bodyToIterable(), $part->getPreparedHeaders()->toArray()];
|
||||||
}
|
}
|
||||||
@ -119,4 +110,26 @@ class HttpBrowser extends AbstractBrowser
|
|||||||
|
|
||||||
return $headers;
|
return $headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively go through the list. If the file has a tmp_name, convert it to a DataPart.
|
||||||
|
* Keep the original hierarchy.
|
||||||
|
*/
|
||||||
|
private function getUploadedFiles(array $files): array
|
||||||
|
{
|
||||||
|
$uploadedFiles = [];
|
||||||
|
foreach ($files as $name => $file) {
|
||||||
|
if (!\is_array($file)) {
|
||||||
|
return $uploadedFiles;
|
||||||
|
}
|
||||||
|
if (!isset($file['tmp_name'])) {
|
||||||
|
$uploadedFiles[$name] = $this->getUploadedFiles($file);
|
||||||
|
}
|
||||||
|
if (isset($file['tmp_name'])) {
|
||||||
|
$uploadedFiles[$name] = DataPart::fromPath($file['tmp_name'], $file['name']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uploadedFiles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,17 +27,17 @@ class HttpBrowserTest extends AbstractBrowserTest
|
|||||||
/**
|
/**
|
||||||
* @dataProvider validContentTypes
|
* @dataProvider validContentTypes
|
||||||
*/
|
*/
|
||||||
public function testRequestHeaders(array $request, array $exepectedCall)
|
public function testRequestHeaders(array $requestArguments, array $expectedArguments)
|
||||||
{
|
{
|
||||||
$client = $this->createMock(HttpClientInterface::class);
|
$client = $this->createMock(HttpClientInterface::class);
|
||||||
$client
|
$client
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('request')
|
->method('request')
|
||||||
->with(...$exepectedCall)
|
->with(...$expectedArguments)
|
||||||
->willReturn($this->createMock(ResponseInterface::class));
|
->willReturn($this->createMock(ResponseInterface::class));
|
||||||
|
|
||||||
$browser = new HttpBrowser($client);
|
$browser = new HttpBrowser($client);
|
||||||
$browser->request(...$request);
|
$browser->request(...$requestArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validContentTypes()
|
public function validContentTypes()
|
||||||
@ -61,7 +61,7 @@ class HttpBrowserTest extends AbstractBrowserTest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMultiPartRequest()
|
public function testMultiPartRequestWithSingleFile()
|
||||||
{
|
{
|
||||||
$client = $this->createMock(HttpClientInterface::class);
|
$client = $this->createMock(HttpClientInterface::class);
|
||||||
$client
|
$client
|
||||||
@ -81,4 +81,90 @@ class HttpBrowserTest extends AbstractBrowserTest
|
|||||||
file_put_contents($path, 'my_file');
|
file_put_contents($path, 'my_file');
|
||||||
$browser->request('POST', 'http://example.com/', [], ['file' => ['tmp_name' => $path, 'name' => 'foo']]);
|
$browser->request('POST', 'http://example.com/', [], ['file' => ['tmp_name' => $path, 'name' => 'foo']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMultiPartRequestWithNormalFlatArray()
|
||||||
|
{
|
||||||
|
$client = $this->createMock(HttpClientInterface::class);
|
||||||
|
$this->expectClientToSendRequestWithFiles($client, ['file1_content', 'file2_content']);
|
||||||
|
|
||||||
|
$browser = new HttpBrowser($client);
|
||||||
|
$browser->request('POST', 'http://example.com/', [], [
|
||||||
|
'file1' => $this->getUploadedFile('file1'),
|
||||||
|
'file2' => $this->getUploadedFile('file2'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiPartRequestWithNormalNestedArray()
|
||||||
|
{
|
||||||
|
$client = $this->createMock(HttpClientInterface::class);
|
||||||
|
$this->expectClientToSendRequestWithFiles($client, ['file1_content', 'file2_content']);
|
||||||
|
|
||||||
|
$browser = new HttpBrowser($client);
|
||||||
|
$browser->request('POST', 'http://example.com/', [], [
|
||||||
|
'level1' => [
|
||||||
|
'level2' => [
|
||||||
|
'file1' => $this->getUploadedFile('file1'),
|
||||||
|
'file2' => $this->getUploadedFile('file2'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiPartRequestWithBracketedArray()
|
||||||
|
{
|
||||||
|
$client = $this->createMock(HttpClientInterface::class);
|
||||||
|
$this->expectClientToSendRequestWithFiles($client, ['file1_content', 'file2_content']);
|
||||||
|
|
||||||
|
$browser = new HttpBrowser($client);
|
||||||
|
$browser->request('POST', 'http://example.com/', [], [
|
||||||
|
'form[file1]' => $this->getUploadedFile('file1'),
|
||||||
|
'form[file2]' => $this->getUploadedFile('file2'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiPartRequestWithInvalidItem()
|
||||||
|
{
|
||||||
|
$client = $this->createMock(HttpClientInterface::class);
|
||||||
|
$this->expectClientToSendRequestWithFiles($client, ['file1_content']);
|
||||||
|
|
||||||
|
$browser = new HttpBrowser($client);
|
||||||
|
$browser->request('POST', 'http://example.com/', [], [
|
||||||
|
'file1' => $this->getUploadedFile('file1'),
|
||||||
|
'file2' => 'INVALID',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function uploadFile(string $data): string
|
||||||
|
{
|
||||||
|
$path = tempnam(sys_get_temp_dir(), 'http');
|
||||||
|
file_put_contents($path, $data);
|
||||||
|
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getUploadedFile(string $name): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'tmp_name' => $this->uploadFile($name.'_content'),
|
||||||
|
'name' => $name.'_name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function expectClientToSendRequestWithFiles(HttpClientInterface $client, $fileContents)
|
||||||
|
{
|
||||||
|
$client
|
||||||
|
->expects($this->once())
|
||||||
|
->method('request')
|
||||||
|
->with('POST', 'http://example.com/', $this->callback(function ($options) use ($fileContents) {
|
||||||
|
$this->assertStringContainsString('Content-Type: multipart/form-data', implode('', $options['headers']));
|
||||||
|
$this->assertInstanceOf('\Generator', $options['body']);
|
||||||
|
$body = implode('', iterator_to_array($options['body'], false));
|
||||||
|
foreach ($fileContents as $content) {
|
||||||
|
$this->assertStringContainsString($content, $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}))
|
||||||
|
->willReturn($this->createMock(ResponseInterface::class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user