8000 Merge branch '6.0' into 6.1 · symfony/symfony@c9e27ee · GitHub
[go: up one dir, main page]

Skip to content

Commit c9e27ee

Browse files
Merge branch '6.0' into 6.1
* 6.0: [Lock] fix tests [Serializer] Ensuring end of line character apply with constructor settings in CSV encoder [Serializer] Fix ignored callbacks in denormalization fix sorting bug
2 parents a1133b5 + 6f81bf8 commit c9e27ee

File tree

16 files changed

+352
-57
lines changed

16 files changed

+352
-57
lines changed

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ protected function sortTaggedServicesByPriority(array $services): array
248248
{
249249
$maxPriority = [];
250250
foreach ($services as $service => $tags) {
251-
$maxPriority[$service] = 0;
251+
$maxPriority[$service] = \PHP_INT_MIN;
252252
foreach ($tags as $tag) {
253253
$currentPriority = $tag['priority'] ?? 0;
254254
if ($maxPriority[$service] < $currentPriority) {

src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ public static function getContainerDefinitionsWithPriorityTags()
191191
$definition1 = new Definition('Full\\Qualified\\Class1');
192192
$definition2 = new Definition('Full\\Qualified\\Class2');
193193
$definition3 = new Definition('Full\\Qualified\\Class3');
194+
$definition4 = new Definition('Full\\Qualified\\Class4');
194195

195196
return [
196197
'definition_1' => $definition1
@@ -219,6 +220,13 @@ public static function getContainerDefinitionsWithPriorityTags()
219220
->setAbstract(false)
220221
->addTag('tag1', ['attr1' => 'val1', 'attr2' => 'val2', 'priority' => 0])
221222
->addTag('tag1', ['attr3' => 'val3', 'priority' => 40]),
223+
'definition_4' => $definition4
224+
->setPublic(true)
225+
->setSynthetic(true)
226+
->setFile('/path/to/file')
227+
->setLazy(false)
228+
->setAbstract(false)
229+
->addTag('tag1', ['priority' => 0]),
222230
];
223231
}
224232

src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,25 @@
6363
}
6464
]
6565
},
66+
"definition_4": {
67+
"class": "Full\\Qualified\\Class4",
68+
"public": true,
69+
"synthetic": true,
70+
"lazy": false,
71+
"shared": true,
72+
"abstract": false,
73+
"autowire": false,
74+
"autoconfigure": false,
75+
"file": "\/path\/to\/file",
76+
"tags": [
77+
{
78+
"name": "tag1",
79+
"parameters": {
80+
"priority": 0
81+
}
82+
}
83+
]
84+
},
6685
"definition_2": {
6786
"class": "Full\\Qualified\\Class2",
6887
"public": true,

src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ Definitions
4444
- Attr2: val2
4545
- Tag: `tag2`
4646

47+
### definition_4
48+
49+
- Class: `Full\Qualified\Class4`
50+
- Public: yes
51+
- Synthetic: yes
52+
- Lazy: no
53+
- Shared: yes
54+
- Abstract: no
55+
- Autowired: no
56+
- Autoconfigured: no
57+
- File: `/path/to/file`
58+
- Tag: `tag1`
59+
- Priority: 0
60+
4761
### definition_2
4862

4963
- Class: `Full\Qualified\Class2`

src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
(same service as previous, another tag) val1 val2 0
1010
definition_1 val1 30 Full\Qualified\Class1
1111
(same service as previous, another tag) val2
12+
definition_4 0 Full\Qualified\Class4
1213
definition_2 val1 val2 -20 Full\Qualified\Class2
1314
------------------------------------------ ------- ------- ---------- ------- -----------------------
1415

src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_priority_tag.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
<tag name="tag2"/>
3030
</tags>
3131
</definition>
32+
<definition id="definition_4" class="Full\Qualified\Class4" public="true" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
33+
<tags>
34+
<tag name="tag1">
35+
<parameter name="priority">0</parameter>
36+
</tag>
37+
</tags>
38+
</definition>
3239
<definition id="definition_2" class="Full\Qualified\Class2" public="true" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
3340
<tags>
3441
<tag name="tag1">

src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Doctrine\DBAL\Connection;
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\Cache\Adapter\AbstractAdapter;
17+
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
1718
use Symfony\Component\Cache\Traits\RedisProxy;
1819
use Symfony\Component\Lock\Store\DoctrineDbalPostgreSqlStore;
1920
use Symfony\Component\Lock\Store\DoctrineDbalStore;
@@ -66,7 +67,7 @@ public function validConnections()
6667
if (\extension_loaded('sysvsem')) {
6768
yield ['semaphore', SemaphoreStore::class];
6869
}
69-
if (class_exists(\Memcached::class) && class_exists(AbstractAdapter::class)) {
70+
if (class_exists(AbstractAdapter::class) && MemcachedAdapter::isSupported()) {
7071
yield ['memcached://server.com', MemcachedStore::class];
7172
yield ['memcached:?host[localhost]&host[localhost:12345]', MemcachedStore::class];
7273
}

src/Symfony/Component/Serializer/Encoder/CsvEncoder.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,19 +90,20 @@ public function encode(mixed $data, string $format, array $context = []): string
9090
unset($value);
9191

9292
$headers = array_merge(array_values($headers), array_diff($this->extractHeaders($data), $headers));
93+
$endOfLine = $context[self::END_OF_LINE] ?? $this->defaultContext[self::END_OF_LINE];
9394

9495
if (!($context[self::NO_HEADERS_KEY] ?? $this->defaultContext[self::NO_HEADERS_KEY])) {
9596
fputcsv($handle, $headers, $delimiter, $enclosure, $escapeChar);
96-
if ("\n" !== ($context[self::END_OF_LINE] ?? $this->defaultContext[self::END_OF_LINE]) && 0 === fseek($handle, -1, \SEEK_CUR)) {
97-
fwrite($handle, $context[self::END_OF_LINE]);
97+
if ("\n" !== $endOfLine && 0 === fseek($handle, -1, \SEEK_CUR)) {
98+
fwrite($handle, $endOfLine);
9899
}
99100
}
100101

101102
$headers = array_fill_keys($headers, '');
102103
foreach ($data as $row) {
103104
fputcsv($handle, array_replace($headers, $row), $delimiter, $enclosure, $escapeChar);
104-
if ("\n" !== ($context[self::END_OF_LINE] ?? $this->defaultContext[self::END_OF_LINE]) && 0 === fseek($handle, -1, \SEEK_CUR)) {
105-
fwrite($handle, $context[self::END_OF_LINE]);
105+
if ("\n" !== $endOfLine && 0 === fseek($handle, -1, \SEEK_CUR)) {
106+
fwrite($handle, $endOfLine);
106107
}
107108
}
108109

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

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,15 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
8080
public const DEFAULT_CONSTRUCTOR_ARGUMENTS = 'default_constructor_arguments';
8181

8282
/**
83-
* Hashmap of field name => callable to normalize this field.
83+
* Hashmap of field name => callable to (de)normalize this field.
8484
*
8585
* The callable is called if the field is encountered with the arguments:
8686
*
87-
* - mixed $attributeValue value of this field
88-
* - object $object the whole object being normalized
89-
* - string $attributeName name of the attribute being normalized
90-
* - string $format the requested format
91-
* - array $context the serialization context
87+
* - mixed $attributeValue value of this field
88+
* - object|string $object the whole object being normalized or the object's class being denormalized
89+
* - string $attributeName name of the attribute being (de)normalized
90+
* - string $format the requested format
91+
* - array $context the serialization context
9292
*/
9393
public const CALLBACKS = 'callbacks';
9494

@@ -143,17 +143,7 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory
143143
$this->nameConverter = $nameConverter;
144144
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
145145

146-
if (isset($this->defaultContext[self::CALLBACKS])) {
147-
if (!\is_array($this->defaultContext[self::CALLBACKS])) {
148-
throw new InvalidArgumentException(sprintf('The "%s" default context option must be an array of callables.', self::CALLBACKS));
149-
}
150-
151-
foreach ($this->defaultContext[self::CALLBACKS] as $attribute => $callback) {
152-
if (!\is_callable($callback)) {
153-
throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" default context option.', $attribute, self::CALLBACKS));
154-
}
155-
}
156-
}
146+
$this->validateCallbackContext($this->defaultContext, 'default');
157147

158148
if (isset($this->defaultContext[self::CIRCULAR_REFERENCE_HANDLER]) && !\is_callable($this->defaultContext[self::CIRCULAR_REFERENCE_HANDLER])) {
159149
throw new InvalidArgumentException(sprintf('Invalid callback found in the "%s" default context option.', self::CIRCULAR_REFERENCE_HANDLER));
@@ -433,7 +423,7 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara
433423
throw new LogicException(sprintf('Cannot create an instance of "%s" from serialized data because the serializer inject in "%s" is not a denormalizer.', $parameterClass, static::class));
434424
}
435425

436-
return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName, $format));
426+
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName, $format));
437427
}
438428
} catch (\ReflectionException $e) {
439429
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
@@ -445,7 +435,7 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara
445435
return null;
446436
}
447437

448-
return $parameterData;
438+
return $this->applyCallbacks($parameterData, $class->getName(), $parameterName, $format, $context);
449439
}
450440

451441
/**
@@ -461,4 +451,38 @@ protected function createChildContext(array $parentContext, string $attribute, ?
461451

462452
return $parentContext;
463453
}
454+
455+
/**
456+
* Validate callbacks set in context.
457+
*
458+
* @param string $contextType Used to specify which context is invalid in exceptions
459+
*
460+
* @throws InvalidArgumentException
461+
*/
462+
final protected function validateCallbackContext(array $context, string $contextType = ''): void
463+
{
464+
if (!isset($context[self::CALLBACKS])) {
465+
return;
466+
}
467+
468+
if (!\is_array($context[self::CALLBACKS])) {
469+
throw new InvalidArgumentException(sprintf('The "%s"%s context option must be an array of callables.', self::CALLBACKS, '' !== $contextType ? " $contextType" : ''));
470+
}
471+
472+
foreach ($context[self::CALLBACKS] as $attribute => $callback) {
473+
if (!\is_callable($callback)) {
474+
throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s"%s context option.', $attribute, self::CALLBACKS, '' !== $contextType ? " $contextType" : ''));
475+
}
476+
}
477+
}
478+
479+
final protected function applyCallbacks(mixed $value, object|string $object, string $attribute, ?string $format, array $context): mixed
480+
{
481+
/**
482+
* @var callable|null
483+
*/
484+
$callback = $context[self::CALLBACKS][$attribute] ?? $this->defaultContext[self::CALLBACKS][$attribute] ?? null;
485+
486+
return $callback ? $callback($value, $object, $attribute, $format, $context) : $value;
487+
}
464488
}

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

Lines changed: 10 additions & 19 deletions
10000
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,7 @@ public function normalize(mixed $object, string $format = null, array $context =
151151
$context['cache_key'] = $this->getCacheKey($format, $context);
152152
}
153153

154-
if (isset($context[self::CALLBACKS])) {
155-
if (!\is_array($context[self::CALLBACKS])) {
156-
throw new InvalidArgumentException(sprintf('The "%s" context option must be an array of callables.', self::CALLBACKS));
157-
}
158-
159-
foreach ($context[self::CALLBACKS] as $attribute => $callback) {
160-
if (!\is_callable($callback)) {
161-
throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" context option.', $attribute, self::CALLBACKS));
162-
}
163-
}
164-
}
154+
$this->validateCallbackContext($context);
165155

166156
if ($this->isCircularReference($object, $context)) {
167157
return $this->handleCircularReference($object, $format, $context);
@@ -207,13 +197,7 @@ public function normalize(mixed $object, string $format = null, array $context =
207197
$attributeValue = $maxDepthHandler($attributeValue, $object, $attribute, $format, $attributeContext);
208198
}
209199

210-
/**
211-
* @var callable|null
212-
*/
213-
$callback = $context[self::CALLBACKS][$attribute] ?? $this->defaultContext[self::CALLBACKS][$attribute] ?? null;
214-
if ($callback) {
215-
$attributeValue = $callback($attributeValue, $object, $attribute, $format, $attributeContext);
216-
}
200+
$attributeValue = $this->applyCallbacks($attributeValue, $object, $attribute, $format, $attributeContext);
217201

218202
if (null !== $attributeValue && !is_scalar($attributeValue)) {
219203
$stack[$attribute] = $attributeValue;
@@ -369,6 +353,8 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
369353
$context['cache_key'] = $this->getCacheKey($format, $context);
370354
}
371355

356+
$this->validateCallbackContext($context);
357+
372358
$allowedAttributes = $this->getAllowedAttributes($type, $context, true);
373359
$normalizedData = $this->prepareForDenormalization($data);
374360
$extraAttributes = [];
@@ -412,6 +398,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
412398
throw $exception;
413399
}
414400
}
401+
402+
$value = $this->applyCallbacks($value, $resolvedClass, $attribute, $format, $attributeContext);
403+
415404
try {
416405
$this->setAttributeValue($object, $attribute, $value, $format, $attributeContext);
417406
} catch (InvalidArgumentException $e) {
@@ -600,7 +589,9 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara
600589
return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
601590
}
602591

603-
return $this->validateAndDenormalize($types, $class->getName(), $parameterName, $parameterData, $format, $context);
592+
$parameterData = $this->validateAndDenormalize($types, $class->getName(), $parameterName, $parameterData, $format, $context);
593+
594+
return $this->applyCallbacks($parameterData, $class->getName(), $parameterName, $format, $context);
604595
}
605596

606597
/**

src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,4 +691,12 @@ public function testEndOfLine()
691691

692692
$this->assertSame("foo,bar\r\nhello,test\r\n", $this->encoder->encode($value, 'csv', [CsvEncoder::END_OF_LINE => "\r\n"]));
693693
}
694+
695+
public function testEndOfLinePassedInConstructor()
696+
{
697+
$value = ['foo' => 'hello', 'bar' => 'test'];
698+
699+
$encoder = new CsvEncoder([CsvEncoder::END_OF_LINE => "\r\n"]);
700+
$this->assertSame("foo,bar\r\nhello,test\r\n", $encoder->encode($value, 'csv'));
701+
}
694702
}

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,34 @@ class CallbacksObject
66
{
77
public $bar;
88

9-
public function __construct($bar = null)
9+
/**
10+
* @var string|null
11+
*/
12+
public $foo;
13+
14+
public function __construct($bar = null, string $foo = null)
1015
{
1116
$this->bar = $bar;
17+
$this->foo = $foo;
1218
}
1319

1420
public function getBar()
1521
{
1622
return $this->bar;
1723
}
24+
25+
public function setBar($bar)
26+
{
27+
$this->bar = $bar;
28+
}
29+
30+
public function getFoo(): ?string
31+
{
32+
return $this->foo;
33+
}
34+
35+
public function setFoo(?string $foo)
36+
{
37+
$this->foo = $foo;
38+
}
1839
}

0 commit comments

Comments
 (0)
0