8000 Merge branch '5.4' into 6.3 · symfony/symfony@0e8d59f · GitHub
[go: up one dir, main page]

Skip to content

Commit 0e8d59f

Browse files
Merge branch '5.4' into 6.3
* 5.4: [Form] Fix merging params & files when "multiple" is enabled [HttpFoundation] Do not swallow trailing `=` in cookie value Handle Sendinblue error responses without a message key [Serializer] Fix collecting only first missing constructor argument [Messenger] Fix DoctrineOpenTransactionLoggerMiddleware [Translation] Add missing return type [Validator] add missing catalan translations
2 parents d69ab75 + 3d4a4e1 commit 0e8d59f

File tree

17 files changed

+281
-42
lines changed

17 files changed

+281
-42
lines changed

src/Symfony/Bridge/Doctrine/Messenger/DoctrineOpenTransactionLoggerMiddleware.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
class DoctrineOpenTransactionLoggerMiddleware extends AbstractDoctrineMiddleware
2727
{
2828
private $logger;
29+
/** @var bool */
30+
private $isHandling = false;
2931

3032
public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null, LoggerInterface $logger = null)
3133
{
@@ -36,6 +38,12 @@ public function __construct(ManagerRegistry $managerRegistry, string $entityMana
3638

3739
protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope
3840
{
41+
if ($this->isHandling) {
42+
return $stack->next()->handle($envelope, $stack);
43+
}
44+
45+
$this->isHandling = true;
46+
3947
try {
4048
return $stack->next()->handle($envelope, $stack);
4149
} finally {
@@ -44,6 +52,7 @@ protected function handleForManager(EntityManagerInterface $entityManager, Envel
4452
'message' => $envelope->getMessage(),
4553
]);
4654
}
55+
$this->isHandling = false;
4756
}
4857
}
4958
}

src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Form\FormError;
1616
use Symfony\Component\Form\FormInterface;
1717
use Symfony\Component\Form\RequestHandlerInterface;
18+
use Symfony\Component\Form\Util\FormUtil;
1819
use Symfony\Component\Form\Util\ServerParams;
1920
use Symfony\Component\HttpFoundation\File\File;
2021
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -95,7 +96,7 @@ public function handleRequest(FormInterface $form, mixed $request = null)
9596
}
9697

9798
if (\is_array($params) && \is_array($files)) {
98-
$data = array_replace_recursive($params, $files);
99+
$data = FormUtil::mergeParamsAndFiles($params, $files);
99100
} else {
100101
$data = $params ?: $files;
101102
}

src/Symfony/Component/Form/NativeRequestHandler.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form;
1313

1414
use Symfony\Component\Form\Exception\UnexpectedTypeException;
15+
use Symfony\Component\Form\Util\FormUtil;
1516
use Symfony\Component\Form\Util\ServerParams;
1617

1718
/**
@@ -106,7 +107,7 @@ public function handleRequest(FormInterface $form, mixed $request = null)
106107
}
107108

108109
if (\is_array($params) && \is_array($files)) {
109-
$data = array_replace_recursive($params, $files);
110+
$data = FormUtil::mergeParamsAndFiles($params, $files);
110111
} else {
111112
$data = $params ?: $files;
112113
}

src/Symfony/Component/Form/Tests/AbstractRequestHandlerTestCase.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,42 @@ public function testMergeParamsAndFiles($method)
236236
$this->assertSame($file, $form->get('field2')->getData());
237237
}
238238

239+
/**
240+
* @dataProvider methodExceptGetProvider
241+
*/
242+
public function testMergeParamsAndFilesMultiple($method)
243+
{
244+
$form = $this->createForm('param1', $method, true);
245+
$form->add($this->createBuilder('field1', false, ['allow_file_upload' => true, 'multiple' => true])->getForm());
246+
$file1 = $this->getUploadedFile();
247+
$file2 = $this->getUploadedFile();
248+
249+
$this->setRequestData($method, [
250+
'param1' => [
251+
'field1' => [
252+
'foo',
253+
'bar',
254+
'baz',
255+
],
256+
],
257+
], [
258+
'param1' => [
259+
'field1' => [
260+
$file1,
261+
$file2,
262+
],
263+
],
264+
]);
265+
266+
$this->requestHandler->handleRequest($form, $this->request);
267+
$data = $form->get('field1')->getData();
268+
269+
$this->assertTrue($form->isSubmitted());
270+
$this->assertIsArray($data);
271+
$this->assertCount(5, $data);
272+
$this->assertSame(['foo', 'bar', 'baz', $file1, $file2], $data);
273+
}
274+
239275
/**
240276
* @dataProvider methodExceptGetProvider
241277
*/

src/Symfony/Component/Form/Util/FormUtil.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,29 @@ public static function isEmpty(mixed $data): bool
3737
// not considered to be empty, ever.
3838
return null === $data || '' === $data;
3939
}
40+
41+
/**
42+
* Recursively replaces or appends elements of the first array with elements
43+
* of second array. If the key is an integer, the values will be appended to
44+
* the new array; otherwise, the value from the second array will replace
45+
* the one from the first array.
46+
*/
47+
public static function mergeParamsAndFiles(array $params, array $files): array
48+
{
49+
$result = [];
50+
51+
foreach ($params as $key => $value) {
52+
if (\is_array($value) && \is_array($files[$key] ?? null)) {
53+
$value = self::mergeParamsAndFiles($value, $files[$key]);
54+
unset($files[$key]);
55+
}
56+
if (\is_int($key)) {
57+
$result[] = $value;
58+
} else {
59+
$result[$key] = $value;
60+
}
61+
}
62+
63+
return array_merge($result, $files);
64+
}
4065
}

src/Symfony/Component/HttpFoundation/HeaderUtils.php

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,21 @@ private function __construct()
3333
*
3434
* Example:
3535
*
36-
* HeaderUtils::split("da, en-gb;q=0.8", ",;")
36+
* HeaderUtils::split('da, en-gb;q=0.8', ',;')
3737
* // => ['da'], ['en-gb', 'q=0.8']]
3838
*
3939
* @param string $separators List of characters to split on, ordered by
40-
* precedence, e.g. ",", ";=", or ",;="
40+
* precedence, e.g. ',', ';=', or ',;='
4141
*
4242
* @return array Nested array with as many levels as there are characters in
4343
* $separators
4444
*/
4545
public static function split(string $header, string $separators): array
4646
{
47+
if ('' === $separators) {
48+
throw new \InvalidArgumentException('At least one separator must be specified.');
49+
}
50+
4751
$quotedSeparators = preg_quote($separators, '/');
4852

4953
preg_match_all('
@@ -77,8 +81,8 @@ public static function split(string $header, string $separators): array
7781
*
7882
* Example:
7983
*
80-
* HeaderUtils::combine([["foo", "abc"], ["bar"]])
81-
* // => ["foo" => "abc", "bar" => true]
84+
* HeaderUtils::combine([['foo', 'abc'], ['bar']])
85+
* // => ['foo' => 'abc', 'bar' => true]
8286
*/
8387
public static function combine(array $parts): array
8488
{
@@ -95,13 +99,13 @@ public static function combine(array $parts): array
9599
/**
96100
* Joins an associative array into a string for use in an HTTP header.
97101
*
98-
* The key and value of each entry are joined with "=", and all entries
102+
* The key and value of each entry are joined with '=', and all entries
99103
* are joined with the specified separator and an additional space (for
100104
* readability). Values are quoted if necessary.
101105
*
102106
* Example:
103107
*
104-
* HeaderUtils::toString(["foo" => "abc", "bar" => true, "baz" => "a b c"], ",")
108+
* HeaderUtils::toString(['foo' => 'abc', 'bar' => true, 'baz' => 'a b c'], ',')
105109
* // => 'foo=abc, bar, baz="a b c"'
106110
*/
107111
public static function toString(array $assoc, string $separator): string
@@ -252,40 +256,37 @@ public static function parseQuery(string $query, bool $ignoreBrackets = false, s
252256
private static function groupParts(array $matches, string $separators, bool $first = true): array
253257
{
254258
$separator = $separators[0];
255-
$partSeparators = substr($separators, 1);
256-
259+
$separators = substr($separators, 1);
257260
$i = 0;
261+
262+
if ('' === $separators && !$first) {
263+
$parts = [''];
264+
265+
foreach ($matches as $match) {
266+
if (!$i && isset($match['separator'])) {
267+
$i = 1;
268+
$parts[1] = '';
269+
} else {
270+
$parts[$i] .= self::unquote($match[0]);
271+
}
272+
}
273+
274+
return $parts;
275+
}
276+
277+
$parts = [];
258278
$partMatches = [];
259-
$previousMatchWasSeparator = false;
279+
260280
foreach ($matches as $match) {
261-
if (!$first && $previousMatchWasSeparator && isset($match['separator']) && $match['separator'] === $separator) {
262-
$previousMatchWasSeparator = true;
263-
$partMatches[$i][] = $match;
264-
} elseif (isset($match['separator']) && $match['separator'] === $separator) {
265-
$previousMatchWasSeparator = true;
281+
if (($match['separator'] ?? null) === $separator) {
266282
++$i;
267283
} else {
268-
$previousMatchWasSeparator = false;
269284
$partMatches[$i][] = $match;
270285
}
271286
}
272287

273-
$parts = [];
274-
if ($partSeparators) {
275-
foreach ($partMatches as $matches) {
276-
$parts[] = self::groupParts($matches, $partSeparators, false);
277-
}
278-
} else {
279-
foreach ($partMatches as $matches) {
280-
$parts[] = self::unquote($matches[0][0]);
281-
}
282-
283-
if (!$first && 2 < \count($parts)) {
284-
$parts = [
285-
$parts[0],
286-
implode($separator, \array_slice($parts, 1)),
287-
];
288-
}
288+
foreach ($partMatches as $matches) {
289+
$parts[] = '' === $separators ? self::unquote($matches[0][0]) : self::groupParts($matches, $separators, false);
289290
}
290291

291292
return $parts;

src/Symfony/Component/HttpFoundation/Tests/CookieTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ public function testFromString()
313313
$cookie = Cookie::fromString('foo=bar', true);
314314
$this->assertEquals(Cookie::create('foo', 'bar', 0, '/', null, false, false, false, null), $cookie);
315315

316+
$cookie = Cookie::fromString('foo=bar=', true);
317+
$this->assertEquals(Cookie::create('foo', 'bar=', 0, '/', null, false, false, false, null), $cookie);
318+
316319
$cookie = Cookie::fromString('foo', true);
317320
$this->assertEquals(Cookie::create('foo', null, 0, '/', null, false, false, false, null), $cookie);
318321

src/Symfony/Component/HttpFoundation/Tests/HeaderUtilsTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static function provideHeaderToSplit(): array
3434
[['foo', '123, bar'], 'foo=123, bar', '='],
3535
[['foo', '123, bar'], ' foo = 123, bar ', '='],
3636
[[['foo', '123'], ['bar']], 'foo=123, bar', ',='],
37-
[[[['foo', '123']], [['bar'], ['foo', '456']]], 'foo=123, bar; foo=456', ',;='],
37+
[[[['foo', '123']], [['bar'], ['foo', '456']]], 'foo=123, bar;; foo=456', ',;='],
3838
[[[['foo', 'a,b;c=d']]], 'foo="a,b;c=d"', ',;='],
3939

4040
[['foo', 'bar'], 'foo,,,, bar', ','],
@@ -46,13 +46,15 @@ public static function provideHeaderToSplit(): array
4646

4747
[[['foo_cookie', 'foo=1&bar=2&baz=3'], ['expires', 'Tue, 22-Sep-2020 06:27:09 GMT'], ['path', '/']], 'foo_cookie=foo=1&bar=2&baz=3; expires=Tue, 22-Sep-2020 06:27:09 GMT; path=/', ';='],
4848
[[['foo_cookie', 'foo=='], ['expires', 'Tue, 22-Sep-2020 06:27:09 GMT'], ['path', '/']], 'foo_cookie=foo==; expires=Tue, 22-Sep-2020 06:27:09 GMT; path=/', ';='],
49+
[[['foo_cookie', 'foo='], ['expires', 'Tue, 22-Sep-2020 06:27:09 GMT'], ['path', '/']], 'foo_cookie=foo=; expires=Tue, 22-Sep-2020 06:27:09 GMT; path=/', ';='],
4950
[[['foo_cookie', 'foo=a=b'], ['expires', 'Tue, 22-Sep-2020 06:27:09 GMT'], ['path', '/']], 'foo_cookie=foo="a=b"; expires=Tue, 22-Sep-2020 06:27:09 GMT; path=/', ';='],
5051

5152
// These are not a valid header values. We test that they parse anyway,
5253
// and that both the valid and invalid parts are returned.
5354
[[], '', ','],
5455
[[], ',,,', ','],
55-
[['foo', 'bar', 'baz'], 'foo, "bar", "baz', ','],
56+
[[['', 'foo'], ['bar', '']], '=foo,bar=', ',='],
57+
[['foo', 'foobar', 'baz'], 'foo, foo"bar", "baz', ','],
5658
[['foo', 'bar, baz'], 'foo, "bar, baz', ','],
5759
[['foo', 'bar, baz\\'], 'foo, "bar, baz\\', ','],
5860
[['foo', 'bar, baz\\'], 'foo, "bar, baz\\\\', ','],

src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueApiTransport.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
6565
}
6666

6767
if (201 !== $statusCode) {
68-
throw new HttpTransportException('Unable to send an email: '.$result['message'].sprintf(' (code %d).', $statusCode), $response);
68+
throw new HttpTransportException('Unable to send an email: '.($result['message'] ?? $response->getContent(false)).sprintf(' (code %d).', $statusCode), $response);
6969
}
7070

7171
$sentMessage->setMessageId($result['messageId']);

src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransport.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ protected function doSend(MessageInterface $message): SentMessage
7575
if (201 !== $statusCode) {
7676
$error = $response->toArray(false);
7777

78-
throw new TransportException('Unable to send the SMS: '.$error['message'], $response);
78+
throw new TransportException('Unable to send the SMS: '.($error['message'] ?? $response->getContent(false)), $response);
7979
}
8080

8181
$success = $response->toArray(false);

src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,6 @@ protected function instantiateObject(array &$data, string $class, array &$contex
405405
true
406406
);
407407
$context['not_normalizable_value_exceptions'][] = $exception;
408-
409-
return $reflectionClass->newInstanceWithoutConstructor();
410408
}
411409
}
412410

src/Symfony/Component/Serializer/Tests/Fixtures/Php80WithPromotedTypedConstructor.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313

1414
final class Php80WithPromotedTypedConstructor
1515
{
16-
public function __construct(public bool $bool)
17-
{
16+
public function __construct(
17+
public bool $bool,
18+
public string $string,
19+
public int $int,
20+
) {
1821
}
1922
}
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\Serializer\Tests\Fixtures;
13+
14+
final class WithTypedConstructor
15+
{
16+
/**
17+
* @var string
18+
*/
19+
public $string;
20+
/**
21+
* @var bool
22+
*/
23+
public $bool;
24+
/**
25+
* @var int
26+
*/
27+
public $int;
28+
29+
public function __construct(string $string, bool $bool, int $int)
30+
{
31+
$this->string = $string;
32+
$this->bool = $bool;
33+
$this->int = $int;
34+
}
35+
}

src/Symfony/Component/Serializer/Tests/Normalizer/Features/ConstructorArgumentsTestTrait.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,22 @@ public function testConstructorWithMissingData()
7171
self::assertSame(['bar', 'baz'], $e->getMissingConstructorArguments());
7272
}
7373
}
74+
75+
public function testExceptionsAreCollectedForConstructorWithMissingData()
76+
{
77+
$data = [
78+
'foo' => 10,
79+
];
80+
81+
$exceptions = [];
82+
83+
$normalizer = $this->getDenormalizerForConstructArguments();
84+
$normalizer->denormalize($data, ConstructorArgumentsObject::class, null, [
85+
'not_normalizable_value_exceptions' => &$exceptions,
86+
]);
87+
88+
self::assertCount(2, $exceptions);
89+
self::assertSame('Failed to create object because the class misses the "bar" property.', $exceptions[0]->getMessage());
90+
self::assertSame('Failed to create object because the class misses the "baz" property.', $exceptions[1]->getMessage());
91+
}
7492
}

0 commit comments

Comments
 (0)
0