8000 feature #30482 [Mime] Fix support for date form parts (fabpot) · symfony/symfony@c01347f · GitHub
[go: up one dir, main page]

Skip to content

Commit c01347f

Browse files
committed
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
2 parents ba727ec + 5c8a4e3 commit c01347f

File tree

5 files changed

+85
-32
lines changed

5 files changed

+85
-32
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mime\Encoder;
13+
14+
/**
15+
* @author Fabien Potencier <fabien@symfony.com>
16+
*
17+
* @experimental in 4.3
18+
*/
19+
final class EightBitContentEncoder implements ContentEncoderInterface
20+
{
21+
public function encodeByteStream($stream, int $maxLineLength = 0): iterable
22+
{
23+
yield from $stream;
24+
}
25+
26+
public function getName(): string
27+
{
28+
return '8bit';
29+
}
30+
31+
public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
32+
{
33+
return $string;
34+
}
35+
}

src/Symfony/Component/Mime/Part/AbstractMultipartPart.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Component\Mime\Part;
1313

14-
use Symfony\Component\Mime\Exception\LogicException;
1514
use Symfony\Component\Mime\Header\Headers;
1615

1716
/**
@@ -57,11 +56,6 @@ public function getPreparedHeaders(): Headers
5756
public function bodyToString(): string
5857
{
5958
$parts = $this->getParts();
60-
61-
if (\count($parts) < 2) {
62-
throw new LogicException(sprintf('A "%s" instance must have at least 2 parts.', __CLASS__));
63-
}
64-
6559
$string = '';
6660
foreach ($parts as $part) {
6761
$string .= '--'.$this->getBoundary()."\r\n".$part->toString()."\r\n";
@@ -74,11 +68,6 @@ public function bodyToString(): string
7468
public function bodyToIterable(): iterable
7569
{
7670
$parts = $this->getParts();
77-
78-
if (\count($parts) < 2) {
79-
throw new LogicException(sprintf('A "%s" instance must have at least 2 parts.', __CLASS__));
80-
}
81-
8271
foreach ($parts as $part) {
8372
yield '--'.$this->getBoundary()."\r\n";
8473
yield from $part->toIterable();

src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function __construct(array $fields = [])
4242
$this->fields[$name] = $value;
4343
}
4444
// HTTP does not support \r\n in header values
45-
$this->getHeaders()->setMaxLineLength(1000);
45+
$this->getHeaders()->setMaxLineLength(-1);
4646
}
4747

4848
public function getMediaSubtype(): string
@@ -58,34 +58,38 @@ public function getParts(): array
5858
private function prepareFields(array $fields): array
5959
{
6060
$values = [];
61-
foreach ($fields as $name => $value) {
62-
if (\is_array($value)) {
63-
foreach ($value as $v) {
64-
$values[] = $this->preparePart($name, $v);
65-
}
66-
} else {
67-
$values[] = $this->preparePart($name, $value);
61+
array_walk_recursive($fields, function ($item, $key) use (&$values) {
62+
if (!\is_array($item)) {
63+
$values[] = $this->preparePart($key, $item);
6864
}
69-
}
65+
});
7066

7167
return $values;
7268
}
7369

7470
private function preparePart($name, $value): TextPart
7571
{
7672
if (\is_string($value)) {
77-
return $this->configurePart($name, new TextPart($value));
73+
return $this->configurePart($name, new TextPart($value, 'utf-8', 'plain', '8bit'));
7874
}
7975

8076
return $this->configurePart($name, $value);
8177
}
8278

8379
private function configurePart(string $name, TextPart $part): TextPart
8480
{
81+
static $r;
82+
83+
if (null === $r) {
84+
$r = new \ReflectionProperty(TextPart::class, 'encoding');
85+
$r->setAccessible(true);
86+
}
87+
8588
10000 $part->setDisposition('form-data');
8689
$part->setName($name);
8790
// HTTP does not support \r\n in header values
88-
$part->getHeaders()->setMaxLineLength(1000);
91+
$part->getHeaders()->setMaxLineLength(-1);
92+
$r->setValue($part, '8bit');
8993

9094
return $part;
9195
}

src/Symfony/Component/Mime/Part/TextPart.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Mime\Encoder\Base64ContentEncoder;
1515
use Symfony\Component\Mime\Encoder\ContentEncoderInterface;
16+
use Symfony\Component\Mime\Encoder\EightBitContentEncoder;
1617
use Symfony\Component\Mime\Encoder\QpContentEncoder;
1718
use Symfony\Component\Mime\Exception\InvalidArgumentException;
1819
use Symfony\Component\Mime\Header\Headers;
@@ -31,8 +32,7 @@ class TextPart extends AbstractPart
3132
private $subtype;
3233
private $disposition;
3334
private $name;
34-
35-
protected $encoding;
35+
private $encoding;
3636

3737
/**
3838
* @param resource|string $body
@@ -49,12 +49,11 @@ public function __construct($body, ?string $charset = 'utf-8', $subtype = 'plain
4949
$this->charset = $charset;
5050
$this->subtype = $subtype;
5151

52-
// FIXME: can also be 7BIT, 8BIT, ...
5352
if (null === $encoding) {
5453
$this->encoding = $this->chooseEncoding();
5554
} else {
56-
if ('quoted-printable' !== $encoding && 'base64' !== $encoding) {
57-
throw new InvalidArgumentException(sprintf('The encoding must be one of "quoted-printable" or "base64" ("%s" given).', $encoding));
55+
if ('quoted-printable' !== $encoding && 'base64' !== $encoding && '8bit' !== $encoding) {
56+
throw new InvalidArgumentException(sprintf('The encoding must be one of "quoted-printable", "base64", or "8bit" ("%s" given).', $encoding));
5857
}
5958
$this->encoding = $encoding;
6059
}
@@ -147,8 +146,12 @@ public function getPreparedHeaders(): Headers
147146
return $headers;
148147
}
149148

150-
protected function getEncoder(): ContentEncoderInterface
149+
private function getEncoder(): ContentEncoderInterface
151150
{
151+
if ('8bit' === $this->encoding) {
152+
return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new EightBitContentEncoder());
153+
}
154+
152155
if ('quoted-printable' === $this->encoding) {
153156
return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new QpContentEncoder());
154157
}

src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class FormDataPartTest extends TestCase
2020
{
2121
public function testConstructor()
2222
{
23+
$r = new \ReflectionProperty(TextPart::class, 'encoding');
24+
$r->setAccessible(true);
25+
2326
$b = new TextPart('content');
2427
$c = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif');
2528
$f = new FormDataPart([
@@ -29,16 +32,35 @@ public function testConstructor()
2932
]);
3033
$this->assertEquals('multipart', $f->getMediaType());
3134
$this->assertEquals('form-data', $f->getMediaSubtype());
32-
$t = new TextPart($content);
35+
$t = new TextPart($content, 'utf-8', 'plain', '8bit');
3336
$t->setDisposition('form-data');
3437
$t->setName('foo');
35-
$t->getHeaders()->setMaxLineLength(1000);
38+
$t->getHeaders()->setMaxLineLength(-1);
3639
$b->setDisposition('form-data');
3740
$b->setName('bar');
38-
$b->getHeaders()->setMaxLineLength(1000);
41+
$b->getHeaders()->setMaxLineLength(-1);
42+
$r->setValue($b, '8bit');
3943
$c->setDisposition('form-data');
4044
$c->setName('baz');
41-
$c->getHeaders()->setMaxLineLength(1000);
45+
$c->getHeaders()->setMaxLineLength(-1);
46+
$r->setValue($c, '8bit');
4247
$this->assertEquals([$t, $b, $c], $f->getParts());
4348
}
49+
50+
public function testToString()
51+
{
52+
$p = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif');
53+
$this->assertEquals(base64_encode(file_get_contents($file)), $p->bodyToString());
54+
}
55+
56+
public function testContentLineLength()
57+
{
58+
$f = new FormDataPart([
59+
'foo' => new DataPart($foo = str_repeat('foo', 1000), 'foo.txt', 'text/plain'),
60+
'bar' => $bar = str_repeat('bar', 1000),
61+
]);
62+
$parts = $f->getParts();
63+
$this->assertEquals($foo, $parts[0]->bodyToString());
64+
$this->assertEquals($bar, $parts[1]->bodyToString());
65+
}
4466
}

0 commit comments

Comments
 (0)
0