Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5148549

Browse filesBrowse files
Merge branch '5.4' into 6.3
* 5.4: [Messenger] Fix cloned TraceableStack not unstacking the stack independently [DependencyInjection] Fix autocasting null env values to empty string with container.env_var_processors_locator [Cache] Fix support for Redis Sentinel using php-redis 6.0.0 minor #51693 Disable the dead code analysis in Psalm (stof) Update the PR template [SecurityBundle][PasswordHasher] Fix password migration with custom hasher service with security bundle config [Translator] Fix support for `default_path` in XML [HttpClient] Fix TraceableResponse if response has no destruct method
2 parents 4caaa2e + aa56d07 commit 5148549
Copy full SHA for 5148549

File tree

Expand file treeCollapse file tree

14 files changed

+275
-34
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+275
-34
lines changed

‎.github/PULL_REQUEST_TEMPLATE.md

Copy file name to clipboardExpand all lines: .github/PULL_REQUEST_TEMPLATE.md
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
| Q | A
22
| ------------- | ---
3-
| Branch? | 6.4 for features / 5.4, or 6.3 for bug fixes <!-- see below -->
3+
| Branch? | 6.4 for features / 5.4 or 6.3 for bug fixes <!-- see below -->
44
| Bug fix? | yes/no
55
| New feature? | yes/no <!-- please update src/**/CHANGELOG.md files -->
66
| Deprecations? | yes/no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
77
| Tickets | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exists, explain below instead -->
88
| License | MIT
9-
| Doc PR | symfony/symfony-docs#... <!-- required for new features -->
9+
1010
<!--
11-
Replace this notice by a short README for your feature/bugfix.
11+
Replace this notice by a description of your feature/bugfix.
1212
This will help reviewers and should be a good start for the documentation.
1313
1414
Additionally (see https://symfony.com/releases):

‎psalm.xml

Copy file name to clipboardExpand all lines: psalm.xml
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
cacheDirectory="./.github/psalm/cache/"
99
errorBaseline=".github/psalm/psalm.baseline.xml"
1010
findUnusedBaselineEntry="false"
11+
findUnusedCode="false"
1112
>
1213
<projectFiles>
1314
<directory name="src" />

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@
237237
<xsd:attribute name="logging" type="xsd:boolean" />
238238
<xsd:attribute name="formatter" type="xsd:string" />
239239
<xsd:attribute name="cache-dir" type="xsd:string" />
240+
<xsd:attribute name="default-path" type="xsd:string" />
240241
</xsd:complexType>
241242

242243
<xsd:complexType name="pseudo_localization">

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,10 @@ private function createHasher(array $config): Reference|array
721721
{
722722
// a custom hasher service
723723
if (isset($config['id'])) {
724-
return new Reference($config['id']);
724+
return $config['migrate_from'] ?? false ? [
725+
'instance' => new Reference($config['id']),
726+
'migrate_from' => $config['migrate_from'],
727+
] : new Reference($config['id']);
725728
}
726729

727730
if ($config['migrate_from'] ?? false) {

‎src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,33 @@ public function testNothingDoneWithEmptyConfiguration()
887887
$this->assertFalse($container->has('security.authorization_checker'));
888888
}
889889

890+
public function testCustomHasherWithMigrateFrom()
891+
{
892+
$container = $this->getRawContainer();
893+
894+
$container->loadFromExtension('security', [
895+
'enable_authenticator_manager' => true,
896+
'password_hashers' => [
897+
'legacy' => 'md5',
898+
'App\User' => [
899+
'id' => 'App\Security\CustomHasher',
900+
'migrate_from' => 'legacy',
901+
],
902+
],
903+
'firewalls' => ['main' => ['http_basic' => true]],
904+
]);
905+
906+
$container->compile();
907+
908+
$hashersMap = $container->getDefinition('security.password_hasher_factory')->getArgument(0);
909+
910+
$this->assertArrayHasKey('App\User', $hashersMap);
911+
$this->assertEquals($hashersMap['App\User'], [
912+
'instance' => new Reference('App\Security\CustomHasher'),
913+
'migrate_from' => ['legacy'],
914+
]);
915+
}
916+
890917
protected function getRawContainer()
891918
{
892919
$container = new ContainerBuilder();

‎src/Symfony/Component/Cache/Traits/RedisTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/RedisTrait.php
+21-4Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ public static function createConnection(#[\SensitiveParameter] string $dsn, arra
212212
do {
213213
$host = $hosts[$hostIndex]['host'] ?? $hosts[$hostIndex]['path'];
214214
$port = $hosts[$hostIndex]['port'] ?? 0;
215+
$passAuth = isset($params['auth']) && (!$isRedisExt || \defined('Redis::OPT_NULL_MULTIBULK_AS_NULL'));
215216
$address = false;
216217

217218
if (isset($hosts[$hostIndex]['host']) && $tls) {
@@ -221,11 +222,27 @@ public static function createConnection(#[\SensitiveParameter] string $dsn, arra
221222
if (!isset($params['redis_sentinel'])) {
222223
break;
223224
}
224-
$extra = [];
225-
if (\defined('Redis::OPT_NULL_MULTIBULK_AS_NULL') && isset($params['auth'])) {
226-
$extra = [$params['auth']];
225+
226+
if (version_compare(phpversion('redis'), '6.0.0', '>=') && $isRedisExt) {
227+
$options = [
228+
'host' => $host,
229+
'port' => $port,
230+
'connectTimeout' => $params['timeout'],
231+
'persistent' => $params['persistent_id'],
232+
'retryInterval' => $params['retry_interval'],
233+
'readTimeout' => $params['read_timeout'],
234+
];
235+
236+
if ($passAuth) {
237+
$options['auth'] = $params['auth'];
238+
}
239+
240+
$sentinel = new \RedisSentinel($options);
241+
} else {
242+
$extra = $passAuth ? [$params['auth']] : [];
243+
244+
$sentinel = new $sentinelClass($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...$extra);
227245
}
228-
$sentinel = new $sentinelClass($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...$extra);
229246

230247
try {
231248
if ($address = $sentinel->getMasterAddrByName($params['redis_sentinel'])) {

‎src/Symfony/Component/DependencyInjection/Container.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Container.php
+3-7Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,9 @@ protected function getEnv(string $name): mixed
373373
$localName = $name;
374374
}
375375

376-
if ($processors->has($prefix)) {
377-
$processor = $processors->get($prefix);
378-
} else {
379-
$processor = new EnvVarProcessor($this);
380-
if (false === $i) {
381-
$prefix = '';
382-
}
376+
$processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this);
377+
if (false === $i) {
378+
$prefix = '';
383379
}
384380

385381
$this->resolving[$envName] = true;

‎src/Symfony/Component/DependencyInjection/EnvVarProcessorInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/EnvVarProcessorInterface.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface EnvVarProcessorInterface
2424
* Returns the value of the given variable as managed by the current instance.
2525
*
2626
* @param string $prefix The namespace of the variable
27+
* @param string $prefix The namespace of the variable; when the empty string is passed, null values should be kept as is
2728
* @param string $name The name of the variable within the namespace
2829
* @param \Closure(string): mixed $getEnv A closure that allows fetching more env vars
2930
*

‎src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\DependencyInjection\Container;
1616
use Symfony\Component\DependencyInjection\ContainerInterface;
17+
use Symfony\Component\DependencyInjection\EnvVarProcessor;
1718
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1819
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
1920
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
2021
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
2122
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
23+
use Symfony\Component\DependencyInjection\ServiceLocator;
2224
use Symfony\Contracts\Service\ResetInterface;
2325

2426
class ContainerTest extends TestCase
@@ -404,6 +406,33 @@ public function testRequestAnInternalSharedPrivateService()
404406
$c->get('internal_dependency');
405407
$c->get('internal');
406408
}
409+
410+
public function testGetEnvDoesNotAutoCastNullWithDefaultEnvVarProcessor()
411+
{
412+
$container = new Container();
413+
$container->setParameter('env(FOO)', null);
414+
$container->compile();
415+
416+
$r = new \ReflectionMethod($container, 'getEnv');
417+
$r->setAccessible(true);
418+
$this->assertNull($r->invoke($container, 'FOO'));
419+
}
420+
421+
public function testGetEnvDoesNotAutoCastNullWithEnvVarProcessorsLocatorReturningDefaultEnvVarProcessor()
422+
{
423+
$container = new Container();
424+
$container->setParameter('env(FOO)', null);
425+
$container->set('container.env_var_processors_locator', new ServiceLocator([
426+
'string' => static function () use ($container): EnvVarProcessor {
427+
return new EnvVarProcessor($container);
428+
},
429+
]));
430+
$container->compile();
431+
432+
$r = new \ReflectionMethod($container, 'getEnv');
433+
$r->setAccessible(true);
434+
$this->assertNull($r->invoke($container, 'FOO'));
435+
}
407436
}
408437

409438
class ProjectServiceContainer extends Container

‎src/Symfony/Component/HttpClient/Response/TraceableResponse.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/Response/TraceableResponse.php
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ public function __wakeup()
5757
public function __destruct()
5858
{
5959
try {
60-
$this->response->__destruct();
60+
if (method_exists($this->response, '__destruct')) {
61+
$this->response->__destruct();
62+
}
6163
} finally {
6264
if ($this->event?->isStarted()) {
6365
$this->event->stop();

‎src/Symfony/Component/Messenger/Middleware/TraceableMiddleware.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Messenger/Middleware/TraceableMiddleware.php
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,9 @@ public function stop(): void
8888
}
8989
$this->currentEvent = null;
9090
}
91+
92+
public function __clone()
93+
{
94+
$this->stack = clone $this->stack;
95+
}
9196
}

‎src/Symfony/Component/Messenger/Tests/Middleware/TraceableMiddlewareTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Messenger/Tests/Middleware/TraceableMiddlewareTest.php
+89Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Messenger\Middleware\StackInterface;
1717
use Symfony\Component\Messenger\Middleware\StackMiddleware;
1818
use Symfony\Component\Messenger\Middleware\TraceableMiddleware;
19+
use Symfony\Component\Messenger\Middleware\TraceableStack;
1920
use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase;
2021
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
2122
use Symfony\Component\Stopwatch\Stopwatch;
@@ -140,4 +141,92 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
140141
$traced->handle($envelope, new StackMiddleware(new \ArrayIterator([null, $middleware])));
141142
$this->assertSame(1, $middleware->calls);
142143
}
144+
145+
public function testClonedTraceableStackUnstacksIndependently()
146+
{
147+
// import TraceableStack
148+
class_exists(TraceableMiddleware::class);
149+
150+
$stackMiddleware = new StackMiddleware([
151+
null,
152+
$this->createMock(MiddlewareInterface::class),
153+
$this->createMock(MiddlewareInterface::class),
154+
]);
155+
156+
$stopwatch = $this->createMock(Stopwatch::class);
157+
158+
$traceableStack = new TraceableStack($stackMiddleware, $stopwatch, 'command_bus', 'messenger.middleware');
159+
$clonedStack = clone $traceableStack;
160+
161+
$traceableStackMiddleware1 = $traceableStack->next();
162+
$traceableStackMiddleware2 = $traceableStack->next();
163+
$traceableStackTail = $traceableStack->next();
164+
self::assertSame($stackMiddleware, $traceableStackTail);
165+
166+
// unstack clonedStack independently
167+
$clonedStackMiddleware1 = $clonedStack->next();
168+
self::assertSame($traceableStackMiddleware1, $clonedStackMiddleware1);
169+
self::assertNotSame($traceableStackMiddleware2, $clonedStackMiddleware1);
170+
171+
$clonedStackMiddleware2 = $clonedStack->next();
172+
self::assertSame($traceableStackMiddleware2, $clonedStackMiddleware2);
173+
174+
$clonedStackTail = $clonedStack->next();
175+
self::assertNotSame($stackMiddleware, $clonedStackTail, 'stackMiddleware was also cloned');
176+
}
177+
178+
public function testClonedTraceableStackUsesSameStopwatch()
179+
{
180+
// import TraceableStack
181+
class_exists(TraceableMiddleware::class);
182+
183+
$middlewareIterable = [null, $this->createMock(MiddlewareInterface::class)];
184+
185+
$stackMiddleware = new StackMiddleware($middlewareIterable);
186+
187+
$stopwatch = $this->createMock(Stopwatch::class);
188+
$stopwatch->expects($this->exactly(2))->method('isStarted')->willReturn(true);
189+
190+
$startSeries = [
191+
[$this->matches('"%sMiddlewareInterface%s" on "command_bus"'), 'messenger.middleware'],
192+
[$this->identicalTo('Tail on "command_bus"'), 'messenger.middleware'],
193+
[$this->matches('"%sMiddlewareInterface%s" on "command_bus"'), 'messenger.middleware'],
194+
[$this->identicalTo('Tail on "command_bus"'), 'messenger.middleware'],
195+
];
196+
$stopwatch->expects($this->exactly(4))
197+
->method('start')
198+
->willReturnCallback(function (string $name, string $category = null) use (&$startSeries) {
199+
[$constraint, $expectedCategory] = array_shift($startSeries);
200+
201+
$constraint->evaluate($name);
202+
$this->assertSame($expectedCategory, $category);
203+
204+
return $this->createMock(StopwatchEvent::class);
205+
})
206+
;
207+
208+
$stopSeries = [
209+
$this->matches('"%sMiddlewareInterface%s" on "command_bus"'),
210+
$this->matches('"%sMiddlewareInterface%s" on "command_bus"'),
211+
];
212+
$stopwatch->expects($this->exactly(2))
213+
->method('stop')
214+
->willReturnCallback(function (string $name) use (&$stopSeries) {
215+
$constraint = array_shift($stopSeries);
216+
$constraint->evaluate($name);
217+
218+
return $this->createMock(StopwatchEvent::class);
219+
})
220+
;
221+
222+
$traceableStack = new TraceableStack($stackMiddleware, $stopwatch, 'command_bus', 'messenger.middleware');
223+
$clonedStack = clone $traceableStack;
224+
225+
// unstack the stacks independently
226+
$traceableStack->next();
227+
$traceableStack->next();
228+
229+
$clonedStack->next();
230+
$clonedStack->next();
231+
}
143232
}

‎src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php
+32-18Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ public function getPasswordHasher(string|PasswordAuthenticatedUserInterface|Pass
7070
*/
7171
private function createHasher(array $config, bool $isExtra = false): PasswordHasherInterface
7272
{
73+
if (isset($config['instance'])) {
74+
if (!isset($config['migrate_from'])) {
75+
return $config['instance'];
76+
}
77+
78+
$config = $this->getMigratingPasswordConfig($config);
79+
}
80+
7381
if (isset($config['algorithm'])) {
7482
$rawConfig = $config;
7583
$config = $this->getHasherConfigFromAlgorithm($config);
@@ -126,24 +134,8 @@ private function getHasherConfigFromAlgorithm(array $config): array
126134
];
127135
}
128136

129-
if ($frompasswordHashers = ($config['migrate_from'] ?? false)) {
130-
unset($config['migrate_from']);
131-
$hasherChain = [$this->createHasher($config, true)];
132-
133-
foreach ($frompasswordHashers as $name) {
134-
if ($hasher = $this->passwordHashers[$name] ?? false) {
135-
$hasher = $hasher instanceof PasswordHasherInterface ? $hasher : $this->createHasher($hasher, true);
136-
} else {
137-
$hasher = $this->createHasher(['algorithm' => $name], true);
138-
}
139-
140-
$hasherChain[] = $hasher;
141-
}
142-
143-
return [
144-
'class' => MigratingPasswordHasher::class,
145-
'arguments' => $hasherChain,
146-
];
137+
if ($config['migrate_from'] ?? false) {
138+
return $this->getMigratingPasswordConfig($config);
147139
}
148140

149141
switch ($config['algorithm']) {
@@ -223,4 +215,26 @@ private function getHasherConfigFromAlgorithm(array $config): array
223215
],
224216
];
225217
}
218+
219+
private function getMigratingPasswordConfig(array $config): array
220+
{
221+
$frompasswordHashers = $config['migrate_from'];
222+
unset($config['migrate_from']);
223+
$hasherChain = [$this->createHasher($config, true)];
224+
225+
foreach ($frompasswordHashers as $name) {
226+
if ($hasher = $this->passwordHashers[$name] ?? false) {
227+
$hasher = $hasher instanceof PasswordHasherInterface ? $hasher : $this->createHasher($hasher, true);
228+
} else {
229+
$hasher = $this->createHasher(['algorithm' => $name], true);
230+
}
231+
232+
$hasherChain[] = $hasher;
233+
}
234+
235+
return [
236+
'class' => MigratingPasswordHasher::class,
237+
'arguments' => $hasherChain,
238+
];
239+
}
226240
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.