Skip to content
Permalink
Browse files

feature #30482 [Mime] Fix support for date form parts (fabpot)

This PR was squashed before being merged into the 4.3-dev branch (closes #30482).

Discussion
----------

[Mime] Fix support for date form parts

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | yes <!-- don't forget to update src/**/CHANGELOG.md files -->
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

<!--
Write a short README entry for your feature/bugfix here (replace this comment block.)
This will help people understand your PR and can be used as a start of the Doc PR.
Additionally:
 - Bug fixes must be submitted against the lowest branch where they apply
   (lowest branches are regularly merged to upper ones so they get the fixes too).
 - Features and deprecations must be submitted against the master branch.
-->

Commits
-------

5c8a4e3 [Mime] removed the 2 parts constraints on Multipart (not true for DataFormPart for instance)
0450c4f [Mime] fixed support for date form parts
  • Loading branch information...
fabpot committed Mar 7, 2019
2 parents ba727ec + 5c8a4e3 commit c01347fd387054493280ed0aaa87b36ca6aa9474
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Encoder;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 4.3
*/
final class EightBitContentEncoder implements ContentEncoderInterface
{
public function encodeByteStream($stream, int $maxLineLength = 0): iterable
{
yield from $stream;
}
public function getName(): string
{
return '8bit';
}
public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
{
return $string;
}
}
@@ -11,7 +11,6 @@
namespace Symfony\Component\Mime\Part;
use Symfony\Component\Mime\Exception\LogicException;
use Symfony\Component\Mime\Header\Headers;
/**
@@ -57,11 +56,6 @@ public function getPreparedHeaders(): Headers
public function bodyToString(): string
{
$parts = $this->getParts();
if (\count($parts) < 2) {
throw new LogicException(sprintf('A "%s" instance must have at least 2 parts.', __CLASS__));
}
$string = '';
foreach ($parts as $part) {
$string .= '--'.$this->getBoundary()."\r\n".$part->toString()."\r\n";
@@ -74,11 +68,6 @@ public function bodyToString(): string
public function bodyToIterable(): iterable
{
$parts = $this->getParts();
if (\count($parts) < 2) {
throw new LogicException(sprintf('A "%s" instance must have at least 2 parts.', __CLASS__));
}
foreach ($parts as $part) {
yield '--'.$this->getBoundary()."\r\n";
yield from $part->toIterable();
@@ -42,7 +42,7 @@ public function __construct(array $fields = [])
$this->fields[$name] = $value;
}
// HTTP does not support \r\n in header values
$this->getHeaders()->setMaxLineLength(1000);
$this->getHeaders()->setMaxLineLength(-1);
}
public function getMediaSubtype(): string
@@ -58,34 +58,38 @@ public function getParts(): array
private function prepareFields(array $fields): array
{
$values = [];
foreach ($fields as $name => $value) {
if (\is_array($value)) {
foreach ($value as $v) {
$values[] = $this->preparePart($name, $v);
}
} else {
$values[] = $this->preparePart($name, $value);
array_walk_recursive($fields, function ($item, $key) use (&$values) {
if (!\is_array($item)) {
$values[] = $this->preparePart($key, $item);
}
}
});
return $values;
}
private function preparePart($name, $value): TextPart
{
if (\is_string($value)) {
return $this->configurePart($name, new TextPart($value));
return $this->configurePart($name, new TextPart($value, 'utf-8', 'plain', '8bit'));
}
return $this->configurePart($name, $value);
}
private function configurePart(string $name, TextPart $part): TextPart
{
static $r;
if (null === $r) {
$r = new \ReflectionProperty(TextPart::class, 'encoding');
$r->setAccessible(true);
}
$part->setDisposition('form-data');
$part->setName($name);
// HTTP does not support \r\n in header values
$part->getHeaders()->setMaxLineLength(1000);
$part->getHeaders()->setMaxLineLength(-1);
$r->setValue($part, '8bit');
return $part;
}
@@ -13,6 +13,7 @@
use Symfony\Component\Mime\Encoder\Base64ContentEncoder;
use Symfony\Component\Mime\Encoder\ContentEncoderInterface;
use Symfony\Component\Mime\Encoder\EightBitContentEncoder;
use Symfony\Component\Mime\Encoder\QpContentEncoder;
use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Header\Headers;
@@ -31,8 +32,7 @@ class TextPart extends AbstractPart
private $subtype;
private $disposition;
private $name;
protected $encoding;
private $encoding;
/**
* @param resource|string $body
@@ -49,12 +49,11 @@ public function __construct($body, ?string $charset = 'utf-8', $subtype = 'plain
$this->charset = $charset;
$this->subtype = $subtype;
// FIXME: can also be 7BIT, 8BIT, ...
if (null === $encoding) {
$this->encoding = $this->chooseEncoding();
} else {
if ('quoted-printable' !== $encoding && 'base64' !== $encoding) {
throw new InvalidArgumentException(sprintf('The encoding must be one of "quoted-printable" or "base64" ("%s" given).', $encoding));
if ('quoted-printable' !== $encoding && 'base64' !== $encoding && '8bit' !== $encoding) {
throw new InvalidArgumentException(sprintf('The encoding must be one of "quoted-printable", "base64", or "8bit" ("%s" given).', $encoding));
}
$this->encoding = $encoding;
}
@@ -147,8 +146,12 @@ public function getPreparedHeaders(): Headers
return $headers;
}
protected function getEncoder(): ContentEncoderInterface
private function getEncoder(): ContentEncoderInterface
{
if ('8bit' === $this->encoding) {
return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new EightBitContentEncoder());
}
if ('quoted-printable' === $this->encoding) {
return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new QpContentEncoder());
}
@@ -20,6 +20,9 @@ class FormDataPartTest extends TestCase
{
public function testConstructor()
{
$r = new \ReflectionProperty(TextPart::class, 'encoding');
$r->setAccessible(true);
$b = new TextPart('content');
$c = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif');
$f = new FormDataPart([
@@ -29,16 +32,35 @@ public function testConstructor()
]);
$this->assertEquals('multipart', $f->getMediaType());
$this->assertEquals('form-data', $f->getMediaSubtype());
$t = new TextPart($content);
$t = new TextPart($content, 'utf-8', 'plain', '8bit');
$t->setDisposition('form-data');
$t->setName('foo');
$t->getHeaders()->setMaxLineLength(1000);
$t->getHeaders()->setMaxLineLength(-1);
$b->setDisposition('form-data');
$b->setName('bar');
$b->getHeaders()->setMaxLineLength(1000);
$b->getHeaders()->setMaxLineLength(-1);
$r->setValue($b, '8bit');
$c->setDisposition('form-data');
$c->setName('baz');
$c->getHeaders()->setMaxLineLength(1000);
$c->getHeaders()->setMaxLineLength(-1);
$r->setValue($c, '8bit');
$this->assertEquals([$t, $b, $c], $f->getParts());
}
public function testToString()
{
$p = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif');
$this->assertEquals(base64_encode(file_get_contents($file)), $p->bodyToString());
}
public function testContentLineLength()
{
$f = new FormDataPart([
'foo' => new DataPart($foo = str_repeat('foo', 1000), 'foo.txt', 'text/plain'),
'bar' => $bar = str_repeat('bar', 1000),
]);
$parts = $f->getParts();
$this->assertEquals($foo, $parts[0]->bodyToString());
$this->assertEquals($bar, $parts[1]->bodyToString());
}
}

0 comments on commit c01347f

Please sign in to comment.
You can’t perform that action at this time.