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

Skip to content

Commit 9e92ab3

Browse files
Merge branch '5.4' into 6.3
* 5.4: [HttpClient] Fix pausing responses before they start when using curl separate child and parent context in NotificationEmail on writes do not overwrite the cache key when it is false [Mailer] Throw TransportException when unable to read from socket [Serializer] Rewrite `AbstractObjectNormalizer::createChildContext()` to use the provided `cache_key` from original context when creating child contexts [HttpClient] Fix error chunk creation in passthru Adjusting and removing the 'review' attribute from the pt_br translation XML. [Serializer] Take unnamed variadic parameters into account when denormalizing
2 parents 893d30e + 2e025f9 commit 9e92ab3

File tree

12 files changed

+291
-13
lines changed

12 files changed

+291
-13
lines changed

src/Symfony/Bridge/Twig/Mime/NotificationEmail.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,23 @@ public function getHtmlTemplate(): ?string
174174
return '@email/'.$this->theme.'/notification/body.html.twig';
175175
}
176176

177+
public function context(array $context)
178+
{
179+
$parentContext = [];
180+
181+
foreach ($context as $key => $value) {
182+
if (\array_key_exists($key, $this->context)) {
183+
$this->context[$key] = $value;
184+
} else {
185+
$parentContext[$key] = $value;
186+
}
187+
}
188+
189+
parent::context($parentContext);
190+
191+
return $this;
192+
}
193+
177194
public function getContext(): array
178195
{
179196
return array_merge($this->context, parent::getContext());

src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,54 @@ public function testPublicMailSubject()
128128
$headers = $email->getPreparedHeaders();
129129
$this->assertSame('Foo', $headers->get('Subject')->getValue());
130130
}
131+
132+
public function testContext()
133+
{
134+
$email = new NotificationEmail();
135+
$email->context(['some' => 'context']);
136+
137+
$this->assertSame([
138+
'importance' => NotificationEmail::IMPORTANCE_LOW,
139+
'content' => '',
140+
'exception' => false,
141+
'action_text' => null,
142+
'action_url' => null,
143+
'markdown' => false,
144+
'raw' => false,
145+
'footer_text' => 'Notification e-mail sent by Symfony',
146+
'some' => 'context',
147+
], $email->getContext());
148+
149+
$context = $email->getContext();
150+
$context['foo'] = 'bar';
151+
$email->context($context);
152+
153+
$this->assertSame([
154+
'importance' => NotificationEmail::IMPORTANCE_LOW,
155+
'content' => '',
156+
'exception' => false,
157+
'action_text' => null,
158+
'action_url' => null,
159+
'markdown' => false,
160+
'raw' => false,
161+
'footer_text' => 'Notification e-mail sent by Symfony',
162+
'some' => 'context',
163+
'foo' => 'bar',
164+
], $email->getContext());
165+
166+
$email->action('Action Text', 'Action URL');
167+
168+
$this->assertSame([
169+
'importance' => NotificationEmail::IMPORTANCE_LOW,
170+
'content' => '',
171+
'exception' => false,
172+
'action_text' => 'Action Text',
173+
'action_url' => 'Action URL',
174+
'markdown' => false,
175+
'raw' => false,
176+
'footer_text' => 'Notification e-mail sent by Symfony',
177+
'some' => 'context',
178+
'foo' => 'bar',
179+
], $email->getContext());
180+
}
131181
}

src/Symfony/Component/HttpClient/Response/AsyncResponse.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public function __construct(HttpClientInterface $client, string $method, string
6565
while (true) {
6666
foreach (self::stream([$response], $timeout) as $chunk) {
6767
if ($chunk->isTimeout() && $response->passthru) {
68-
foreach (self::passthru($response->client, $response, new ErrorChunk($response->offset, new TransportException($chunk->getError()))) as $chunk) {
68+
foreach (self::passthru($response->client, $response, new ErrorChunk($response->offset, $chunk->getError())) as $chunk) {
6969
if ($chunk->isFirst()) {
7070
return false;
7171
}

src/Symfony/Component/HttpClient/Response/CurlResponse.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ public function __construct(CurlClientState $multi, \CurlHandle|string $ch, ?arr
9898
$this->info['pause_handler'] = static function (float $duration) use ($ch, $multi, $execCounter) {
9999
if (0 < $duration) {
100100
if ($execCounter === $multi->execCounter) {
101-
$multi->execCounter = !\is_float($execCounter) ? 1 + $execCounter : \PHP_INT_MIN;
102101
curl_multi_remove_handle($multi->handle, $ch);
103102
}
104103

src/Symfony/Component/HttpClient/Tests/RetryableHttpClientTest.php

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

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpClient\Exception\ServerException;
16+
use Symfony\Component\HttpClient\Exception\TimeoutException;
1617
use Symfony\Component\HttpClient\HttpClient;
1718
use Symfony\Component\HttpClient\MockHttpClient;
1819
use Symfony\Component\HttpClient\NativeHttpClient;
@@ -21,6 +22,7 @@
2122
use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
2223
use Symfony\Component\HttpClient\RetryableHttpClient;
2324
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
25+
use Symfony\Contracts\HttpClient\Test\TestHttpServer;
2426

2527
class RetryableHttpClientTest extends TestCase
2628
{
@@ -245,6 +247,35 @@ public function testRetryOnErrorAssertContent()
245247
self::assertSame('Test out content', $response->getContent(), 'Content should be buffered');
246248
}
247249

250+
/**
251+
* @testWith ["GET"]
252+
* ["POST"]
253+
* ["PUT"]
254+
* ["PATCH"]
255+
* ["DELETE"]
256+
*/
257+
public function testRetryOnHeaderTimeout(string $method)
258+
{
259+
$client = HttpClient::create();
260+
261+
if ($client instanceof NativeHttpClient) {
262+
$this->markTestSkipped('NativeHttpClient cannot timeout before receiving headers');
263+
}
264+
265+
TestHttpServer::start();
266+
267+
$client = new RetryableHttpClient($client);
268+
$response = $client->request($method, 'http://localhost:8057/timeout-header', ['timeout' => 0.1]);
269+
270+
try {
271+
$response->getStatusCode();
272+
$this->fail(TimeoutException::class.' expected');
273+
} catch (TimeoutException $e) {
274+
}
275+
276+
$this->assertSame('Idle timeout reached for "http://localhost:8057/timeout-header".', $response->getInfo('error'));
277+
}
278+
248279
public function testRetryWithMultipleBaseUris()
249280
{
250281
$client = new RetryableHttpClient(

src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public function readLine(): string
7474
return '';
7575
}
7676

77-
$line = fgets($this->out);
77+
$line = @fgets($this->out);
7878
if ('' === $line || false === $line) {
7979
$metas = stream_get_meta_data($this->out);
8080
if ($metas['timed_out']) {
@@ -83,6 +83,9 @@ public function readLine(): string
8383
if ($metas['eof']) {
8484
throw new TransportException(sprintf('Connection to "%s" has been closed unexpectedly.', $this->getReadConnectionDescription()));
8585
}
86+
if (false === $line) {
87+
throw new TransportException(sprintf('Unable to read from connection to "%s": ', $this->getReadConnectionDescription()).error_get_last()['message']);
88+
}
8689
}
8790

8891
$this->debug .= sprintf('< %s', $line);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
361361
$variadicParameters[$parameterKey] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $attributeContext, $format);
362362
}
363363

364-
$params = array_merge($params, $variadicParameters);
364+
$params = array_merge(array_values($params), $variadicParameters);
365365
$unsetKeys[] = $key;
366366
}
367367
} elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,11 @@ private function isMaxDepthReached(array $attributesMetadata, string $class, str
734734
protected function createChildContext(array $parentContext, string $attribute, ?string $format): array
735735
{
736736
$context = parent::createChildContext($parentContext, $attribute, $format);
737-
$context['cache_key'] = $this->getCacheKey($format, $context);
737+
if ($context['cache_key'] ?? false) {
738+
$context['cache_key'] .= '-'.$attribute;
739+
} elseif (false !== ($context['cache_key'] ?? null)) {
740+
$context['cache_key'] = $this->getCacheKey($format, $context);
741+
}
738742

739743
return $context;
740744
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
class DummyWithWithVariadicParameterConstructor
15+
{
16+
private $foo;
17+
18+
private $bar;
19+
20+
private $baz;
21+
22+
public function __construct(string $foo, int $bar = 1, Dummy ...$baz)
23+
{
24+
$this->foo = $foo;
25+
$this->bar = $bar;
26+
$this->baz = $baz;
27+
}
F438
28+
29+
public function getFoo(): string
30+
{
31+
return $this->foo;
32+
}
33+
34+
public function getBar(): int
35+
{
36+
return $this->bar;
37+
}
38+
39+
/** @return Dummy[] */
40+
public function getBaz(): array
41+
{
42+
return $this->baz;
43+
}
44+
}

src/Symfony/Component/Serializer/Tests/Normalizer/AbstractNormalizerTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use Symfony\Component\Serializer\Tests\Fixtures\AbstractNormalizerDummy;
3131
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\IgnoreDummy;
3232
use Symfony\Component\Serializer\Tests\Fixtures\Dummy;
33+
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithWithVariadicParameterConstructor;
3334
use Symfony\Component\Serializer\Tests\Fixtures\NullableConstructorArgumentDummy;
3435
use Symfony\Component\Serializer\Tests\Fixtures\NullableOptionalConstructorArgumentDummy;
3536
use Symfony\Component\Serializer\Tests\Fixtures\StaticConstructorDummy;
@@ -255,6 +256,25 @@ public static function getNormalizer()
255256
yield [new ObjectNormalizer(null, null, null, $extractor)];
256257
}
257258

259+
public function testVariadicConstructorDenormalization()
260+
{
261+
$data = [
262+
'foo' => 'woo',
263+
'baz' => [
264+
['foo' => null, 'bar' => null, 'baz' => null, 'qux' => null],
265+
['foo' => null, 'bar' => null, 'baz' => null, 'qux' => null],
266+
],
267+
];
268+
269+
$normalizer = new ObjectNormalizer();
270+
$normalizer->setSerializer(new Serializer([$normalizer]));
271+
272+
$expected = new DummyWithWithVariadicParameterConstructor('woo', 1, new Dummy(), new Dummy());
273+
$actual = $normalizer->denormalize($data, DummyWithWithVariadicParameterConstructor::class);
274+
275+
$this->assertEquals($expected, $actual);
276+
}
277+
258278
public static function getNormalizerWithCustomNameConverter()
259279
{
260280
$extractor = new PhpDocExtractor();

src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,116 @@ public function testDenormalizeUntypedStringObject()
922922
$this->assertEquals(new DummyWithStringObject(new DummyString()), $actual);
923923
$this->assertEquals('', $actual->value->value);
924924
}
925+
926+
public function testProvidingContextCacheKeyGeneratesSameChildContextCacheKey()
927+
{
928+
$foobar = new Dummy();
929+
$foobar->foo = new EmptyDummy();
930+
$foobar->bar = 'bar';
931+
$foobar->baz = 'baz';
932+
933+
$normalizer = new class() extends AbstractObjectNormalizerDummy {
934+
public $childContextCacheKey;
935+
936+
protected function extractAttributes(object $object, string $format = null, array $context = []): array
937+
{
938+
return array_keys((array) $object);
939+
}
940+
941+
protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = [])
942+
{
943+
return $object->{$attribute};
944+
}
945+
946+
protected function createChildContext(array $parentContext, string $attribute, ?string $format): array
947+
{
948+
$childContext = parent::createChildContext($parentContext, $attribute, $format);
949+
$this->childContextCacheKey = $childContext['cache_key'];
950+
951+
return $childContext;
952+
}
953+
};
954+
955+
$serializer = new Serializer([$normalizer]);
956+
957+
$serializer->normalize($foobar, null, ['cache_key' => 'hardcoded', 'iri' => '/dummy/1']);
958+
$firstChildContextCacheKey = $normalizer->childContextCacheKey;
959+
960+
$serializer->normalize($foobar, null, ['cache_key' => 'hardcoded', 'iri' => '/dummy/2']);
961+
$secondChildContextCacheKey = $normalizer->childContextCacheKey;
962+
963+
$this->assertSame($firstChildContextCacheKey, $secondChildContextCacheKey);
964+
}
965+
966+
public function testChildContextKeepsOriginalContextCacheKey()
967+
{
968+
$foobar = new Dummy();
969+
$foobar->foo = new EmptyDummy();
970+
$foobar->bar = 'bar';
971+
$foobar->baz = 'baz';
972+
973+
$normalizer = new class() extends AbstractObjectNormalizerDummy {
974+
public $childContextCacheKey;
975+
976+
protected function extractAttributes(object $object, string $format = null, array $context = []): array
977+
{
978+
return array_keys((array) $object);
979+
}
980+
981+
protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = [])
982+
{
983+
return $object->{$attribute};
984+
}
985+
986+
protected function createChildContext(array $parentContext, string $attribute, ?string $format): array
987+
{
988+
$childContext = parent::createChildContext($parentContext, $attribute, $format);
989+
$this->childContextCacheKey = $childContext['cache_key'];
990+
991+
return $childContext;
992+
}
993+
};
994+
995+
$serializer = new Serializer([$normalizer]);
996+
$serializer->normalize($foobar, null, ['cache_key' => 'hardcoded', 'iri' => '/dummy/1']);
997+
998+
$this->assertSame('hardcoded-foo', $normalizer->childContextCacheKey);
999+
}
1000+
1001+
public function testChildContextCacheKeyStaysFalseWhenOriginalCacheKeyIsFalse()
1002+
{
1003+
$foobar = new Dummy();
1004+
$ 10000 foobar->foo = new EmptyDummy();
1005+
$foobar->bar = 'bar';
1006+
$foobar->baz = 'baz';
1007+
1008+
$normalizer = new class() extends AbstractObjectNormalizerDummy {
1009+
public $childContextCacheKey;
1010+
1011+
protected function extractAttributes(object $object, string $format = null, array $context = []): array
1012+
{
1013+
return array_keys((array) $object);
1014+
}
1015+
1016+
protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = [])
1017+
{
1018+
return $object->{$attribute};
1019+
}
1020+
1021+
protected function createChildContext(array $parentContext, string $attribute, ?string $format): array
1022+
{
1023+
$childContext = parent::createChildContext($parentContext, $attribute, $format);
1024+
$this->childContextCacheKey = $childContext['cache_key'];
1025+
1026+
return $childContext;
1027+
}
1028+
};
1029+
1030+
$serializer = new Serializer([$normalizer]);
1031+
$serializer->normalize($foobar, null, ['cache_key' => false]);
1032+
1033+
$this->assertFalse($normalizer->childContextCacheKey);
1034+
}
9251035
}
9261036

9271037
class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer

0 commit comments

Comments
 (0)
0