diff --git a/src/Symfony/Component/Mime/Part/DataPart.php b/src/Symfony/Component/Mime/Part/DataPart.php index b42ecb4da102e..c5ca9410db224 100644 --- a/src/Symfony/Component/Mime/Part/DataPart.php +++ b/src/Symfony/Component/Mime/Part/DataPart.php @@ -26,10 +26,12 @@ class DataPart extends TextPart private $mediaType; private $cid; + private $formEncoding; + /** * @param resource|string|File $body Use a File instance to defer loading the file until rendering */ - public function __construct($body, string $filename = null, string $contentType = null, string $encoding = null) + public function __construct($body, string $filename = null, string $contentType = null, string $encoding = null, string $formEncoding = null) { unset($this->_parent); @@ -42,6 +44,8 @@ public function __construct($body, string $filename = null, string $contentType parent::__construct($body, null, $subtype, $encoding); + $this->formEncoding = $formEncoding ?? '8bit'; + if (null !== $filename) { $this->filename = $filename; $this->setName($filename); @@ -49,9 +53,9 @@ public function __construct($body, string $filename = null, string $contentType $this->setDisposition('attachment'); } - public static function fromPath(string $path, string $name = null, string $contentType = null): self + public static function fromPath(string $path, string $name = null, string $contentType = null, string $encoding = null): self { - return new self(new File($path), $name, $contentType); + return new self(new File($path), $name, $contentType, $encoding, $encoding); } /** diff --git a/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php b/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php index 54a7c76a353a0..a0484b17cfc9a 100644 --- a/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php +++ b/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php @@ -94,16 +94,35 @@ private function preparePart(string $name, string|TextPart $value): TextPart private function configurePart(string $name, TextPart $part): TextPart { - static $r; - - $r ??= new \ReflectionProperty(TextPart::class, 'encoding'); - $part->setDisposition('form-data'); $part->setName($name); // HTTP does not support \r\n in header values $part->getHeaders()->setMaxLineLength(\PHP_INT_MAX); - $r->setValue($part, '8bit'); + if ($part instanceof DataPart) { + // data part may specify base64 encoding + $this->configureDataPartEncoding($part); + } else { + $this->configureTextPartEncoding($part); + } return $part; } + + private function configureDataPartEncoding(DataPart $part) + { + static $r; + + $r ??= new \ReflectionProperty(DataPart::class, 'formEncoding'); + $formEncoding = $r->getValue($part); + + $this->configureTextPartEncoding($part, $formEncoding); + } + + private function configureTextPartEncoding(TextPart $part, $encoding = '8bit') + { + static $r; + + $r ??= new \ReflectionProperty(TextPart::class, 'encoding'); + $r->setValue($part, $encoding); + } } diff --git a/src/Symfony/Component/Mime/Tests/EmailTest.php b/src/Symfony/Component/Mime/Tests/EmailTest.php index c4f829d7d12de..c47fe23ec5606 100644 --- a/src/Symfony/Component/Mime/Tests/EmailTest.php +++ b/src/Symfony/Component/Mime/Tests/EmailTest.php @@ -530,6 +530,7 @@ public function testSymfonySerialize() { "filename": "test.txt", "mediaType": "application", + "formEncoding": "8bit", "body": "Some Text file", "charset": null, "subtype": "octet-stream", diff --git a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php index 7ac3336f90666..e78b1d6a6597b 100644 --- a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php +++ b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php @@ -47,6 +47,27 @@ public function testConstructor() $this->assertEquals([$t, $b, $c], $f->getParts()); } + public function testConstructorWithBase64Encoding() + { + $r = new \ReflectionProperty(TextPart::class, 'encoding'); + + $d = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif', null, null, '8bit'); + $e = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif', null, null, 'base64'); + $f = new FormDataPart([ + 'foo' => clone $d, + 'bar' => clone $e + ]); + $d->setDisposition('form-data'); + $d->setName('foo'); + $d->getHeaders()->setMaxLineLength(\PHP_INT_MAX); + $r->setValue($d, '8bit'); + $e->setDisposition('form-data'); + $e->setName('bar'); + $e->getHeaders()->setMaxLineLength(\PHP_INT_MAX); + $r->setValue($e, 'base64'); + $this->assertEquals([$d, $e], $f->getParts()); + } + public function testNestedArrayParts() { $p1 = new TextPart('content', 'utf-8', 'plain', '8bit');