Skip to content

Commit 3f04032

Browse files
Merge branch '7.3' into 7.4
* 7.3: [SecurityBundle] Remove legacy parameter in SecurityDataCollectorTest Fix incorrect serialized data mangling [Console] Specify types of interactive question choices Prevent duplicate entries in module preloads [Validator] Expression constraint docblock incorrectly states default value for negate [ObjectMapper] Preserve non-promoted constructor parameters prefer the public API over reflection in test
2 parents b8eaa4b + cd8b407 commit 3f04032

File tree

12 files changed

+164
-55
lines changed

12 files changed

+164
-55
lines changed

src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class SecurityDataCollectorTest extends TestCase
4444
{
4545
public function testCollectWhenSecurityIsDisabled()
4646
{
47-
$collector = new SecurityDataCollector(null, null, null, null, null, null, true);
47+
$collector = new SecurityDataCollector(null, null, null, null, null, null);
4848
$collector->collect(new Request(), new Response());
4949

5050
$this->assertSame('security', $collector->getName());
@@ -64,7 +64,7 @@ public function testCollectWhenSecurityIsDisabled()
6464
public function testCollectWhenAuthenticationTokenIsNull()
6565
{
6666
$tokenStorage = new TokenStorage();
67-
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null, true);
67+
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null);
6868
$collector->collect(new Request(), new Response());
6969

7070
$this->assertTrue($collector->isEnabled());
@@ -86,7 +86,7 @@ public function testCollectAuthenticationTokenAndRoles(array $roles, array $norm
8686
$tokenStorage = new TokenStorage();
8787
$tokenStorage->setToken(new UsernamePasswordToken(new InMemoryUser('hhamon', 'P4$$w0rD', $roles), 'provider', $roles));
8888

89-
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null, true);
89+
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null);
9090
$collector->collect(new Request(), new Response());
9191
$collector->lateCollect();
9292

@@ -109,7 +109,7 @@ public function testCollectSwitchUserToken()
109109
$tokenStorage = new TokenStorage();
110110
$tokenStorage->setToken(new SwitchUserToken(new InMemoryUser('hhamon', 'P4$$w0rD', ['ROLE_USER']), 'provider', ['ROLE_USER'], $adminToken));
111111

112-
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null, true);
112+
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null);
113113
$collector->collect(new Request(), new Response());
114114
$collector->lateCollect();
115115

@@ -139,7 +139,7 @@ public function testGetFirewall()
139139
->with($request)
140140
->willReturn($firewallConfig);
141141

142-
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()), true);
142+
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()));
143143
$collector->collect($request, new Response());
144144
$collector->lateCollect();
145145
$collected = $collector->getFirewall();
@@ -163,7 +163,7 @@ public function testGetFirewallReturnsNull()
163163
$response = new Response();
164164

165165
// Don't inject any firewall map
166-
$collector = new SecurityDataCollector(null, null, null, null, null, null, true);
166+
$collector = new SecurityDataCollector(null, null, null, null, null, null);
167167
$collector->collect($request, $response);
168168
$this->assertNull($collector->getFirewall());
169169

@@ -173,7 +173,7 @@ public function testGetFirewallReturnsNull()
173173
->disableOriginalConstructor()
174174
->getMock();
175175

176-
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()), true);
176+
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()));
177177
$collector->collect($request, $response);
178178
$this->assertNull($collector->getFirewall());
179179

@@ -183,7 +183,7 @@ public function testGetFirewallReturnsNull()
183183
->disableOriginalConstructor()
184184
->getMock();
185185

186-
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()), true);
186+
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator()));
187187
$collector->collect($request, $response);
188188
$this->assertNull($collector->getFirewall());
189189
}
@@ -225,7 +225,7 @@ public function authenticate(RequestEvent $event): void
225225
$firewall = new TraceableFirewallListener($firewallMap, new EventDispatcher(), new LogoutUrlGenerator());
226226
$firewall->onKernelRequest($event);
227227

228-
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, $firewall, true);
228+
$collector = new SecurityDataCollector(null, null, null, null, $firewallMap, $firewall);
229229
$collector->collect($request, $response);
230230

231231
$this->assertCount(1, $collector->getListeners());
@@ -272,7 +272,7 @@ public function dispatch(object $event, ?string $eventName = null): object
272272
],
273273
]]);
274274

275-
$dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true);
275+
$dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null);
276276

277277
$dataCollector->collect(new Request(), new Response());
278278

@@ -360,7 +360,7 @@ public function dispatch(object $event, ?string $eventName = null): object
360360
],
361361
]);
362362

363-
$dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true);
363+
$dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null);
364364

365365
$dataCollector->collect(new Request(), new Response());
366366

@@ -432,7 +432,7 @@ public function testGetVotersIfAccessDecisionManagerHasNoVoters()
432432
'voterDetails' => [],
433433
]]);
434434

435-
$dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true);
435+
$dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null);
436436

437437
$dataCollector->collect(new Request(), new Response());
438438

src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function render(string|array $entryPoint, array $attributes = []): string
8181
} elseif ('css' !== $data['type']) {
8282
$importMap[$importName] = $path;
8383
if ($preload) {
84-
$modulePreloads[] = $path;
84+
$modulePreloads[$path] = $path;
8585
}
8686
} elseif ($preload) {
8787
$webLinks[$path] = 'style';

src/Symfony/Component/Config/ResourceCheckerConfigCache.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public function write(string $content, ?array $metadata = null): void
126126
}
127127

128128
$ser = preg_replace_callback('/;O:(\d+):"/', static fn ($m) => ';O:'.(9 + $m[1]).':"Tracking\\', $ser);
129-
$ser = preg_replace_callback('/s:(\d+):"\0[^\0]++\0/', static fn ($m) => 's:'.($m[1] - \strlen($m[0]) + 6).':"', $ser);
129+
$ser = preg_replace_callback('/s:(\d+):"(\0[^\0]++\0)/', static fn ($m) => 's:'.($m[1] - \strlen($m[2])).':"', $ser);
130130
$ser = unserialize($ser, ['allowed_classes' => false]);
131131
$ser = @json_encode($ser, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE) ?: [];
132132
$ser = str_replace('"__PHP_Incomplete_Class_Name":"Tracking\\\\', '"@type":"', $ser);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Symfony\Component\Config\Tests\Fixtures;
4+
5+
use Symfony\Component\Config\Resource\ResourceInterface;
6+
7+
class ResourceWithVeryVeryVeryVeryVeryVeryVeryVeryLongName implements ResourceInterface
8+
{
9+
public function __construct(private string $resource)
10+
{
11+
}
12+
13+
public function __toString(): string
14+
{
15+
return __CLASS__;
16+
}
17+
}

src/Symfony/Component/Config/Tests/ResourceCheckerConfigCacheTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Config\Resource\FileResource;
1616
use Symfony\Component\Config\ResourceCheckerConfigCache;
1717
use Symfony\Component\Config\ResourceCheckerInterface;
18+
use Symfony\Component\Config\Tests\Fixtures\ResourceWithVeryVeryVeryVeryVeryVeryVeryVeryLongName;
1819
use Symfony\Component\Config\Tests\Resource\ResourceStub;
1920

2021
class ResourceCheckerConfigCacheTest extends TestCase
@@ -170,4 +171,21 @@ public function testCacheWithCustomMetaFile()
170171
],
171172
], \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
172173
}
174+
175+
public function testCacheWithResourceWithLongPropertyId()
176+
{
177+
$cache = new ResourceCheckerConfigCache($this->cacheFile);
178+
$cache->write('foo', [new ResourceWithVeryVeryVeryVeryVeryVeryVeryVeryLongName(__FILE__)]);
179+
180+
$this->assertStringNotEqualsFile($this->cacheFile.'.meta', '');
181+
182+
$this->assertStringEqualsFile($this->cacheFile.'.meta.json', json_encode([
183+
'resources' => [
184+
[
185+
'@type' => ResourceWithVeryVeryVeryVeryVeryVeryVeryVeryLongName::class,
186+
'resource' => __FILE__,
187+
],
188+
],
189+
], \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
190+
}
173191
}

src/Symfony/Component/Console/Question/ChoiceQuestion.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ class ChoiceQuestion extends Question
2525
private string $errorMessage = 'Value "%s" is invalid';
2626

2727
/**
28-
* @param string $question The question to ask to the user
29-
* @param array $choices The list of available choices
30-
* @param string|bool|int|float|null $default The default answer to return
28+
* @param string $question The question to ask to the user
29+
* @param array<string|bool|int|float> $choices The list of available choices
30+
* @param string|bool|int|float|null $default The default answer to return
3131
*/
3232
public function __construct(
3333
string $question,
@@ -45,7 +45,7 @@ public function __construct(
4545
}
4646

4747
/**
48-
* Returns available choices.
48+
* @return array<string|bool|int|float>
4949
*/
5050
public function getChoices(): array
5151
{

src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ public function testReverseTransformDoesNotCauseIntegerPrecisionLoss()
714714
$this->markTestSkipped('Test is not applicable on 32-bit systems where no integer loses precision when cast to float.');
715715
}
716716

717-
$transformer = new NumberToLocalizedStringTransformer();
717+
$transformer = new NumberToLocalizedStringTransformer(2);
718718

719719
// Test a large integer that causes actual precision loss when cast to float
720720
$largeInt = \PHP_INT_MAX - 1; // This value loses precision when cast to float
@@ -723,24 +723,4 @@ public function testReverseTransformDoesNotCauseIntegerPrecisionLoss()
723723
$this->assertSame($largeInt, $result);
724724
$this->assertIsInt($result);
725725
}
726-
727-
public function testRoundMethodKeepsIntegersAsIntegers()
728-
{
729-
if (\PHP_INT_SIZE === 4) {
730-
$this->markTestSkipped('Test is not applicable on 32-bit systems where no integer loses precision when cast to float.');
731-
}
732-
733-
$transformer = new NumberToLocalizedStringTransformer(2); // scale=2 triggers rounding
734-
735-
// Use reflection to test the private round() method directly
736-
$reflection = new \ReflectionClass($transformer);
737-
$roundMethod = $reflection->getMethod('round');
738-
739-
$int = \PHP_INT_MAX - 1;
740-
$result = $roundMethod->invoke($transformer, $int);
741-
742-
// With the fix, integers should stay as integers, not be converted to floats
743-
$this->assertSame($int, $result);
744-
$this->assertIsInt($result);
745-
}
746726
}

src/Symfony/Component/ObjectMapper/ObjectMapper.php

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,23 @@ public function map(object $source, object|string|null $target = null): object
9191

9292
$this->objectMap[$source] = $mappedTarget;
9393
$ctorArguments = [];
94-
$constructor = $targetRefl->getConstructor();
95-
foreach ($constructor?->getParameters() ?? [] as $parameter) {
96-
if (!$parameter->isPromoted()) {
97-
continue;
98-
}
99-
94+
$targetConstructor = $targetRefl->getConstructor();
95+
foreach ($targetConstructor?->getParameters() ?? [] as $parameter) {
10096
$parameterName = $parameter->getName();
101-
$property = $targetRefl->getProperty($parameterName);
10297

103-
if ($property->isReadOnly() && $property->isInitialized($mappedTarget)) {
104-
continue;
98+
if ($targetRefl->hasProperty($parameterName)) {
99+
$property = $targetRefl->getProperty($parameterName);
100+
101+
if ($property->isReadOnly() && $property->isInitialized($mappedTarget)) {
102+
continue;
103+
}
105104
}
106105

107-
// this may be filled later on see storeValue
108-
$ctorArguments[$parameterName] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null;
106+
if ($this->isReadable($source, $parameterName)) {
107+
$ctorArguments[$parameterName] = $this->getRawValue($source, $parameterName);
108+
} else {
109+
$ctorArguments[$parameterName] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null;
110+
}
109111
}
110112

111113
$readMetadataFrom = $source;
@@ -159,7 +161,7 @@ public function map(object $source, object|string|null $target = null): object
159161
}
160162
}
161163

162-
if (!$mappingToObject && !$map?->transform && $constructor) {
164+
if (!$mappingToObject && !$map?->transform && $targetConstructor) {
163165
try {
164166
$mappedTarget->__construct(...$ctorArguments);
165167
} catch (\ReflectionException $e) {
@@ -186,6 +188,19 @@ public function map(object $source, object|string|null $target = null): object
186188
return $mappedTarget;
187189
}
188190

191+
private function isReadable(object $source, string $propertyName): bool
192+
{
193+
if ($this->propertyAccessor) {
194+
return $this->propertyAccessor->isReadable($source, $propertyName);
195+
}
196+
197+
if (!property_exists($source, $propertyName) && !isset($source->{$propertyName})) {
198+
return false;
199+
}
200+
201+
return true;
202+
}
203+
189204
private function getRawValue(object $source, string $propertyName): mixed
190205
{
191206
if ($this->propertyAccessor) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\ObjectMapper\Tests\Fixtures\InitializedConstructor;
13+
14+
class C
15+
{
16+
public string $bar;
17+
18+
public function __construct(string $bar)
19+
{
20+
$this->bar = $bar;
21+
}
22+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\ObjectMapper\Tests\Fixtures\InitializedConstructor;
13+
14+
use Symfony\Component\ObjectMapper\Attribute\Map;
15+
16+
class D
17+
{
18+
#[Map(if: false)]
19+
public string $barUpperCase;
20+
21+
public function __construct(string $bar)
22+
{
23+
$this->barUpperCase = strtoupper($bar);
24+
}
25+
}

0 commit comments

Comments
 (0)