diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 6d5d4c2dfa0a5..3a51ed538ba69 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -23,6 +23,8 @@
EOF;
return (new PhpCsFixer\Config())
+ // @see https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/7777
+ ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
->setRules([
'@PHP71Migration' => true,
'@PHPUnit75Migration:risky' => true,
diff --git a/CHANGELOG-5.4.md b/CHANGELOG-5.4.md
index c160dd4fef756..5fcb1b3325c48 100644
--- a/CHANGELOG-5.4.md
+++ b/CHANGELOG-5.4.md
@@ -7,6 +7,26 @@ in 5.4 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.4.0...v5.4.1
+* 5.4.42 (2024-07-26)
+
+ * bug #57815 [Console][PhpUnitBridge][VarDumper] Fix `NO_COLOR` empty value handling (alexandre-daubois)
+ * bug #57828 [Translation] Fix CSV escape char in `CsvFileLoader` on PHP >= 7.4 (alexandre-daubois)
+ * bug #57812 [Validator] treat uninitialized properties referenced by property paths as null (xabbuh)
+ * bug #57816 [DoctrineBridge] fix messenger bus dispatch inside an active transaction (IndraGunawan)
+ * bug #57799 [ErrorHandler][VarDumper] Remove PHP 8.4 deprecations (alexandre-daubois)
+ * bug #57802 [PropertyInfo] Fix nullable value returned from extractFromMutator on CollectionType (benjilebon)
+ * bug #57832 [DependencyInjection] Do not try to load default method name on interface (lyrixx)
+ * bug #57753 [ErrorHandler] restrict the maximum length of the X-Debug-Exception header (xabbuh)
+ * bug #57674 [Cache] Improve `dbindex` DSN parameter parsing (constantable)
+ * bug #57663 [Cache] use copy() instead of rename() on Windows (xabbuh)
+ * bug #57617 [PropertyInfo] Handle collection in PhpStan same as PhpDoc (mtarld)
+ * bug #54057 [Messenger] Passing actual `Envelope` to `WorkerMessageRetriedEvent` (daffoxdev)
+ * bug #57645 [Routing] Discard in-memory cache of routes when writing the file-based cache (mpdude)
+ * bug #57621 [Mailer] force HTTP 1.1 for Mailgun API requests (xabbuh)
+ * bug #57616 [String] Revert "Fixed u()->snake(), b()->snake() and s()->snake() methods" (nicolas-grekas)
+ * bug #57594 [String] Normalize underscores in snake() (xabbuh)
+ * bug #57585 [HttpFoundation] Fix MockArraySessionStorage to generate more conform ids (Seldaek)
+
* 5.4.41 (2024-06-28)
* bug #57497 [String] Fixed u()->snake(), b()->snake() and s()->snake() methods (arczinosek)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 8542f255df6a1..e0cdfdc00f607 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -25,8 +25,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Jérémy DERUSSÉ (jderusse)
- Roland Franssen
- Jules Pietri (heah)
- - Johannes S (johannes)
- Oskar Stark (oskarstark)
+ - Johannes S (johannes)
- Kris Wallsmith (kriswallsmith)
- Jakub Zalas (jakubzalas)
- Yonel Ceruto (yonelceruto)
@@ -57,25 +57,25 @@ The Symfony Connect username in parenthesis allows to get more information
- Vincent Langlet (deviling)
- Valentin Udaltsov (vudaltsov)
- Alexandre Salomé (alexandresalome)
+ - Simon André (simonandre)
- Grégoire Paris (greg0ire)
- William DURAND
- ornicar
- Dany Maillard (maidmaid)
- - Simon André (simonandre)
- Eriksen Costa
- Diego Saint Esteben (dosten)
- stealth35 (stealth35)
- Alexander Mols (asm89)
- Gábor Egyed (1ed)
- Francis Besset (francisbesset)
+ - Mathias Arlaud (mtarld)
- Titouan Galopin (tgalopin)
- Pierre du Plessis (pierredup)
- David Maicher (dmaicher)
- Bulat Shakirzyanov (avalanche123)
- - Mathias Arlaud (mtarld)
+ - Tomasz Kowalczyk (thunderer)
- Iltar van der Berg
- Miha Vrhovnik (mvrhov)
- - Tomasz Kowalczyk (thunderer)
- Gary PEGEOT (gary-p)
- Saša Stamenković (umpirsky)
- Allison Guilhem (a_guilhem)
@@ -98,6 +98,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ruud Kamphuis (ruudk)
- Andrej Hudec (pulzarraider)
- Jáchym Toušek (enumag)
+ - Tomas Norkūnas (norkunas)
- Christian Raue
- Eric Clemmons (ericclemmons)
- Denis (yethee)
@@ -107,7 +108,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Douglas Greenshields (shieldo)
- Frank A. Fiebig (fafiebig)
- Baldini
- - Tomas Norkūnas (norkunas)
- Alex Pott
- Fran Moreno (franmomu)
- Charles Sarrazin (csarrazi)
@@ -132,10 +132,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Phil E. Taylor (philetaylor)
- Joel Wurtz (brouznouf)
- John Wards (johnwards)
+ - Yanick Witschi (toflar)
- Théo FIDRY
- Antoine Hérault (herzult)
- Konstantin.Myakshin
- - Yanick Witschi (toflar)
- Jeroen Spee (jeroens)
- Arnaud Le Blanc (arnaud-lb)
- Sebastiaan Stok (sstok)
@@ -155,6 +155,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Vladimir Tsykun (vtsykun)
- Jacob Dreesen (jdreesen)
- Włodzimierz Gajda (gajdaw)
+ - Nicolas Philippe (nikophil)
- Javier Spagnoletti (phansys)
- Martin Auswöger
- Adrien Brault (adrienbrault)
@@ -162,9 +163,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Teoh Han Hui (teohhanhui)
- Przemysław Bogusz (przemyslaw-bogusz)
- Colin Frei
- - Nicolas Philippe (nikophil)
- excelwebzone
- Paráda József (paradajozsef)
+ - Maximilian Beckers (maxbeckers)
- Baptiste Clavié (talus)
- Alexander Schwenn (xelaris)
- Fabien Pennequin (fabienpennequin)
@@ -172,7 +173,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Malte Schlüter (maltemaltesich)
- jeremyFreeAgent (jeremyfreeagent)
- Michael Babker (mbabker)
- - Maximilian Beckers (maxbeckers)
- Valtteri R (valtzu)
- Joshua Thijssen
- Vasilij Dusko
@@ -235,6 +235,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Roland Franssen :)
- Romain Monteil (ker0x)
- Sergey (upyx)
+ - Florent Morselli (spomky_)
- Marco Pivetta (ocramius)
- Antonio Pauletich (x-coder264)
- Vincent Touzet (vincenttouzet)
@@ -265,7 +266,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Tyson Andre
- GDIBass
- Samuel NELA (snela)
- - Florent Morselli (spomky_)
+ - Baptiste Leduc (korbeil)
- Vincent AUBERT (vincent)
- Michael Voříšek
- zairig imad (zairigimad)
@@ -289,6 +290,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Martin Hujer (martinhujer)
- Sergey Linnik (linniksa)
- Richard Miller
+ - Quynh Xuan Nguyen (seriquynh)
+ - Victor Bocharsky (bocharsky_bw)
- Aleksandar Jakovljevic (ajakov)
- Mario A. Alvarez Garcia (nomack84)
- Thomas Rabaix (rande)
@@ -297,7 +300,7 @@ The Symfony Connect username in parenthesis allows to get more information
- DQNEO
- Chi-teck
- Andre Rømcke (andrerom)
- - Baptiste Leduc (korbeil)
+ - Patrick Landolt (scube)
- Karoly Gossler (connorhu)
- Timo Bakx (timobakx)
- Giorgio Premi
@@ -319,7 +322,6 @@ The Symfony Connect username in parenthesis allows to get more information
- sun (sun)
- Larry Garfield (crell)
- Leo Feyer
- - Victor Bocharsky (bocharsky_bw)
- Nikolay Labinskiy (e-moe)
- Asis Pattisahusiwa
- Martin Schuhfuß (usefulthink)
@@ -355,7 +357,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Maxime Veber (nek-)
- Valentine Boineau (valentineboineau)
- Rui Marinho (ruimarinho)
- - Patrick Landolt (scube)
- Filippo Tessarotto (slamdunk)
- Jeroen Noten (jeroennoten)
- Possum
@@ -492,7 +493,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Andrew Moore (finewolf)
- Bertrand Zuchuat (garfield-fr)
- Marc Morera (mmoreram)
- - Quynh Xuan Nguyen (seriquynh)
- Gabor Toth (tgabi333)
- realmfoo
- Fabien S (bafs)
@@ -549,6 +549,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Martin Herndl (herndlm)
- Dmytro Borysovskyi (dmytr0)
- Johann Pardanaud
+ - Pierre Rineau
- Kai Dederichs
- Pavel Kirpitsov (pavel-kirpichyov)
- Robert Meijers
@@ -738,6 +739,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Miro Michalicka
- M. Vondano
- Dominik Zogg
+ - Maximilian Zumbansen
- Vadim Borodavko (javer)
- Tavo Nieves J (tavoniievez)
- Luc Vieillescazes (iamluc)
@@ -750,7 +752,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Giso Stallenberg (gisostallenberg)
- Rob Bast
- Roberto Espinoza (respinoza)
- - Pierre Rineau
- Soufian EZ ZANTAR (soezz)
- Marek Zajac
- Adam Harvey
@@ -889,6 +890,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Marcin Chyłek (songoq)
- Ned Schwartz
- Ziumin
+ - Daniel Tiringer
- Lenar Lõhmus
- Ilija Tovilo (ilijatovilo)
- Sander Toonen (xatoo)
@@ -925,6 +927,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ninos
- julien57
- Mátyás Somfai (smatyas)
+ - MrMicky
- Bastien DURAND (deamon)
- Dmitry Simushev
- alcaeus
@@ -1390,7 +1393,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Jason Woods
- mwsaz
- bogdan
- - Daniel Tiringer
- Geert De Deckere
- grizlik
- Derek ROTH
@@ -1505,7 +1507,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Valérian Galliat
- Sorin Pop (sorinpop)
- d-ph
- - MrMicky
- Stewart Malik
- Renan Taranto (renan-taranto)
- Ninos Ego
@@ -1522,6 +1523,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Amaury Leroux de Lens (amo__)
- Rene de Lima Barbosa (renedelima)
- Christian Jul Jensen
+ - Lukas Kaltenbach
- Alexandre GESLIN
- The Whole Life to Learn
- Pierre Tondereau
@@ -1772,6 +1774,7 @@ The Symfony Connect username in parenthesis allows to get more information
- benatespina (benatespina)
- Denis Kop
- Fabrice Locher
+ - Konstantin Chigakov
- Kamil Szalewski (szal1k)
- Jean-Guilhem Rouel (jean-gui)
- Yoann MOROCUTTI
@@ -1830,7 +1833,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Atthaphon Urairat
- Benoit Garret
- HellFirePvP
- - Maximilian Zumbansen
- Maximilian Ruta (deltachaos)
- Jon Green (jontjs)
- Jakub Sacha
@@ -1898,6 +1900,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Albert Ganiev (helios-ag)
- Neil Katin
- Oleg Mifle
+ - V1nicius00
- David Otton
- Will Donohoe
- peter
@@ -1966,6 +1969,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Roger Webb
- Dmitriy Simushev
- Pawel Smolinski
+ - Yury (daffox)
- John Espiritu (johnillo)
- Tomasz (timitao)
- Nguyen Tuan Minh (tuanminhgp)
@@ -2407,6 +2411,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Alex Silcock
- Raphael Hardt
- Ivan Nemets
+ - Dave Long
- Qingshan Luo
- Michael Olšavský
- Ergie Gonzaga
@@ -2526,6 +2531,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Wouter de Wild
- Peter Potrowl
- povilas
+ - andreybolonin1989@gmail.com
- Gavin Staniforth
- bahram
- Alessandro Tagliapietra (alex88)
@@ -2540,6 +2546,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Tiago Garcia (tiagojsag)
- Artiom
- Jakub Simon
+ - Petrisor Ciprian Daniel
- Eviljeks
- robin.de.croock
- Brandon Antonio Lorenzo
@@ -2706,6 +2713,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Nil Borodulia
- Adam Katz
- Almog Baku (almogbaku)
+ - Boris Grishenko (arczinosek)
- Arrakis (arrakis)
- Danil Khaliullin (bifidokk)
- Benjamin Schultz (bschultz)
@@ -2893,6 +2901,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Markus Staab
- Ryan Hendrickson
- Valentin
+ - Gerard
- Sören Bernstein
- michael.kubovic
- devel
@@ -2944,6 +2953,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Maxime Corteel (mcorteel)
- Dan Patrick (mdpatrick)
- Mathieu MARCHOIS (mmar)
+ - Nei Rauni Santos (nrauni)
- Geoffrey Monte (numerogeek)
- Martijn Boers (plebian)
- Plamen Mishev (pmishev)
@@ -3425,6 +3435,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Bastien THOMAS
- Shaun Simmons
- Pierre-Louis LAUNAY
+ - Arseny Razin
- A. Pauly
- djama
- Benjamin Rosenberger
@@ -3526,6 +3537,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Matthias Larisch
- Maxime P
- Sean Templeton
+ - Willem Mouwen
- db306
- Michaël VEROUX
- Julia
@@ -3621,6 +3633,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Jérémy (libertjeremy)
- Mehdi Achour (machour)
- Mamikon Arakelyan (mamikon)
+ - Mark Schmale (masch)
- Matt Ketmo (mattketmo)
- Moritz Borgmann (mborgmann)
- Matt Drollette (mdrollette)
diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineOpenTransactionLoggerMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineOpenTransactionLoggerMiddleware.php
index 2ef3bbbb92815..1efbdd47e163c 100644
--- a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineOpenTransactionLoggerMiddleware.php
+++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineOpenTransactionLoggerMiddleware.php
@@ -43,11 +43,12 @@ protected function handleForManager(EntityManagerInterface $entityManager, Envel
}
$this->isHandling = true;
+ $initialTransactionLevel = $entityManager->getConnection()->getTransactionNestingLevel();
try {
return $stack->next()->handle($envelope, $stack);
} finally {
- if ($entityManager->getConnection()->isTransactionActive()) {
+ if ($entityManager->getConnection()->getTransactionNestingLevel() > $initialTransactionLevel) {
$this->logger->error('A handler opened a transaction but did not close it.', [
'message' => $envelope->getMessage(),
]);
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineOpenTransactionLoggerMiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineOpenTransactionLoggerMiddlewareTest.php
index 3682ad00d5085..a1d4118deba3e 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineOpenTransactionLoggerMiddlewareTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineOpenTransactionLoggerMiddlewareTest.php
@@ -50,9 +50,9 @@ public function log($level, $message, $context = []): void
public function testMiddlewareWrapsInTransactionAndFlushes()
{
- $this->connection->expects($this->exactly(1))
- ->method('isTransactionActive')
- ->willReturn(true, true, false)
+ $this->connection->expects($this->exactly(2))
+ ->method('getTransactionNestingLevel')
+ ->willReturn(0, 1)
;
$this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock());
diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
index 2821d92e358f4..c2c0cbb8fc9e6 100644
--- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
+++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
@@ -410,7 +410,7 @@ private static function hasColorSupport()
}
// Follow https://no-color.org/
- if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
+ if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) {
return false;
}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/log_file.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/log_file.phpt
index 7f114ab5e2e5a..51f8d6cb1b21e 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/log_file.phpt
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/log_file.phpt
@@ -2,7 +2,7 @@
Test DeprecationErrorHandler with log file
--FILE--
%command.full_name%
-When the option --force is provided, secrets that already exist in the local vault are overriden.
+When the --force option is provided, secrets that already exist in the local vault are overridden.
%command.full_name% --force
EOF
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php
index eaee983b13488..b082c405298fc 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php
@@ -28,7 +28,7 @@ class AnnotationsCacheWarmerTest extends TestCase
protected function setUp(): void
{
- $this->cacheDir = sys_get_temp_dir().'/'.uniqid();
+ $this->cacheDir = sys_get_temp_dir().'/'.uniqid('', true);
$fs = new Filesystem();
$fs->mkdir($this->cacheDir);
parent::setUp();
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php
index 85975c62170e0..f44eefedf66ca 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php
@@ -36,7 +36,7 @@ class ConfigBuilderCacheWarmerTest extends TestCase
protected function setUp(): void
{
- $this->varDir = sys_get_temp_dir().'/'.uniqid();
+ $this->varDir = sys_get_temp_dir().'/'.uniqid('', true);
$fs = new Filesystem();
$fs->mkdir($this->varDir);
}
diff --git a/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php b/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php
index 650a0c71c1c2e..9373ff6c9217f 100644
--- a/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php
+++ b/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\SkippedTestSuiteError;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Traits\RedisTrait;
/**
@@ -133,4 +134,78 @@ public function testPconnectSelectsCorrectDatabase()
ini_set('redis.pconnect.connection_limit', $prevPoolSize);
}
}
+
+ /**
+ * @dataProvider provideDbIndexDsnParameter
+ */
+ public function testDbIndexDsnParameter(string $dsn, int $expectedDb)
+ {
+ if (!getenv('REDIS_AUTHENTICATED_HOST')) {
+ self::markTestSkipped('REDIS_AUTHENTICATED_HOST env var is not defined.');
+ }
+
+ $mock = new class () {
+ use RedisTrait;
+ };
+ $connection = $mock::createConnection($dsn);
+ self::assertSame($expectedDb, $connection->getDbNum());
+ }
+
+ public static function provideDbIndexDsnParameter(): array
+ {
+ return [
+ [
+ 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST'),
+ 0,
+ ],
+ [
+ 'redis:?host['.getenv('REDIS_HOST').']',
+ 0,
+ ],
+ [
+ 'redis:?host['.getenv('REDIS_HOST').']&dbindex=1',
+ 1,
+ ],
+ [
+ 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'?dbindex=2',
+ 2,
+ ],
+ [
+ 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/4',
+ 4,
+ ],
+ [
+ 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/?dbindex=5',
+ 5,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideInvalidDbIndexDsnParameter
+ */
+ public function testInvalidDbIndexDsnParameter(string $dsn)
+ {
+ if (!getenv('REDIS_AUTHENTICATED_HOST')) {
+ self::markTestSkipped('REDIS_AUTHENTICATED_HOST env var is not defined.');
+ }
+ $this->expectException(InvalidArgumentException::class);
+
+ $mock = new class () {
+ use RedisTrait;
+ };
+ $mock::createConnection($dsn);
+ }
+
+ public static function provideInvalidDbIndexDsnParameter(): array
+ {
+ return [
+ [
+ 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/abc'
+ ],
+ [
+ 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/3?dbindex=6'
+ ]
+ ];
+ }
}
diff --git a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
index 0455093c9b93c..ab7e7dd90fe64 100644
--- a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
+++ b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
@@ -114,8 +114,13 @@ private function write(string $file, string $data, ?int $expiresAt = null)
touch($this->tmp, $expiresAt ?: time() + 31556952); // 1 year in seconds
}
- $success = rename($this->tmp, $file);
- $unlink = !$success;
+ if ('\\' === \DIRECTORY_SEPARATOR) {
+ $success = copy($this->tmp, $file);
+ $unlink = true;
+ } else {
+ $success = rename($this->tmp, $file);
+ $unlink = !$success;
+ }
return $success;
} finally {
diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php
index 518a563060240..129254bd68c7a 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -152,10 +152,10 @@ public static function createConnection(string $dsn, array $options = [])
if (isset($params['host']) || isset($params['path'])) {
if (!isset($params['dbindex']) && isset($params['path'])) {
if (preg_match('#/(\d+)?$#', $params['path'], $m)) {
- $params['dbindex'] = $m[1] ?? '0';
+ $params['dbindex'] = $m[1] ?? $query['dbindex'] ?? '0';
$params['path'] = substr($params['path'], 0, -\strlen($m[0]));
} elseif (isset($params['host'])) {
- throw new InvalidArgumentException('Invalid Redis DSN: query parameter "dbindex" must be a number.');
+ throw new InvalidArgumentException('Invalid Redis DSN: parameter "dbindex" must be a number.');
}
}
@@ -170,6 +170,10 @@ public static function createConnection(string $dsn, array $options = [])
throw new InvalidArgumentException('Invalid Redis DSN: missing host.');
}
+ if (isset($params['dbindex'], $query['dbindex']) && $params['dbindex'] !== $query['dbindex']) {
+ throw new InvalidArgumentException('Invalid Redis DSN: path and query "dbindex" parameters mismatch.');
+ }
+
$params += $query + $options + self::$defaultConnectionOptions;
if (isset($params['redis_sentinel']) && !class_exists(\Predis\Client::class) && !class_exists(\RedisSentinel::class)) {
diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php
index 5f5ffce329b93..72479f8a2563a 100644
--- a/src/Symfony/Component/Console/Output/StreamOutput.php
+++ b/src/Symfony/Component/Console/Output/StreamOutput.php
@@ -91,7 +91,7 @@ protected function doWrite(string $message, bool $newline)
protected function hasColorSupport()
{
// Follow https://no-color.org/
- if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
+ if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) {
return false;
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php
index 8c4d841f5a1f8..8d27303ee0cc6 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php
@@ -133,6 +133,10 @@ public static function getDefault(ContainerBuilder $container, string $serviceId
return null;
}
+ if ($r->isInterface()) {
+ return null;
+ }
+
if (null !== $indexAttribute) {
$service = $class !== $serviceId ? sprintf('service "%s"', $serviceId) : 'on the corresponding service';
$message = [sprintf('Either method "%s::%s()" should ', $class, $defaultMethod), sprintf(' or tag "%s" on %s is missing attribute "%s".', $tagName, $service, $indexAttribute)];
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php
index 4d5ee1fb41b3d..c39d79f9e8772 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php
@@ -151,6 +151,8 @@ public function testTheIndexedTagsByDefaultIndexMethod()
$container->register('service3', IntTagClass::class)->addTag('my_custom_tag');
+ $container->register('service4', HelloInterface::class)->addTag('my_custom_tag');
+
$priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation();
$tag = new TaggedIteratorArgument('my_custom_tag', 'foo', 'getFooBar');
@@ -158,6 +160,7 @@ public function testTheIndexedTagsByDefaultIndexMethod()
'bar_tab_class_with_defaultmethod' => new TypedReference('service2', BarTagClass::class),
'service1' => new TypedReference('service1', FooTagClass::class),
'10' => new TypedReference('service3', IntTagClass::class),
+ 'service4' => new TypedReference('service4', HelloInterface::class),
];
$services = $priorityTaggedServiceTraitImplementation->test($tag, $container);
$this->assertSame(array_keys($expected), array_keys($services));
@@ -244,3 +247,8 @@ class HelloNamedService extends \stdClass
class HelloNamedService2
{
}
+
+interface HelloInterface
+{
+ public static function getFooBar(): string;
+}
diff --git a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php
index 72d0d5630ec9a..644126d6b2ba1 100644
--- a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php
+++ b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php
@@ -430,16 +430,16 @@ public function testHttpVarIsPartiallyOverridden()
$this->assertSame('http_value', $_SERVER['HTTP_TEST_ENV_VAR']);
}
- public function testEnvVarIsOverriden()
+ public function testEnvVarIsOverridden()
{
- putenv('TEST_ENV_VAR_OVERRIDEN=original_value');
+ putenv('TEST_ENV_VAR_OVERRIDDEN=original_value');
$dotenv = (new Dotenv())->usePutenv();
- $dotenv->populate(['TEST_ENV_VAR_OVERRIDEN' => 'new_value'], true);
+ $dotenv->populate(['TEST_ENV_VAR_OVERRIDDEN' => 'new_value'], true);
- $this->assertSame('new_value', getenv('TEST_ENV_VAR_OVERRIDEN'));
- $this->assertSame('new_value', $_ENV['TEST_ENV_VAR_OVERRIDEN']);
- $this->assertSame('new_value', $_SERVER['TEST_ENV_VAR_OVERRIDEN']);
+ $this->assertSame('new_value', getenv('TEST_ENV_VAR_OVERRIDDEN'));
+ $this->assertSame('new_value', $_ENV['TEST_ENV_VAR_OVERRIDDEN']);
+ $this->assertSame('new_value', $_SERVER['TEST_ENV_VAR_OVERRIDDEN']);
}
public function testMemorizingLoadedVarsNamesInSpecialVar()
diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
index b5109f457bc71..840353f327514 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
@@ -55,7 +55,6 @@ class ErrorHandler
\E_USER_DEPRECATED => 'User Deprecated',
\E_NOTICE => 'Notice',
\E_USER_NOTICE => 'User Notice',
- \E_STRICT => 'Runtime Notice',
\E_WARNING => 'Warning',
\E_USER_WARNING => 'User Warning',
\E_COMPILE_WARNING => 'Compile Warning',
@@ -73,7 +72,6 @@ class ErrorHandler
\E_USER_DEPRECATED => [null, LogLevel::INFO],
\E_NOTICE => [null, LogLevel::WARNING],
\E_USER_NOTICE => [null, LogLevel::WARNING],
- \E_STRICT => [null, LogLevel::WARNING],
\E_WARNING => [null, LogLevel::WARNING],
\E_USER_WARNING => [null, LogLevel::WARNING],
\E_COMPILE_WARNING => [null, LogLevel::WARNING],
@@ -183,6 +181,11 @@ public static function call(callable $function, ...$arguments)
public function __construct(?BufferingLogger $bootstrappingLogger = null, bool $debug = false)
{
+ if (\PHP_VERSION_ID < 80400) {
+ $this->levels[\E_STRICT] = 'Runtime Notice';
+ $this->loggers[\E_STRICT] = [null, LogLevel::WARNING];
+ }
+
if ($bootstrappingLogger) {
$this->bootstrappingLogger = $bootstrappingLogger;
$this->setDefaultLogger($bootstrappingLogger);
diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
index 05cbeec166b6e..0602ea4bdb1d6 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
@@ -72,7 +72,7 @@ public function render(\Throwable $exception): FlattenException
{
$headers = ['Content-Type' => 'text/html; charset='.$this->charset];
if (\is_bool($this->debug) ? $this->debug : ($this->debug)($exception)) {
- $headers['X-Debug-Exception'] = rawurlencode($exception->getMessage());
+ $headers['X-Debug-Exception'] = rawurlencode(substr($exception->getMessage(), 0, 2000));
$headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine();
}
diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php
index e6c4c898e19df..69ec52cc8ca62 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php
@@ -58,7 +58,7 @@ public function render(\Throwable $exception): FlattenException
$headers = ['Vary' => 'Accept'];
$debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($exception);
if ($debug) {
- $headers['X-Debug-Exception'] = rawurlencode($exception->getMessage());
+ $headers['X-Debug-Exception'] = rawurlencode(substr($exception->getMessage(), 0, 2000));
$headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine();
}
diff --git a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php
index 2b625f6388af3..e4294c802ae03 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php
@@ -213,7 +213,6 @@ public function testDefaultLogger()
\E_USER_DEPRECATED => [null, LogLevel::INFO],
\E_NOTICE => [$logger, LogLevel::WARNING],
\E_USER_NOTICE => [$logger, LogLevel::CRITICAL],
- \E_STRICT => [null, LogLevel::WARNING],
\E_WARNING => [null, LogLevel::WARNING],
\E_USER_WARNING => [null, LogLevel::WARNING],
\E_COMPILE_WARNING => [null, LogLevel::WARNING],
@@ -225,6 +224,11 @@ public function testDefaultLogger()
\E_ERROR => [null, LogLevel::CRITICAL],
\E_CORE_ERROR => [null, LogLevel::CRITICAL],
];
+
+ if (\PHP_VERSION_ID < 80400) {
+ $loggers[\E_STRICT] = [null, LogLevel::WARNING];
+ }
+
$this->assertSame($loggers, $handler->setLoggers([]));
} finally {
restore_error_handler();
@@ -478,7 +482,6 @@ public function testBootstrappingLogger()
\E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO],
\E_NOTICE => [$bootLogger, LogLevel::WARNING],
\E_USER_NOTICE => [$bootLogger, LogLevel::WARNING],
- \E_STRICT => [$bootLogger, LogLevel::WARNING],
\E_WARNING => [$bootLogger, LogLevel::WARNING],
\E_USER_WARNING => [$bootLogger, LogLevel::WARNING],
\E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING],
@@ -491,6 +494,10 @@ public function testBootstrappingLogger()
\E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL],
];
+ if (\PHP_VERSION_ID < 80400) {
+ $loggers[\E_STRICT] = [$bootLogger, LogLevel::WARNING];
+ }
+
$this->assertSame($loggers, $handler->setLoggers([]));
$handler->handleError(\E_DEPRECATED, 'Foo message', __FILE__, 123, []);
diff --git a/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php
index 3b3caa5e3f789..49144505f7883 100644
--- a/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php
+++ b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php
@@ -27,7 +27,7 @@ protected function setUp(): void
*/
public function testRewindOnFtp()
{
- $i = new RecursiveDirectoryIterator('ftp://speedtest:speedtest@ftp.otenet.gr/', \RecursiveDirectoryIterator::SKIP_DOTS);
+ $i = new RecursiveDirectoryIterator('ftp://test.rebex.net/', \RecursiveDirectoryIterator::SKIP_DOTS);
$i->rewind();
@@ -39,11 +39,11 @@ public function testRewindOnFtp()
*/
public function testSeekOnFtp()
{
- $i = new RecursiveDirectoryIterator('ftp://speedtest:speedtest@ftp.otenet.gr/', \RecursiveDirectoryIterator::SKIP_DOTS);
+ $i = new RecursiveDirectoryIterator('ftp://test.rebex.net/', \RecursiveDirectoryIterator::SKIP_DOTS);
$contains = [
- 'ftp://speedtest:speedtest@ftp.otenet.gr'.\DIRECTORY_SEPARATOR.'test100Mb.db',
- 'ftp://speedtest:speedtest@ftp.otenet.gr'.\DIRECTORY_SEPARATOR.'test100k.db',
+ 'ftp://test.rebex.net'.\DIRECTORY_SEPARATOR.'pub',
+ 'ftp://test.rebex.net'.\DIRECTORY_SEPARATOR.'readme.txt',
];
$actual = [];
diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
index ec43a83a075db..3ff0c9ac17c9c 100644
--- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
@@ -121,7 +121,7 @@ public function testOverridingInternalAttributesUsingCurlOptions()
$httpClient->request('POST', 'http://localhost:8057/', [
'extra' => [
'curl' => [
- \CURLOPT_PRIVATE => 'overriden private',
+ \CURLOPT_PRIVATE => 'overridden private',
],
],
]);
diff --git a/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php b/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php
index 0e62425b6b698..41ed55eda7822 100644
--- a/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php
@@ -37,6 +37,9 @@ public static function tearDownAfterClass(): void
TestHttpServer::stop();
}
+ /**
+ * @requires function ob_gzhandler
+ */
public function testSendRequest()
{
$client = new HttplugClient(new NativeHttpClient());
@@ -51,6 +54,9 @@ public function testSendRequest()
$this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']);
}
+ /**
+ * @requires function ob_gzhandler
+ */
public function testSendAsyncRequest()
{
$client = new HttplugClient(new NativeHttpClient());
diff --git a/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php b/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php
index d1f4deb03a006..65b7f5b3f6794 100644
--- a/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php
@@ -33,6 +33,9 @@ public static function tearDownAfterClass(): void
TestHttpServer::stop();
}
+ /**
+ * @requires function ob_gzhandler
+ */
public function testSendRequest()
{
$factory = new Psr17Factory();
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
index 77bb38f157c12..c6a28b1a4cfdc 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
@@ -226,14 +226,11 @@ public function getMetadataBag()
/**
* Generates a session ID.
*
- * This doesn't need to be particularly cryptographically secure since this is just
- * a mock.
- *
* @return string
*/
protected function generateId()
{
- return hash('sha256', uniqid('ss_mock_', true));
+ return bin2hex(random_bytes(16));
}
protected function loadSession()
diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
index 222b5f2987294..4599bd84b6ae9 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
@@ -368,7 +368,7 @@ public function testAcceptRangeOnUnsafeMethods()
$this->assertEquals('none', $response->headers->get('Accept-Ranges'));
}
- public function testAcceptRangeNotOverriden()
+ public function testAcceptRangeNotOverridden()
{
$request = Request::create('/', 'POST');
$response = new BinaryFileResponse(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
diff --git a/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
index b21e988a4a8b0..fc3f0964c5c69 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
@@ -98,4 +98,22 @@ public function testFilterArrayWithoutArrayFlagIsDeprecated()
$this->expectDeprecation('Since symfony/http-foundation 5.1: Filtering an array value with "Symfony\Component\HttpFoundation\InputBag::filter()" without passing the FILTER_REQUIRE_ARRAY or FILTER_FORCE_ARRAY flag is deprecated');
$bag->filter('foo', \FILTER_VALIDATE_INT);
}
+
+ public function testAdd()
+ {
+ $bag = new InputBag(['foo' => 'bar']);
+ $bag->add(['baz' => 'qux']);
+
+ $this->assertSame('bar', $bag->get('foo'), '->add() does not remove existing parameters');
+ $this->assertSame('qux', $bag->get('baz'), '->add() adds new parameters');
+ }
+
+ public function testReplace()
+ {
+ $bag = new InputBag(['foo' => 'bar']);
+ $bag->replace(['baz' => 'qux']);
+
+ $this->assertNull($bag->get('foo'), '->replace() removes existing parameters');
+ $this->assertSame('qux', $bag->get('baz'), '->replace() adds new parameters');
+ }
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RateLimiter/AbstractRequestRateLimiterTest.php b/src/Symfony/Component/HttpFoundation/Tests/RateLimiter/AbstractRequestRateLimiterTest.php
index 4e102777a45c6..26f2fac90801e 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RateLimiter/AbstractRequestRateLimiterTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RateLimiter/AbstractRequestRateLimiterTest.php
@@ -14,6 +14,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\RateLimiter\LimiterInterface;
+use Symfony\Component\RateLimiter\Policy\NoLimiter;
use Symfony\Component\RateLimiter\RateLimit;
class AbstractRequestRateLimiterTest extends TestCase
@@ -33,6 +34,34 @@ public function testConsume(array $rateLimits, ?RateLimit $expected)
$this->assertSame($expected, $rateLimiter->consume(new Request()));
}
+ public function testConsumeWithoutLimiterAddsSpecialNoLimiter()
+ {
+ $rateLimiter = new MockAbstractRequestRateLimiter([]);
+
+ try {
+ $this->assertSame(\PHP_INT_MAX, $rateLimiter->consume(new Request())->getLimit());
+ } catch (\TypeError $error) {
+ if (str_contains($error->getMessage(), 'RateLimit::__construct(): Argument #1 ($availableTokens) must be of type int, float given')) {
+ $this->markTestSkipped('This test cannot be run on a version of the RateLimiter component that uses \INF instead of \PHP_INT_MAX in NoLimiter.');
+ }
+
+ throw $error;
+ }
+ }
+
+ public function testResetLimiters()
+ {
+ $rateLimiter = new MockAbstractRequestRateLimiter([
+ $limiter1 = $this->createMock(LimiterInterface::class),
+ $limiter2 = $this->createMock(LimiterInterface::class),
+ ]);
+
+ $limiter1->expects($this->once())->method('reset');
+ $limiter2->expects($this->once())->method('reset');
+
+ $rateLimiter->reset(new Request());
+ }
+
public static function provideRateLimits()
{
$now = new \DateTimeImmutable();
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 9a612d805283f..b4a91bcea8695 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -78,11 +78,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static $freshCache = [];
- public const VERSION = '5.4.41';
- public const VERSION_ID = 50441;
+ public const VERSION = '5.4.42';
+ public const VERSION_ID = 50442;
public const MAJOR_VERSION = 5;
public const MINOR_VERSION = 4;
- public const RELEASE_VERSION = 41;
+ public const RELEASE_VERSION = 42;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '11/2024';
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php
index e22950ddd913a..2b536337712d0 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php
@@ -204,7 +204,7 @@ public function testRequestNoLocaleFromAcceptLanguageHeader()
$this->assertEquals('de', $request->getLocale());
}
- public function testRequestAttributeLocaleNotOverridenFromAcceptLanguageHeader()
+ public function testRequestAttributeLocaleNotOverriddenFromAcceptLanguageHeader()
{
$request = Request::create('/');
$request->attributes->set('_locale', 'it');
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
index 7b3cc8159257b..9e7171a83587d 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
@@ -539,7 +539,7 @@ public function testUninitializedSessionWithoutInitializedSession()
$this->assertSame('60', $response->headers->getCacheControlDirective('s-maxage'));
}
- public function testResponseHeadersMaxAgeAndExpiresNotBeOverridenIfSessionStarted()
+ public function testResponseHeadersMaxAgeAndExpiresNotBeOverriddenIfSessionStarted()
{
$session = $this->createMock(Session::class);
$session->expects($this->exactly(2))->method('getUsageIndex')->willReturn(0, 1);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
index e95f212bb75de..e10cb79593a52 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
@@ -61,6 +61,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$endpoint = sprintf('%s/v3/%s/messages', $this->getEndpoint(), urlencode($this->domain));
$response = $this->client->request('POST', 'https://'.$endpoint, [
+ 'http_version' => '1.1',
'auth_basic' => 'api:'.$this->key,
'headers' => $headers,
'body' => $body->bodyToIterable(),
diff --git a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php
index e87aaeff8a4fe..ca4791d8d6a3a 100644
--- a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php
+++ b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageForRetryListener.php
@@ -77,7 +77,7 @@ public function onMessageFailed(WorkerMessageFailedEvent $event)
$retryEnvelope = $this->withLimitedHistory($envelope, new DelayStamp($delay), new RedeliveryStamp($retryCount));
// re-send the message for retry
- $this->getSenderForTransport($event->getReceiverName())->send($retryEnvelope);
+ $retryEnvelope = $this->getSenderForTransport($event->getReceiverName())->send($retryEnvelope);
if (null !== $this->eventDispatcher) {
$this->eventDispatcher->dispatch(new WorkerMessageRetriedEvent($retryEnvelope, $event->getReceiverName()));
diff --git a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php
index a5fe10e85578b..0519a19fb6619 100644
--- a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php
+++ b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageForRetryListenerTest.php
@@ -13,13 +13,16 @@
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
+use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
+use Symfony\Component\Messenger\Event\WorkerMessageRetriedEvent;
use Symfony\Component\Messenger\EventListener\SendFailedMessageForRetryListener;
use Symfony\Component\Messenger\Exception\RecoverableMessageHandlingException;
use Symfony\Component\Messenger\Retry\RetryStrategyInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
+use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
use Symfony\Component\Messenger\Transport\Sender\SenderInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
@@ -190,4 +193,47 @@ public function testEnvelopeKeepOnlyTheLast10Stamps()
$listener->onMessageFailed($event);
}
+
+ public function testRetriedEnvelopePassesToRetriedEvent()
+ {
+ $exception = new \Exception('no!');
+ $envelope = new Envelope(new \stdClass());
+
+ $sender = $this->createMock(SenderInterface::class);
+ $sender->expects($this->once())->method('send')->willReturnCallback(static function (Envelope $envelope) {
+ return $envelope->with(new TransportMessageIdStamp(123));
+ });
+
+ $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
+ $eventDispatcher->expects($this->once())->method('dispatch')->willReturnCallback(
+ function (WorkerMessageRetriedEvent $retriedEvent) {
+ $envelope = $retriedEvent->getEnvelope();
+
+ $transportIdStamp = $envelope->last(TransportMessageIdStamp::class);
+ $this->assertNotNull($transportIdStamp);
+
+ return $retriedEvent;
+ });
+
+ $senderLocator = new Container();
+ $senderLocator->set('my_receiver', $sender);
+
+ $retryStrategy = $this->createMock(RetryStrategyInterface::class);
+ $retryStrategy->expects($this->once())->method('isRetryable')->willReturn(true);
+ $retryStrategy->expects($this->once())->method('getWaitingTime')->willReturn(1000);
+
+ $retryStrategyLocator = new Container();
+ $retryStrategyLocator->set('my_receiver', $retryStrategy);
+
+ $listener = new SendFailedMessageForRetryListener(
+ $senderLocator,
+ $retryStrategyLocator,
+ null,
+ $eventDispatcher
+ );
+
+ $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', $exception);
+
+ $listener->onMessageFailed($event);
+ }
}
diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php b/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php
index c7578774e6cc1..2b5042512162c 100644
--- a/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php
+++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php
@@ -64,7 +64,7 @@ protected function doSend(MessageInterface $message): SentMessage
'phone' => $message->getPhone(),
'text' => $message->getSubject(),
'sender' => $this->from,
- 'clientId' => uniqid(),
+ 'clientId' => uniqid('', true),
],
],
'login' => $this->login,
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index a4f2cc9f5e028..5119f28e2cfe0 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -458,7 +458,7 @@ private function extractFromMutator(string $class, string $property): ?array
$type = $this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass());
if (1 === \count($type) && \in_array($prefix, $this->arrayMutatorPrefixes)) {
- $type = [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type[0])];
+ $type = [new Type(Type::BUILTIN_TYPE_ARRAY, $this->isNullableProperty($class, $property), null, true, new Type(Type::BUILTIN_TYPE_INT), $type[0])];
}
return $type;
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
index 57869e40819bb..9719f0bf24bf6 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
@@ -17,6 +17,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
+use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyCollection;
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\PseudoTypeDummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsedInTrait;
@@ -160,6 +161,7 @@ public static function typesProvider()
null,
],
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)], null, null],
+ ['collectionAsObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyCollection::class, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING)])], null, null],
];
}
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php
index 8786785774878..d3c3f4d366cf7 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php
@@ -17,6 +17,7 @@
use Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummyWithoutDocBlock;
use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
+use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyCollection;
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\RootDummy\RootDummyItem;
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\AnotherNamespace\DummyInAnotherNamespace;
@@ -130,6 +131,7 @@ public static function typesProvider()
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)]],
['rootDummyItems', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, RootDummyItem::class))]],
['rootDummyItem', [new Type(Type::BUILTIN_TYPE_OBJECT, false, RootDummyItem::class)]],
+ ['collectionAsObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyCollection::class, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING)])]],
];
}
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
index 06e8bc53f87b5..0fdab63361f5e 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
@@ -53,6 +53,7 @@ public function testGetProperties()
'bal',
'parent',
'collection',
+ 'collectionAsObject',
'nestedCollection',
'mixedCollection',
'B',
@@ -118,6 +119,7 @@ public function testGetPropertiesWithCustomPrefixes()
'bal',
'parent',
'collection',
+ 'collectionAsObject',
'nestedCollection',
'mixedCollection',
'B',
@@ -172,6 +174,7 @@ public function testGetPropertiesWithNoPrefixes()
'bal',
'parent',
'collection',
+ 'collectionAsObject',
'nestedCollection',
'mixedCollection',
'B',
@@ -520,6 +523,7 @@ public function testTypedProperties()
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))], $this->extractor->getTypes(Php74Dummy::class, 'stringCollection'));
$this->assertEquals([new Type(Type::BUILTIN_TYPE_INT, true)], $this->extractor->getTypes(Php74Dummy::class, 'nullableWithDefault'));
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)], $this->extractor->getTypes(Php74Dummy::class, 'collection'));
+ $this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class))], $this->extractor->getTypes(Php74Dummy::class, 'nullableTypedCollection'));
}
/**
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
index cf0c791784695..ee07eb703aac2 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
@@ -46,6 +46,11 @@ class Dummy extends ParentDummy
*/
public $collection;
+ /**
+ * @var DummyCollection
+ */
+ public $collectionAsObject;
+
/**
* @var string[][]
*/
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyCollection.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyCollection.php
new file mode 100644
index 0000000000000..e8799d8f6be07
--- /dev/null
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyCollection.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
+
+final class DummyCollection implements \IteratorAggregate
+{
+ public function getIterator(): \Traversable
+ {
+ return [];
+ }
+}
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php
index 816b857b67b11..dc72d07756b88 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php74Dummy.php
@@ -23,6 +23,9 @@ class Php74Dummy
private ?int $nullableWithDefault = 1;
public array $collection = [];
+ /** @var Dummy[]|null */
+ public ?array $nullableTypedCollection = null;
+
public function addStringCollection(string $string): void
{
}
@@ -30,4 +33,8 @@ public function addStringCollection(string $string): void
public function removeStringCollection(string $string): void
{
}
+
+ public function addNullableTypedCollection(Dummy $dummy): void
+ {
+ }
}
diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
index 6171530abadc7..dff891aa571db 100644
--- a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
+++ b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
@@ -121,7 +121,7 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
return [$mainType];
}
- $collection = $mainType->isCollection() || \in_array($mainType->getClassName(), [\Traversable::class, \Iterator::class, \IteratorAggregate::class, \ArrayAccess::class, \Generator::class], true);
+ $collection = $mainType->isCollection() || \is_a($mainType->getClassName(), \Traversable::class, true) || \is_a($mainType->getClassName(), \ArrayAccess::class, true);
// it's safer to fall back to other extractors if the generic type is too abstract
if (!$collection && !class_exists($mainType->getClassName())) {
diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php
index 48ec1018056f6..e3d38c4919501 100644
--- a/src/Symfony/Component/Routing/Router.php
+++ b/src/Symfony/Component/Routing/Router.php
@@ -294,6 +294,7 @@ function (ConfigCacheInterface $cache) {
}
$cache->write($dumper->dump(), $this->getRouteCollection()->getResources());
+ unset(self::$cache[$cache->getPath()]);
}
);
@@ -325,6 +326,7 @@ function (ConfigCacheInterface $cache) {
$dumper = $this->getGeneratorDumperInstance();
$cache->write($dumper->dump(), $this->getRouteCollection()->getResources());
+ unset(self::$cache[$cache->getPath()]);
}
);
diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/CompiledUrlMatcherDumperTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/CompiledUrlMatcherDumperTest.php
index 97c067196eb03..dad9dd98c416e 100644
--- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/CompiledUrlMatcherDumperTest.php
+++ b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/CompiledUrlMatcherDumperTest.php
@@ -33,7 +33,7 @@ protected function setUp(): void
{
parent::setUp();
- $this->dumpPath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_matcher.'.uniqid('CompiledUrlMatcher').'.php';
+ $this->dumpPath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_matcher.'.uniqid('CompiledUrlMatcher', true).'.php';
}
protected function tearDown(): void
diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf
index d90e830ff18f4..f75eb12c005eb 100644
--- a/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf
+++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ar.xlf
@@ -64,7 +64,7 @@
Too many failed login attempts, please try again later.
- عدد كبير جدا من محاولات الدخول الفاشلة، يرجى المحاولة مرة أخرى في وقت لاحق.
+ العديد من محاولات الدخول الفاشلة، يرجى المحاولة مرة أخرى في وقت لاحق.Invalid or expired login link.
@@ -72,11 +72,11 @@
Too many failed login attempts, please try again in %minutes% minute.
- عدد كبير جدا من محاولات الدخول الفاشلة، يرجى اعادة المحاولة بعد %minutes% دقيقة.
+ العديد من محاولات الدخول الفاشلة، يرجى اعادة المحاولة بعد %minutes% دقيقة.Too many failed login attempts, please try again in %minutes% minutes.
- عدد محاولات تسجيل الدخول الفاشلة كثيرة، الرجاء المحاولة مرة أخرى بعد %minutes% دقيقة.|عدد محاولات تسجيل الدخول الفاشلة كثيرة، الرجاء المحاولة مرة أخرى بعد %minutes% دقائق.
+ العديد من محاولات الدخول الفاشلة ، يرجى اعادة المحاولة بعد %minutes% دقائق.
An authentication exception occurred.
- Ocorreu uma excepção durante a autenticação.
+ Ocorreu uma exceção durante a autenticação.Authentication credentials could not be found.
@@ -12,7 +12,7 @@
Authentication request could not be processed due to a system problem.
- O pedido de autenticação não foi concluído devido a um problema no sistema.
+ A autenticação não foi concluída devido a um problema no sistema.Invalid credentials.
@@ -24,7 +24,7 @@
Not privileged to request the resource.
- Não possui privilégios para aceder a este recurso.
+ Sem privilégios para solicitar este recurso.Invalid CSRF token.
@@ -36,7 +36,7 @@
No session available, it either timed out or cookies are not enabled.
- Não existe sessão disponível, esta expirou ou os cookies estão desativados.
+ Nenhuma sessão disponível, esta expirou ou os cookies estão desativados.No token could be found.
@@ -44,7 +44,7 @@
Username could not be found.
- Nome de utilizador não encontrado.
+ Nome de usuário não encontrado.Account has expired.
@@ -60,11 +60,11 @@
Account is locked.
- A conta está trancada.
+ A conta está bloqueada.Too many failed login attempts, please try again later.
- Várias tentativas de login falhadas, por favor tente mais tarde.
+ Muitas tentativas de login sem sucesso, por favor, tente mais tarde.Invalid or expired login link.
@@ -72,11 +72,11 @@
Too many failed login attempts, please try again in %minutes% minute.
- Demasiadas tentativas de login, tente novamente num minuto.
+ Muitas tentativas de login sem sucesso, por favor, tente novamente novamente em 1 minuto.Too many failed login attempts, please try again in %minutes% minutes.
- Muitas tentativas de login sem sucesso, por favor tente novamente em %minutes% minutos.
+ Muitas tentativas de login sem sucesso, por favor, tente novamente em %minutes% minutos.