diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 5f2d77a453eaf..d4dafb2aa0029 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
| Q | A
| ------------- | ---
-| Branch? | 7.3 for features / 6.4, and 7.2 for bug fixes
+| Branch? | 7.4 for features / 6.4, 7.2, or 7.3 for bug fixes
| Bug fix? | yes/no
| New feature? | yes/no
| Deprecations? | yes/no
diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff
index 30ac60ab98ad7..d838ce9f7c759 100644
--- a/.github/expected-missing-return-types.diff
+++ b/.github/expected-missing-return-types.diff
@@ -177,6 +177,23 @@ diff --git a/src/Symfony/Component/DependencyInjection/Extension/PrependExtensio
- public function prepend(ContainerBuilder $container);
+ public function prepend(ContainerBuilder $container): void;
}
+diff --git a/src/Symfony/Component/Emoji/EmojiTransliterator.php b/src/Symfony/Component/Emoji/EmojiTransliterator.php
+--- a/src/Symfony/Component/Emoji/EmojiTransliterator.php
++++ b/src/Symfony/Component/Emoji/EmojiTransliterator.php
+@@ -88,5 +88,5 @@ final class EmojiTransliterator extends \Transliterator
+ */
+ #[\ReturnTypeWillChange]
+- public function getErrorCode(): int|false
++ public function getErrorCode(): int
+ {
+ return isset($this->transliterator) ? $this->transliterator->getErrorCode() : 0;
+@@ -97,5 +97,5 @@ final class EmojiTransliterator extends \Transliterator
+ */
+ #[\ReturnTypeWillChange]
+- public function getErrorMessage(): string|false
++ public function getErrorMessage(): string
+ {
+ return isset($this->transliterator) ? $this->transliterator->getErrorMessage() : '';
diff --git a/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php
--- a/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php
+++ b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 40da4746f4fbe..677e6e6a30d91 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -6,7 +6,7 @@ on:
schedule:
- cron: '34 4 * * 6'
push:
- branches: [ "7.3" ]
+ branches: [ "7.4" ]
# Declare default permissions as read only.
permissions: read-all
diff --git a/CHANGELOG-7.2.md b/CHANGELOG-7.2.md
index 93c489ae487bd..d6d188669de42 100644
--- a/CHANGELOG-7.2.md
+++ b/CHANGELOG-7.2.md
@@ -7,6 +7,32 @@ in 7.2 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/v7.2.0...v7.2.1
+* 7.2.7 (2025-05-29)
+
+ * bug #60549 [Translation] Add intl-icu fallback for MessageCatalogue metadata (pontus-mp)
+ * bug #60571 [ErrorHandler] Do not transform file to link if it does not exist (lyrixx)
+ * bug #60494 [Messenger] fix: Add argument as integer (overexpOG)
+ * bug #60524 [Notifier] Fix Clicksend transport (BafS)
+ * bug #60478 [Validator] add missing `$extensions` and `$extensionsMessage` to the `Image` constraint (xabbuh)
+ * bug #60484 [PhpUnitBridge] Clean up mocked features only when ``@group`` is present (HypeMC)
+ * bug #60490 [PhpUnitBridge] set path to the PHPUnit autoload file (xabbuh)
+ * bug #60423 [DependencyInjection] Make `DefinitionErrorExceptionPass` consider `IGNORE_ON_UNINITIALIZED_REFERENCE` and `RUNTIME_EXCEPTION_ON_INVALID_REFERENCE` the same (MatTheCat)
+ * bug #60439 [FrameworkBundle] Fix declaring field-attr tags in xml config files (nicolas-grekas)
+ * bug #60428 [DependencyInjection] Fix missing binding for ServiceCollectionInterface when declaring a service subscriber (nicolas-grekas)
+ * bug #60421 [VarExporter] Fixed lazy-loading ghost objects generation with property hooks (cheack)
+ * bug #60266 [Security] Exclude remember_me from default login authenticators (santysisi)
+ * bug #60400 [Config] Fix generated comment for multiline "info" (GromNaN)
+ * bug #60260 [Serializer] Prevent `Cannot traverse an already closed generator` error by materializing Traversable input (santysisi)
+ * bug #60292 [HttpFoundation] Encode path in `X-Accel-Redirect` header (Athorcis)
+ * bug #58643 [SecurityBundle] Use Composer `InstalledVersions` to check if flex is installed (andyexeter)
+ * bug #60275 [DoctrineBridge] Fix UniqueEntityValidator Stringable identifiers (GiuseppeArcuti, wkania)
+ * bug #60293 [Messenger] fix asking users to select an option if `--force` option is used in `messenger:failed:retry` command (W0rma)
+ * bug #60379 [Security] Avoid failing when PersistentRememberMeHandler handles a malformed cookie (Seldaek)
+ * bug #60373 [FrameworkBundle] Ensure `Email` class exists before using it (Kocal)
+ * bug #60365 [FrameworkBundle] ensure that all supported e-mail validation modes can be configured (xabbuh)
+ * bug #60350 [Security][LoginLink] Throw `InvalidLoginLinkException` on invalid parameters (davidszkiba)
+ * bug #60340 [String] fix EmojiTransliterator return type compatibility with PHP 8.5 (xabbuh)
+
* 7.2.6 (2025-05-02)
* bug #60288 [VarExporter] dump default value for property hooks if present (xabbuh)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index ffc3b6feae6fd..ee2cb2a40889b 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -38,8 +38,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Samuel ROZE (sroze)
- Pascal Borreli (pborreli)
- Romain Neutron
- - Joseph Bielawski (stloyd)
- Kevin Bond (kbond)
+ - Joseph Bielawski (stloyd)
- Drak (drak)
- Abdellatif Ait boudad (aitboudad)
- Lukas Kahwe Smith (lsmith)
@@ -79,8 +79,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Iltar van der Berg
- Miha Vrhovnik (mvrhov)
- Gary PEGEOT (gary-p)
- - Saša Stamenković (umpirsky)
- Alexander Schranz (alexander-schranz)
+ - Saša Stamenković (umpirsky)
- Allison Guilhem (a_guilhem)
- Mathieu Piot (mpiot)
- Vasilij Duško (staff)
@@ -94,8 +94,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Vladimir Reznichenko (kalessil)
- Peter Rehm (rpet)
- Henrik Bjørnskov (henrikbjorn)
- - David Buchmann (dbu)
- Ruud Kamphuis (ruudk)
+ - David Buchmann (dbu)
- Andrej Hudec (pulzarraider)
- Tomas Norkūnas (norkunas)
- Jáchym Toušek (enumag)
@@ -111,14 +111,14 @@ The Symfony Connect username in parenthesis allows to get more information
- Frank A. Fiebig (fafiebig)
- Baldini
- Fran Moreno (franmomu)
+ - Antoine Makdessi (amakdessi)
- Charles Sarrazin (csarrazi)
- Henrik Westphal (snc)
- Dariusz Górecki (canni)
- - Antoine Makdessi (amakdessi)
- Ener-Getick
- Graham Campbell (graham)
- - Massimiliano Arione (garak)
- Joel Wurtz (brouznouf)
+ - Massimiliano Arione (garak)
- Tugdual Saunier (tucksaun)
- Lee McDermott
- Brandon Turner
@@ -175,6 +175,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Dāvis Zālītis (k0d3r1s)
- Gordon Franke (gimler)
- Malte Schlüter (maltemaltesich)
+ - soyuka
- jeremyFreeAgent (jeremyfreeagent)
- Michael Babker (mbabker)
- Alexis Lefebvre
@@ -195,7 +196,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Niels Keurentjes (curry684)
- OGAWA Katsuhiro (fivestar)
- Jhonny Lidfors (jhonne)
- - soyuka
- Juti Noppornpitak (shiroyuki)
- Gregor Harlan (gharlan)
- Anthony MARTIN
@@ -277,6 +277,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Sébastien Alfaiate (seb33300)
- James Halsall (jaitsu)
- Christian Scheb
+ - Alex Hofbauer (alexhofbauer)
- Mikael Pajunen
- Warnar Boekkooi (boekkooi)
- Justin Hileman (bobthecow)
@@ -285,6 +286,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Clément JOBEILI (dator)
- Andreas Möller (localheinz)
- Marek Štípek (maryo)
+ - matlec
- Daniel Espendiller
- Arnaud PETITPAS (apetitpa)
- Michael Käfer (michael_kaefer)
@@ -302,6 +304,7 @@ The Symfony Connect username in parenthesis allows to get more information
- DQNEO
- Chi-teck
- Marko Kaznovac (kaznovac)
+ - Stiven Llupa (sllupa)
- Andre Rømcke (andrerom)
- Bram Leeda (bram123)
- Patrick Landolt (scube)
@@ -327,8 +330,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Stadly
- Stepan Anchugov (kix)
- bronze1man
- - matlec
- sun (sun)
+ - Filippo Tessarotto (slamdunk)
- Larry Garfield (crell)
- Leo Feyer
- Nikolay Labinskiy (e-moe)
@@ -337,10 +340,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Guilliam Xavier
- Pierre Minnieur (pminnieur)
- Dominique Bongiraud
- - Stiven Llupa (sllupa)
- Hugo Monteiro (monteiro)
- Dmitrii Poddubnyi (karser)
- Julien Pauli
+ - Jonathan H. Wage
- Michael Lee (zerustech)
- Florian Lonqueu-Brochard (florianlb)
- Joe Bennett (kralos)
@@ -364,11 +367,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Arjen van der Meijden
- Sven Paulus (subsven)
- Peter Kruithof (pkruithof)
- - Alex Hofbauer (alexhofbauer)
- Maxime Veber (nek-)
- Valentine Boineau (valentineboineau)
- Rui Marinho (ruimarinho)
- - Filippo Tessarotto (slamdunk)
- Jeroen Noten (jeroennoten)
- Possum
- Jérémie Augustin (jaugustin)
@@ -386,7 +387,6 @@ The Symfony Connect username in parenthesis allows to get more information
- dFayet
- Rob Frawley 2nd (robfrawley)
- Renan (renanbr)
- - Jonathan H. Wage
- Nikita Konstantinov (unkind)
- Dariusz
- Daniel Gorgan
@@ -395,6 +395,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Daniel Tschinder
- Christian Schmidt
- Alexander Kotynia (olden)
+ - Matthieu Lempereur (mryamous)
- Elnur Abdurrakhimov (elnur)
- Manuel Reinhard (sprain)
- Zan Baldwin (zanbaldwin)
@@ -426,6 +427,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Sullivan SENECHAL (soullivaneuh)
- Uwe Jäger (uwej711)
- javaDeveloperKid
+ - Chris Smith (cs278)
- W0rma
- Lynn van der Berg (kjarli)
- Michaël Perrin (michael.perrin)
@@ -461,7 +463,6 @@ The Symfony Connect username in parenthesis allows to get more information
- renanbr
- Sébastien Lavoie (lavoiesl)
- Alex Rock (pierstoval)
- - Matthieu Lempereur (mryamous)
- Wodor Wodorski
- Beau Simensen (simensen)
- Magnus Nordlander (magnusnordlander)
@@ -489,9 +490,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Bohan Yang (brentybh)
- Vilius Grigaliūnas
- Jordane VASPARD (elementaire)
- - Chris Smith (cs278)
- Thomas Bisignani (toma)
- Florian Klein (docteurklein)
+ - Pierre Ambroise (dotordu)
- Raphaël Geffroy (raphael-geffroy)
- Damien Alexandre (damienalexandre)
- Manuel Kießling (manuelkiessling)
@@ -542,6 +543,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ahmed Raafat
- Philippe Segatori
- Thibaut Cheymol (tcheymol)
+ - Vincent Chalamon
- Raffaele Carelle
- Erin Millard
- Matthew Lewinski (lewinski)
@@ -583,6 +585,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Daniel STANCU
- Kristen Gilden
- Robbert Klarenbeek (robbertkl)
+ - Dalibor Karlović
- Hamza Makraz (makraz)
- Eric Masoero (eric-masoero)
- Vitalii Ekert (comrade42)
@@ -635,7 +638,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Ivan Sarastov (isarastov)
- flack (flack)
- Shein Alexey
- - Pierre Ambroise (dotordu)
- Joe Lencioni
- Daniel Tschinder
- Diego Agulló (aeoris)
@@ -658,6 +660,7 @@ The Symfony Connect username in parenthesis allows to get more information
- a.dmitryuk
- Anthon Pang (robocoder)
- Julien Galenski (ruian)
+ - Benjamin Morel
- Ben Scott (bpscott)
- Shyim
- Pablo Lozano (arkadis)
@@ -697,7 +700,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Neil Peyssard (nepey)
- Niklas Fiekas
- Mark Challoner (markchalloner)
- - Vincent Chalamon
- Andreas Hennings
- Markus Bachmann (baachi)
- Gunnstein Lye (glye)
@@ -713,6 +715,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ivan Nikolaev (destillat)
- Gildas Quéméner (gquemener)
- Ioan Ovidiu Enache (ionutenache)
+ - Mokhtar Tlili (sf-djuba)
- Maxim Dovydenok (dovydenok-maxim)
- Laurent Masforné (heisenberg)
- Claude Khedhiri (ck-developer)
@@ -762,7 +765,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Tristan Pouliquen
- Miro Michalicka
- Hans Mackowiak
- - Dalibor Karlović
- M. Vondano
- Dominik Zogg
- Maximilian Zumbansen
@@ -936,7 +938,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Forfarle (forfarle)
- Johnny Robeson (johnny)
- Disquedur
- - Benjamin Morel
- Guilherme Ferreira
- Geoffrey Tran (geoff)
- Jannik Zschiesche
@@ -1003,6 +1004,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Alexandre Dupuy (satchette)
- Michel Hunziker
- Malte Blättermann
+ - Ilya Levin (ilyachase)
- Simeon Kolev (simeon_kolev9)
- Joost van Driel (j92)
- Jonas Elfering
@@ -1101,6 +1103,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Kevin SCHNEKENBURGER
- Geordie
- Fabien Salles (blacked)
+ - Tim Düsterhus
- Andreas Erhard (andaris)
- alexpozzi
- Michael Devery (mickadoo)
@@ -1112,6 +1115,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Luca Saba (lucasaba)
- Sascha Grossenbacher (berdir)
- Guillaume Aveline
+ - nathanpage
- Robin Lehrmann
- Szijarto Tamas
- Thomas P
@@ -1491,7 +1495,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Johnson Page (jwpage)
- Kuba Werłos (kuba)
- Ruben Gonzalez (rubenruateltek)
- - Mokhtar Tlili (sf-djuba)
- Michael Roterman (wtfzdotnet)
- Philipp Keck
- Pavol Tuka
@@ -1507,6 +1510,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Dominik Ulrich
- den
- Gábor Tóth
+ - Bastien THOMAS
- ouardisoft
- Daniel Cestari
- Matt Janssen
@@ -1672,13 +1676,13 @@ The Symfony Connect username in parenthesis allows to get more information
- Chris de Kok
- Eduard Bulava (nonanerz)
- Andreas Kleemann (andesk)
- - Ilya Levin (ilyachase)
- Hubert Moreau (hmoreau)
- Nicolas Appriou
- Silas Joisten (silasjoisten)
- Igor Timoshenko (igor.timoshenko)
- Pierre-Emmanuel CAPEL
- Manuele Menozzi
+ - Yevhen Sidelnyk
- “teerasak”
- Anton Babenko (antonbabenko)
- Irmantas Šiupšinskas (irmantas)
@@ -1707,6 +1711,7 @@ The Symfony Connect username in parenthesis allows to get more information
- hamza
- dantleech
- Kajetan Kołtuniak (kajtii)
+ - Dan (dantleech)
- Sander Goossens (sandergo90)
- Rudy Onfroy
- Tero Alén (tero)
@@ -1964,6 +1969,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Peter Trebaticky
- Moza Bogdan (bogdan_moza)
- Viacheslav Sychov
+ - Zuruuh
- Nicolas Sauveur (baishu)
- Helmut Hummel (helhum)
- Matt Brunt
@@ -2015,6 +2021,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Rémi Leclerc
- Jan Vernarsky
- Ionut Cioflan
+ - John Edmerson Pizarra
- Sergio
- Jonas Hünig
- Mehrdad
@@ -2367,6 +2374,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Tom Corrigan (tomcorrigan)
- Luis Galeas
- Bogdan Scordaliu
+ - Sven Scholz
- Martin Pärtel
- Daniel Rotter (danrot)
- Frédéric Bouchery (fbouchery)
@@ -2572,6 +2580,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Benhssaein Youssef
- Benoit Leveque
- bill moll
+ - chillbram
- Benjamin Bender
- PaoRuby
- Holger Lösken
@@ -2866,7 +2875,6 @@ The Symfony Connect username in parenthesis allows to get more information
- fabi
- Grayson Koonce
- Ruben Jansen
- - nathanpage
- Wissame MEKHILEF
- Mihai Stancu
- shreypuranik
@@ -3161,6 +3169,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ashura
- Götz Gottwald
- Alessandra Lai
+ - timesince
- alangvazq
- Christoph Krapp
- Ernest Hymel
@@ -3197,7 +3206,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Buster Neece
- Albert Prat
- Alessandro Loffredo
- - Tim Düsterhus
- Ian Phillips
- Carlos Tasada
- Remi Collet
@@ -3357,11 +3365,14 @@ The Symfony Connect username in parenthesis allows to get more information
- Pavel Barton
- Exploit.cz
- GuillaumeVerdon
+ - Dmitry Danilson
- Marien Fressinaud
- ureimers
- akimsko
- Youpie
- Jason Stephens
+ - Korvin Szanto
+ - wkania
- srsbiz
- Taylan Kasap
- Michael Orlitzky
@@ -3392,6 +3403,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Evgeniy Koval
- Lars Moelleken
- dasmfm
+ - Karel Syrový
- Claas Augner
- Mathias Geat
- neodevcode
@@ -3445,6 +3457,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Sylvain Lorinet
- Pavol Tuka
- klyk50
+ - Colin Michoudet
- jc
- BenjaminBeck
- Aurelijus Rožėnas
@@ -3474,6 +3487,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Philipp
- lol768
- jamogon
+ - Tom Hart
- Vyacheslav Slinko
- Benjamin Laugueux
- guangwu
@@ -3580,7 +3594,6 @@ The Symfony Connect username in parenthesis allows to get more information
- andrey-tech
- David Ronchaud
- Chris McGehee
- - Bastien THOMAS
- Shaun Simmons
- Pierre-Louis LAUNAY
- Arseny Razin
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UserUuidNameDto.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UserUuidNameDto.php
new file mode 100644
index 0000000000000..8c2c60d21ba85
--- /dev/null
+++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UserUuidNameDto.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Doctrine\Tests\Fixtures;
+
+use Symfony\Component\Uid\Uuid;
+
+class UserUuidNameDto
+{
+ public function __construct(
+ public ?Uuid $id,
+ public ?string $fullName,
+ public ?string $address,
+ ) {
+ }
+}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UserUuidNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UserUuidNameEntity.php
new file mode 100644
index 0000000000000..3ac3ead8d201a
--- /dev/null
+++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UserUuidNameEntity.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Doctrine\Tests\Fixtures;
+
+use Doctrine\ORM\Mapping\Column;
+use Doctrine\ORM\Mapping\Entity;
+use Doctrine\ORM\Mapping\Id;
+use Symfony\Component\Uid\Uuid;
+
+#[Entity]
+class UserUuidNameEntity
+{
+ public function __construct(
+ #[Id, Column]
+ public ?Uuid $id = null,
+ #[Column(unique: true)]
+ public ?string $fullName = null,
+ ) {
+ }
+}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php
index 93e9818f4383c..6619f911ae1e0 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php
@@ -41,7 +41,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?str
throw new ConversionException(sprintf('Expected "%s", got "%s"', 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\Foo', get_debug_type($value)));
}
- return $foo->bar;
+ return $value->bar;
}
public function convertToPHPValue($value, AbstractPlatform $platform): ?Foo
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php
index a985eaae7b2dc..4d7a9b1f78f77 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php
@@ -41,9 +41,12 @@
use Symfony\Bridge\Doctrine\Tests\Fixtures\UpdateCompositeIntIdEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\UpdateCompositeObjectNoToStringIdEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\UpdateEmployeeProfile;
+use Symfony\Bridge\Doctrine\Tests\Fixtures\UserUuidNameDto;
+use Symfony\Bridge\Doctrine\Tests\Fixtures\UserUuidNameEntity;
use Symfony\Bridge\Doctrine\Tests\TestRepositoryFactory;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator;
+use Symfony\Component\Uid\Uuid;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
@@ -116,6 +119,7 @@ private function createSchema($em)
$em->getClassMetadata(Employee::class),
$em->getClassMetadata(CompositeObjectNoToStringIdEntity::class),
$em->getClassMetadata(SingleIntIdStringWrapperNameEntity::class),
+ $em->getClassMetadata(UserUuidNameEntity::class),
]);
}
@@ -1401,4 +1405,25 @@ public function testEntityManagerNullObjectWhenDTODoctrineStyle()
$this->validator->validate($dto, $constraint);
}
+
+ public function testUuidIdentifierWithSameValueDifferentInstanceDoesNotCauseViolation()
+ {
+ $uuidString = 'ec562e21-1fc8-4e55-8de7-a42389ac75c5';
+ $existingPerson = new UserUuidNameEntity(Uuid::fromString($uuidString), 'Foo Bar');
+ $this->em->persist($existingPerson);
+ $this->em->flush();
+
+ $dto = new UserUuidNameDto(Uuid::fromString($uuidString), 'Foo Bar', '');
+
+ $constraint = new UniqueEntity(
+ fields: ['fullName'],
+ entityClass: UserUuidNameEntity::class,
+ identifierFieldNames: ['id'],
+ em: self::EM_NAME,
+ );
+
+ $this->validator->validate($dto, $constraint);
+
+ $this->assertNoViolation();
+ }
}
diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
index 87eebbca142c6..4aed1cd3a44c2 100644
--- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
+++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
@@ -197,6 +197,12 @@ public function validate(mixed $value, Constraint $constraint): void
foreach ($constraint->identifierFieldNames as $identifierFieldName) {
$propertyValue = $this->getPropertyValue($entityClass, $identifierFieldName, current($result));
+ if ($fieldValues[$identifierFieldName] instanceof \Stringable) {
+ $fieldValues[$identifierFieldName] = (string) $fieldValues[$identifierFieldName];
+ }
+ if ($propertyValue instanceof \Stringable) {
+ $propertyValue = (string) $propertyValue;
+ }
if ($fieldValues[$identifierFieldName] !== $propertyValue) {
$entityMatched = false;
break;
diff --git a/src/Symfony/Bridge/PhpUnit/SymfonyExtension.php b/src/Symfony/Bridge/PhpUnit/SymfonyExtension.php
index 3a429c1493780..c6a5a58e871a0 100644
--- a/src/Symfony/Bridge/PhpUnit/SymfonyExtension.php
+++ b/src/Symfony/Bridge/PhpUnit/SymfonyExtension.php
@@ -11,6 +11,8 @@
namespace Symfony\Bridge\PhpUnit;
+use PHPUnit\Event\Code\Test;
+use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\Test\BeforeTestMethodErrored;
use PHPUnit\Event\Test\BeforeTestMethodErroredSubscriber;
use PHPUnit\Event\Test\Errored;
@@ -19,6 +21,7 @@
use PHPUnit\Event\Test\FinishedSubscriber;
use PHPUnit\Event\Test\Skipped;
use PHPUnit\Event\Test\SkippedSubscriber;
+use PHPUnit\Metadata\Group;
use PHPUnit\Runner\Extension\Extension;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
@@ -47,22 +50,22 @@ public function bootstrap(Configuration $configuration, Facade $facade, Paramete
$facade->registerSubscriber(new class implements ErroredSubscriber {
public function notify(Errored $event): void
{
- SymfonyExtension::disableClockMock();
- SymfonyExtension::disableDnsMock();
+ SymfonyExtension::disableClockMock($event->test());
+ SymfonyExtension::disableDnsMock($event->test());
}
});
$facade->registerSubscriber(new class implements FinishedSubscriber {
public function notify(Finished $event): void
{
- SymfonyExtension::disableClockMock();
- SymfonyExtension::disableDnsMock();
+ SymfonyExtension::disableClockMock($event->test());
+ SymfonyExtension::disableDnsMock($event->test());
}
});
$facade->registerSubscriber(new class implements SkippedSubscriber {
public function notify(Skipped $event): void
{
- SymfonyExtension::disableClockMock();
- SymfonyExtension::disableDnsMock();
+ SymfonyExtension::disableClockMock($event->test());
+ SymfonyExtension::disableDnsMock($event->test());
}
});
@@ -70,8 +73,13 @@ public function notify(Skipped $event): void
$facade->registerSubscriber(new class implements BeforeTestMethodErroredSubscriber {
public function notify(BeforeTestMethodErrored $event): void
{
- SymfonyExtension::disableClockMock();
- SymfonyExtension::disableDnsMock();
+ if (method_exists($event, 'test')) {
+ SymfonyExtension::disableClockMock($event->test());
+ SymfonyExtension::disableDnsMock($event->test());
+ } else {
+ ClockMock::withClockMock(false);
+ DnsMock::withMockedHosts([]);
+ }
}
});
}
@@ -88,16 +96,38 @@ public function notify(BeforeTestMethodErrored $event): void
/**
* @internal
*/
- public static function disableClockMock(): void
+ public static function disableClockMock(Test $test): void
{
- ClockMock::withClockMock(false);
+ if (self::hasGroup($test, 'time-sensitive')) {
+ ClockMock::withClockMock(false);
+ }
}
/**
* @internal
*/
- public static function disableDnsMock(): void
+ public static function disableDnsMock(Test $test): void
{
- DnsMock::withMockedHosts([]);
+ if (self::hasGroup($test, 'dns-sensitive')) {
+ DnsMock::withMockedHosts([]);
+ }
+ }
+
+ /**
+ * @internal
+ */
+ public static function hasGroup(Test $test, string $groupName): bool
+ {
+ if (!$test instanceof TestMethod) {
+ return false;
+ }
+
+ foreach ($test->metadata() as $metadata) {
+ if ($metadata instanceof Group && $groupName === $metadata->groupName()) {
+ return true;
+ }
+ }
+
+ return false;
}
}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/SymfonyExtensionWithManualRegister.php b/src/Symfony/Bridge/PhpUnit/Tests/SymfonyExtensionWithManualRegister.php
new file mode 100644
index 0000000000000..c02d6f1cf64ce
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/SymfonyExtensionWithManualRegister.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ClockMock;
+use Symfony\Bridge\PhpUnit\DnsMock;
+
+class SymfonyExtensionWithManualRegister extends TestCase
+{
+ public static function setUpBeforeClass(): void
+ {
+ ClockMock::register(self::class);
+ ClockMock::withClockMock(strtotime('2024-05-20 15:30:00'));
+
+ DnsMock::register(self::class);
+ DnsMock::withMockedHosts([
+ 'example.com' => [
+ ['type' => 'A', 'ip' => '1.2.3.4'],
+ ],
+ ]);
+ }
+
+ public static function tearDownAfterClass(): void
+ {
+ ClockMock::withClockMock(false);
+ DnsMock::withMockedHosts([]);
+ }
+
+ public function testDate()
+ {
+ self::assertSame('2024-05-20 15:30:00', date('Y-m-d H:i:s'));
+ }
+
+ public function testGetHostByName()
+ {
+ self::assertSame('1.2.3.4', gethostbyname('example.com'));
+ }
+
+ public function testTime()
+ {
+ self::assertSame(1716219000, time());
+ }
+
+ public function testDnsGetRecord()
+ {
+ self::assertSame([[
+ 'host' => 'example.com',
+ 'class' => 'IN',
+ 'ttl' => 1,
+ 'type' => 'A',
+ 'ip' => '1.2.3.4',
+ ]], dns_get_record('example.com'));
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/symfonyextension.phpt b/src/Symfony/Bridge/PhpUnit/Tests/symfonyextension.phpt
index 2c808c2f5930e..02b0a510e6301 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/symfonyextension.phpt
+++ b/src/Symfony/Bridge/PhpUnit/Tests/symfonyextension.phpt
@@ -5,6 +5,8 @@ if (!getenv('SYMFONY_PHPUNIT_VERSION') || version_compare(getenv('SYMFONY_PHPUNI
--FILE--
=')) {
+ $GLOBALS['_composer_autoload_path'] = "$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/vendor/autoload.php";
+}
+
if ($components) {
$skippedTests = $_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS'] ?? false;
$runningProcs = [];
diff --git a/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php
index 9eeb305aee36c..b06f0a8cedbe4 100644
--- a/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php
+++ b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php
@@ -44,7 +44,7 @@ public function getFunctions(): array
/**
* Adds a "Link" HTTP header.
*
- * @param string $rel The relation type (e.g. "preload", "prefetch", "prerender" or "dns-prefetch")
+ * @param string $rel The relation type (e.g. "preload", "prefetch", or "dns-prefetch")
* @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]")
*
* @return string The relation URI
@@ -115,7 +115,11 @@ public function prefetch(string $uri, array $attributes = []): string
}
/**
- * Indicates to the client that it should prerender this resource .
+ * Indicates to the client that it should prerender this resource.
+ *
+ * This feature is deprecated and superseded by the Speculation Rules API.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/prerender
*
* @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]")
*
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 4d494eed09e60..5154393f2769e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -46,6 +46,7 @@
use Symfony\Component\Translation\Translator;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\Uid\Factory\UuidFactory;
+use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Webhook\Controller\WebhookController;
use Symfony\Component\WebLink\HttpHeaderSerializer;
@@ -251,6 +252,7 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI
->arrayNode('field_attr')
->performNoDeepMerging()
->normalizeKeys(false)
+ ->useAttributeAsKey('name')
->scalarPrototype()->end()
->defaultValue(['data-controller' => 'csrf-protection'])
->end()
@@ -1018,7 +1020,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e
->validate()->castToArray()->end()
->end()
->scalarNode('translation_domain')->defaultValue('validators')->end()
- ->enumNode('email_validation_mode')->values(['html5', 'loose', 'strict'])->defaultValue('html5')->end()
+ ->enumNode('email_validation_mode')->values((class_exists(Email::class) ? Email::VALIDATION_MODES : ['html5-allow-no-tld', 'html5', 'strict']) + ['loose'])->defaultValue('html5')->end()
->arrayNode('mapping')
->addDefaultsIfNotSet()
->fixXmlConfig('path')
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 8d64adeca341d..3c4e4f9f3a5eb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -731,7 +731,7 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu
$container->getDefinition('config_cache_factory')->setArguments([]);
}
- if (!$config['disallow_search_engine_index'] ?? false) {
+ if (!$config['disallow_search_engine_index']) {
$container->removeDefinition('disallow_search_engine_index_response_listener');
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer.php
index 7a3a95739b0f2..f1dc560ab76f8 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer.php
@@ -45,7 +45,6 @@
tagged_iterator('mailer.transport_factory'),
])
- ->set('mailer.default_transport', TransportInterface::class)
->alias('mailer.default_transport', 'mailer.transports')
->alias(TransportInterface::class, 'mailer.default_transport')
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index 491cd1e4ffb7c..e99022acf7c45 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -79,7 +79,7 @@
-
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_csrf_field_attr.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_csrf_field_attr.php
new file mode 100644
index 0000000000000..103ee4797a1b8
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_csrf_field_attr.php
@@ -0,0 +1,22 @@
+loadFromExtension('framework', [
+ 'annotations' => false,
+ 'http_method_override' => false,
+ 'handle_all_throwables' => true,
+ 'php_errors' => ['log' => true],
+ 'csrf_protection' => [
+ 'enabled' => true,
+ ],
+ 'form' => [
+ 'csrf_protection' => [
+ 'field-attr' => [
+ 'data-foo' => 'bar',
+ 'data-bar' => 'baz',
+ ],
+ ],
+ ],
+ 'session' => [
+ 'storage_factory_id' => 'session.storage.factory.native',
+ ],
+]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_field_attr.xml
similarity index 67%
rename from src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml
rename to src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_field_attr.xml
index 4a05e9d33294e..1889703bec2a9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_field_attr.xml
@@ -9,7 +9,13 @@
-
+
+
+
+ bar
+ baz
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml
deleted file mode 100644
index 09ef0ee167eb4..0000000000000
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_csrf_field_attr.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_csrf_field_attr.yml
new file mode 100644
index 0000000000000..db519977548c4
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_csrf_field_attr.yml
@@ -0,0 +1,16 @@
+framework:
+ annotations: false
+ http_method_override: false
+ handle_all_throwables: true
+ php_errors:
+ log: true
+ csrf_protection:
+ enabled: true
+ form:
+ csrf_protection:
+ enabled: true
+ field_attr:
+ data-foo: bar
+ data-bar: baz
+ session:
+ storage_factory_id: session.storage.factory.native
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
index 7bf66512d2b2b..655f9180eceb2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
@@ -1413,6 +1413,17 @@ public function testFormsCanBeEnabledWithoutCsrfProtection()
$this->assertFalse($container->getParameter('form.type_extension.csrf.enabled'));
}
+ public function testFormCsrfFieldAttr()
+ {
+ $container = $this->createContainerFromFile('form_csrf_field_attr');
+
+ $expected = [
+ 'data-foo' => 'bar',
+ 'data-bar' => 'baz',
+ ];
+ $this->assertSame($expected, $container->getParameter('form.type_extension.csrf.field_attr'));
+ }
+
public function testStopwatchEnabledWithDebugModeEnabled()
{
$container = $this->createContainerFromFile('default_config', [
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php
index deac159b6f9b0..ad979a01a96b3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php
@@ -17,6 +17,7 @@
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
+use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Workflow\Exception\InvalidDefinitionException;
class PhpFrameworkExtensionTest extends FrameworkExtensionTestCase
@@ -265,4 +266,31 @@ public function testRateLimiterIsTagged()
$this->assertSame('first', $container->getDefinition('limiter.first')->getTag('rate_limiter')[0]['name']);
$this->assertSame('second', $container->getDefinition('limiter.second')->getTag('rate_limiter')[0]['name']);
}
+
+ /**
+ * @dataProvider emailValidationModeProvider
+ */
+ public function testValidatorEmailValidationMode(string $mode)
+ {
+ $this->expectNotToPerformAssertions();
+
+ $this->createContainerFromClosure(function (ContainerBuilder $container) use ($mode) {
+ $container->loadFromExtension('framework', [
+ 'annotations' => false,
+ 'http_method_override' => false,
+ 'handle_all_throwables' => true,
+ 'php_errors' => ['log' => true],
+ 'validation' => [
+ 'email_validation_mode' => $mode,
+ ],
+ ]);
+ });
+ }
+
+ public static function emailValidationModeProvider()
+ {
+ foreach (Email::VALIDATION_MODES as $mode) {
+ yield [$mode];
+ }
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index f454b9318c183..14e7e45a1dc5c 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -11,6 +11,7 @@
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
+use Composer\InstalledVersions;
use Symfony\Bridge\Twig\Extension\LogoutUrlExtension;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FirewallListenerFactoryInterface;
@@ -61,7 +62,6 @@
use Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticatorManagerListener;
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
-use Symfony\Flex\Command\InstallRecipesCommand;
/**
* SecurityExtension.
@@ -92,7 +92,7 @@ public function prepend(ContainerBuilder $container): void
public function load(array $configs, ContainerBuilder $container): void
{
if (!array_filter($configs)) {
- $hint = class_exists(InstallRecipesCommand::class) ? 'Try running "composer symfony:recipes:install symfony/security-bundle".' : 'Please define your settings for the "security" config section.';
+ $hint = class_exists(InstalledVersions::class) && InstalledVersions::isInstalled('symfony/flex') ? 'Try running "composer symfony:recipes:install symfony/security-bundle".' : 'Please define your settings for the "security" config section.';
throw new InvalidConfigurationException('The SecurityBundle is enabled but is not configured. '.$hint);
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Security.php b/src/Symfony/Bundle/SecurityBundle/Security.php
index 915f766f5175b..252fce011b101 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security.php
@@ -158,8 +158,7 @@ private function getAuthenticator(?string $authenticatorName, string $firewallNa
$firewallAuthenticatorLocator = $this->authenticators[$firewallName];
if (!$authenticatorName) {
- $authenticatorIds = array_keys($firewallAuthenticatorLocator->getProvidedServices());
-
+ $authenticatorIds = array_filter(array_keys($firewallAuthenticatorLocator->getProvidedServices()), fn (string $authenticatorId) => $authenticatorId !== \sprintf('security.authenticator.remember_me.%s', $firewallName));
if (!$authenticatorIds) {
throw new LogicException(\sprintf('No authenticator was found for the firewall "%s".', $firewallName));
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php
index d4b336b4eaa70..82a444ef10358 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php
@@ -152,7 +152,10 @@ public function testLogin()
$firewallAuthenticatorLocator
->expects($this->once())
->method('getProvidedServices')
- ->willReturn(['security.authenticator.custom.dev' => $authenticator])
+ ->willReturn([
+ 'security.authenticator.custom.dev' => $authenticator,
+ 'security.authenticator.remember_me.main' => $authenticator
+ ])
;
$firewallAuthenticatorLocator
->expects($this->once())
@@ -252,6 +255,49 @@ public function testLoginWithoutRequestContext()
$security->login($user);
}
+ public function testLoginFailsWhenTooManyAuthenticatorsFound()
+ {
+ $request = new Request();
+ $authenticator = $this->createMock(AuthenticatorInterface::class);
+ $requestStack = $this->createMock(RequestStack::class);
+ $firewallMap = $this->createMock(FirewallMap::class);
+ $firewall = new FirewallConfig('main', 'main');
+ $userAuthenticator = $this->createMock(UserAuthenticatorInterface::class);
+ $user = $this->createMock(UserInterface::class);
+ $userChecker = $this->createMock(UserCheckerInterface::class);
+
+ $container = $this->createMock(ContainerInterface::class);
+ $container
+ ->expects($this->atLeastOnce())
+ ->method('get')
+ ->willReturnMap([
+ ['request_stack', $requestStack],
+ ['security.firewall.map', $firewallMap],
+ ['security.authenticator.managers_locator', $this->createContainer('main', $userAuthenticator)],
+ ['security.user_checker_locator', $this->createContainer('main', $userChecker)],
+ ])
+ ;
+
+ $requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request);
+ $firewallMap->expects($this->once())->method('getFirewallConfig')->willReturn($firewall);
+
+ $firewallAuthenticatorLocator = $this->createMock(ServiceProviderInterface::class);
+ $firewallAuthenticatorLocator
+ ->expects($this->once())
+ ->method('getProvidedServices')
+ ->willReturn([
+ 'security.authenticator.custom.main' => $authenticator,
+ 'security.authenticator.other.main' => $authenticator
+ ])
+ ;
+
+ $security = new Security($container, ['main' => $firewallAuthenticatorLocator]);
+
+ $this->expectException(\LogicException::class);
+ $this->expectExceptionMessage('Too many authenticators were found for the current firewall "main". You must provide an instance of "Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface" to login programmatically. The available authenticators for the firewall "main" are "security.authenticator.custom.main" ,"security.authenticator.other.main');
+ $security->login($user);
+ }
+
public function testLogout()
{
$request = new Request();
diff --git a/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php b/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php
index 08e91c2d11105..ddd6dbb291051 100644
--- a/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php
+++ b/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php
@@ -412,39 +412,39 @@ private function getComment(BaseNode $node): string
{
$comment = '';
if ('' !== $info = (string) $node->getInfo()) {
- $comment .= ' * '.$info."\n";
+ $comment .= $info."\n";
}
if (!$node instanceof ArrayNode) {
foreach ((array) ($node->getExample() ?? []) as $example) {
- $comment .= ' * @example '.$example."\n";
+ $comment .= '@example '.$example."\n";
}
if ('' !== $default = $node->getDefaultValue()) {
- $comment .= ' * @default '.(null === $default ? 'null' : var_export($default, true))."\n";
+ $comment .= '@default '.(null === $default ? 'null' : var_export($default, true))."\n";
}
if ($node instanceof EnumNode) {
- $comment .= \sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_unique(array_map(fn ($a) => !$a instanceof \UnitEnum ? var_export($a, true) : '\\'.ltrim(var_export($a, true), '\\'), $node->getValues()))))."\n";
+ $comment .= \sprintf('@param ParamConfigurator|%s $value', implode('|', array_unique(array_map(fn ($a) => !$a instanceof \UnitEnum ? var_export($a, true) : '\\'.ltrim(var_export($a, true), '\\'), $node->getValues()))))."\n";
} else {
$parameterTypes = $this->getParameterTypes($node);
- $comment .= ' * @param ParamConfigurator|'.implode('|', $parameterTypes).' $value'."\n";
+ $comment .= '@param ParamConfigurator|'.implode('|', $parameterTypes).' $value'."\n";
}
} else {
foreach ((array) ($node->getExample() ?? []) as $example) {
- $comment .= ' * @example '.json_encode($example)."\n";
+ $comment .= '@example '.json_encode($example)."\n";
}
if ($node->hasDefaultValue() && [] != $default = $node->getDefaultValue()) {
- $comment .= ' * @default '.json_encode($default)."\n";
+ $comment .= '@default '.json_encode($default)."\n";
}
}
if ($node->isDeprecated()) {
- $comment .= ' * @deprecated '.$node->getDeprecation($node->getName(), $node->getParent()->getName())['message']."\n";
+ $comment .= '@deprecated '.$node->getDeprecation($node->getName(), $node->getParent()->getName())['message']."\n";
}
- return $comment;
+ return $comment ? ' * '.str_replace("\n", "\n * ", rtrim($comment, "\n"))."\n" : '';
}
/**
diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues.php
index 153af57be9b5b..5c1259c20edd8 100644
--- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues.php
+++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues.php
@@ -38,7 +38,13 @@ public function getConfigTreeBuilder(): TreeBuilder
->arrayPrototype()
->fixXmlConfig('option')
->children()
- ->scalarNode('dsn')->end()
+ ->scalarNode('dsn')
+ ->info(<<<'INFO'
+ The DSN to use. This is a required option.
+ The info is used to describe the DSN,
+ it can be multi-line.
+ INFO)
+ ->end()
->scalarNode('serializer')->defaultNull()->end()
->arrayNode('options')
->normalizeKeys(false)
diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues/Symfony/Config/NodeInitialValues/Messenger/TransportsConfig.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues/Symfony/Config/NodeInitialValues/Messenger/TransportsConfig.php
index b9d8b48db3556..6a98166eccc94 100644
--- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues/Symfony/Config/NodeInitialValues/Messenger/TransportsConfig.php
+++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/NodeInitialValues/Symfony/Config/NodeInitialValues/Messenger/TransportsConfig.php
@@ -16,6 +16,9 @@ class TransportsConfig
private $_usedProperties = [];
/**
+ * The DSN to use. This is a required option.
+ * The info is used to describe the DSN,
+ * it can be multi-line.
* @default null
* @param ParamConfigurator|mixed $value
* @return $this
diff --git a/src/Symfony/Component/Console/Tests/SignalRegistry/SignalMapTest.php b/src/Symfony/Component/Console/Tests/SignalRegistry/SignalMapTest.php
index f4e320477d4be..3a0c49bb01e21 100644
--- a/src/Symfony/Component/Console/Tests/SignalRegistry/SignalMapTest.php
+++ b/src/Symfony/Component/Console/Tests/SignalRegistry/SignalMapTest.php
@@ -18,15 +18,13 @@ class SignalMapTest extends TestCase
{
/**
* @requires extension pcntl
- *
- * @testWith [2, "SIGINT"]
- * [9, "SIGKILL"]
- * [15, "SIGTERM"]
- * [31, "SIGSYS"]
*/
- public function testSignalExists(int $signal, string $expected)
+ public function testSignalExists()
{
- $this->assertSame($expected, SignalMap::getSignalName($signal));
+ $this->assertSame('SIGINT', SignalMap::getSignalName(\SIGINT));
+ $this->assertSame('SIGKILL', SignalMap::getSignalName(\SIGKILL));
+ $this->assertSame('SIGTERM', SignalMap::getSignalName(\SIGTERM));
+ $this->assertSame('SIGSYS', SignalMap::getSignalName(\SIGSYS));
}
public function testSignalDoesNotExist()
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php
index 26ab135b1a99c..2d6ad689443e8 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php
@@ -62,7 +62,10 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
}
if ($value instanceof Reference && $this->currentId !== $targetId = (string) $value) {
- if (ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
+ if (
+ ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()
+ || ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
+ ) {
$this->sourceReferences[$targetId][$this->currentId] ??= true;
} else {
$this->sourceReferences[$targetId][$this->currentId] = false;
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
index 87470c39894e4..89b822bc53b44 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php
@@ -20,6 +20,7 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Contracts\Service\Attribute\SubscribedService;
+use Symfony\Contracts\Service\ServiceCollectionInterface;
use Symfony\Contracts\Service\ServiceProviderInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
@@ -134,6 +135,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
$value->setBindings([
PsrContainerInterface::class => new BoundArgument($locatorRef, false),
ServiceProviderInterface::class => new BoundArgument($locatorRef, false),
+ ServiceCollectionInterface::class => new BoundArgument($locatorRef, false),
] + $value->getBindings());
return parent::processValue($value);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php
index 9ab5c27fcf763..5ed7be315114a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php
@@ -64,6 +64,9 @@ public function testSkipNestedErrors()
$container->register('foo', 'stdClass')
->addArgument(new Reference('bar', ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE));
+ $container->register('baz', 'stdClass')
+ ->addArgument(new Reference('bar', ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE));
+
$pass = new DefinitionErrorExceptionPass();
$pass->process($container);
diff --git a/src/Symfony/Component/Emoji/EmojiTransliterator.php b/src/Symfony/Component/Emoji/EmojiTransliterator.php
index 03fe1bd61a7d6..67dfe3fb3f56c 100644
--- a/src/Symfony/Component/Emoji/EmojiTransliterator.php
+++ b/src/Symfony/Component/Emoji/EmojiTransliterator.php
@@ -83,14 +83,22 @@ public function createInverse(): self
return self::create($this->id, \Transliterator::REVERSE);
}
+ /**
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
public function getErrorCode(): int|false
{
return isset($this->transliterator) ? $this->transliterator->getErrorCode() : 0;
}
+ /**
+ * @return string
+ */
+ #[\ReturnTypeWillChange]
public function getErrorMessage(): string|false
{
- return isset($this->transliterator) ? $this->transliterator->getErrorMessage() : false;
+ return isset($this->transliterator) ? $this->transliterator->getErrorMessage() : '';
}
public static function listIDs(): array
diff --git a/src/Symfony/Component/Emoji/Tests/EmojiTransliteratorTest.php b/src/Symfony/Component/Emoji/Tests/EmojiTransliteratorTest.php
index 0bdbf51ed36d5..1dacfe9917742 100644
--- a/src/Symfony/Component/Emoji/Tests/EmojiTransliteratorTest.php
+++ b/src/Symfony/Component/Emoji/Tests/EmojiTransliteratorTest.php
@@ -206,6 +206,6 @@ public function testGetErrorMessageWithUninitializedTransliterator()
{
$transliterator = EmojiTransliterator::create('emoji-en');
- $this->assertFalse($transliterator->getErrorMessage());
+ $this->assertSame('', $transliterator->getErrorMessage());
}
}
diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
index d0d1246a1ddbd..7bd9a083e53a4 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
@@ -233,6 +233,10 @@ private function formatFile(string $file, int $line, ?string $text = null): stri
$text .= ' at line '.$line;
}
+ if (!file_exists($file)) {
+ return $text;
+ }
+
$link = $this->fileLinkFormat->format($file, $line);
return \sprintf('%s', $this->escape($link), $text);
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/WeekType.php b/src/Symfony/Component/Form/Extension/Core/Type/WeekType.php
index aea4a6c564889..c4827604e5f0c 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/WeekType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/WeekType.php
@@ -39,7 +39,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
} else {
$yearOptions = $weekOptions = [
'error_bubbling' => true,
- 'empty_data' => '',
];
// when the form is compound the entries of the array are ignored in favor of children data
// so we need to handle the cascade setting here
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php b/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php
index 44725a69c71a5..2704ee5303ad2 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php
@@ -34,7 +34,7 @@ public function addType(FormTypeInterface $type)
public function getType($name): FormTypeInterface
{
- return $this->types[$name] ?? null;
+ return $this->types[$name];
}
public function hasType($name): bool
diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
index 2c4d831bb9afa..a7358183bd461 100644
--- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
+++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
@@ -237,7 +237,7 @@ public function prepare(Request $request): static
$path = $location.substr($path, \strlen($pathPrefix));
// Only set X-Accel-Redirect header if a valid URI can be produced
// as nginx does not serve arbitrary file paths.
- $this->headers->set($type, $path);
+ $this->headers->set($type, rawurlencode($path));
$this->maxlen = 0;
break;
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
index 1263fa39298ff..7627cd5ec492a 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
@@ -314,7 +314,15 @@ public function testXAccelMapping($realpath, $mapping, $virtual)
$property->setValue($response, $file);
$response->prepare($request);
- $this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect'));
+ $header = $response->headers->get('X-Accel-Redirect');
+
+ if ($virtual) {
+ // Making sure the path doesn't contain characters unsupported by nginx
+ $this->assertMatchesRegularExpression('/^([^?%]|%[0-9A-F]{2})*$/', $header);
+ $header = rawurldecode($header);
+ }
+
+ $this->assertEquals($virtual, $header);
}
public function testDeleteFileAfterSend()
@@ -361,6 +369,7 @@ public static function getSampleXAccelMappings()
['/home/Foo/bar.txt', '/var/www/=/files/,/home/Foo/=/baz/', '/baz/bar.txt'],
['/home/Foo/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', '/baz/bar.txt'],
['/tmp/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', null],
+ ['/var/www/var/www/files/foo%.txt', '/var/www/=/files/', '/files/var/www/files/foo%.txt'],
];
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index d5a41390e1b5d..9031fc34f52f9 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -589,7 +589,6 @@ public function testGetUri()
$server['REDIRECT_QUERY_STRING'] = 'query=string';
$server['REDIRECT_URL'] = '/path/info';
- $server['SCRIPT_NAME'] = '/index.php';
$server['QUERY_STRING'] = 'query=string';
$server['REQUEST_URI'] = '/path/info?toto=test&1=1';
$server['SCRIPT_NAME'] = '/index.php';
@@ -716,7 +715,6 @@ public function testGetUriForPath()
$server['REDIRECT_QUERY_STRING'] = 'query=string';
$server['REDIRECT_URL'] = '/path/info';
- $server['SCRIPT_NAME'] = '/index.php';
$server['QUERY_STRING'] = 'query=string';
$server['REQUEST_URI'] = '/path/info?toto=test&1=1';
$server['SCRIPT_NAME'] = '/index.php';
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 12f65d3a89c15..09b362c1ff72c 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static array $freshCache = [];
- public const VERSION = '7.2.6';
- public const VERSION_ID = 70206;
+ public const VERSION = '7.2.7';
+ public const VERSION_ID = 70207;
public const MAJOR_VERSION = 7;
public const MINOR_VERSION = 2;
- public const RELEASE_VERSION = 6;
+ public const RELEASE_VERSION = 7;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '07/2025';
diff --git a/src/Symfony/Component/Mailer/Bridge/Azure/README.md b/src/Symfony/Component/Mailer/Bridge/Azure/README.md
index acd9cc25abb53..36b81fccfa385 100644
--- a/src/Symfony/Component/Mailer/Bridge/Azure/README.md
+++ b/src/Symfony/Component/Mailer/Bridge/Azure/README.md
@@ -21,8 +21,8 @@ where:
Resources
---------
- * [Microsoft Azure (ACS) Email API Docs](https://learn.microsoft.com/en-us/rest/api/communication/dataplane/email/send)
+ * [Microsoft Azure (ACS) Email API Docs](https://learn.microsoft.com/en-us/rest/api/communication/email/email/send)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
- in the [main Symfony repository](https://github.com/symfony/symfony)
\ No newline at end of file
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
index 65650cedd72e2..0a6197810d4a2 100644
--- a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
+++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
@@ -31,6 +31,7 @@ class Connection
'x-max-length-bytes',
'x-max-priority',
'x-message-ttl',
+ 'x-delivery-limit',
];
/**
diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php
index 47bcd1463a915..15dbe84a37da3 100644
--- a/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php
+++ b/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php
@@ -224,8 +224,8 @@ private function runWorker(string $failureTransportName, ReceiverInterface $rece
$this->forceExit = true;
try {
- $choice = $io->choice('Please select an action', ['retry', 'delete', 'skip'], 'retry');
- $shouldHandle = $shouldForce || 'retry' === $choice;
+ $choice = $shouldForce ? 'retry' : $io->choice('Please select an action', ['retry', 'delete', 'skip'], 'retry');
+ $shouldHandle = 'retry' === $choice;
} finally {
$this->forceExit = false;
}
diff --git a/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php b/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php
index 8d8987e4f7c7a..67af3ac9237a7 100644
--- a/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php
+++ b/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php
@@ -87,7 +87,7 @@ protected function doSend(MessageInterface $message): SentMessage
$response = $this->client->request('POST', $endpoint, [
'auth_basic' => [$this->apiUsername, $this->apiKey],
- 'json' => array_filter($options),
+ 'json' => ['messages' => [array_filter($options)]],
]);
try {
diff --git a/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php b/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php
index 6afae4409fa57..e1f9fa37dcae0 100644
--- a/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php
+++ b/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php
@@ -63,10 +63,14 @@ public function testNoInvalidArgumentExceptionIsThrownIfFromIsValid(string $from
$response = $this->createMock(ResponseInterface::class);
$response->expects(self::exactly(2))->method('getStatusCode')->willReturn(200);
$response->expects(self::once())->method('getContent')->willReturn('');
- $client = new MockHttpClient(function (string $method, string $url) use ($response): ResponseInterface {
+ $client = new MockHttpClient(function (string $method, string $url, array $options) use ($response): ResponseInterface {
self::assertSame('POST', $method);
self::assertSame('https://rest.clicksend.com/v3/sms/send', $url);
+ $body = json_decode($options['body'], true);
+ self::assertIsArray($body);
+ self::assertArrayHasKey('messages', $body);
+
return $response;
});
$transport = $this->createTransport($client, $from);
diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
index 9a2c82d0dcf61..8685407861ed1 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
@@ -109,6 +109,11 @@ public function getValue(object|array $objectOrArray, string|PropertyPathInterfa
return $propertyValues[\count($propertyValues) - 1][self::VALUE];
}
+ /**
+ * @template T of object|array
+ * @param T $objectOrArray
+ * @param-out ($objectOrArray is array ? array : T) $objectOrArray
+ */
public function setValue(object|array &$objectOrArray, string|PropertyPathInterface $propertyPath, mixed $value): void
{
if (\is_object($objectOrArray) && (false === strpbrk((string) $propertyPath, '.[') || $objectOrArray instanceof \stdClass && property_exists($objectOrArray, $propertyPath))) {
diff --git a/src/Symfony/Component/Runtime/Tests/phpt/application.php b/src/Symfony/Component/Runtime/Tests/phpt/application.php
index 1e1014e9f3e5a..ca2de555edfb7 100644
--- a/src/Symfony/Component/Runtime/Tests/phpt/application.php
+++ b/src/Symfony/Component/Runtime/Tests/phpt/application.php
@@ -18,8 +18,10 @@
return function (array $context) {
$command = new Command('go');
- $command->setCode(function (InputInterface $input, OutputInterface $output) use ($context) {
+ $command->setCode(function (InputInterface $input, OutputInterface $output) use ($context): int {
$output->write('OK Application '.$context['SOME_VAR']);
+
+ return 0;
});
$app = new Application();
diff --git a/src/Symfony/Component/Runtime/Tests/phpt/command.php b/src/Symfony/Component/Runtime/Tests/phpt/command.php
index 3a5fa11e00000..e307d195b113e 100644
--- a/src/Symfony/Component/Runtime/Tests/phpt/command.php
+++ b/src/Symfony/Component/Runtime/Tests/phpt/command.php
@@ -19,7 +19,9 @@
return function (Command $command, InputInterface $input, OutputInterface $output, array $context) {
$command->addOption('hello', 'e', InputOption::VALUE_REQUIRED, 'How should I greet?', 'OK');
- return $command->setCode(function () use ($input, $output, $context) {
+ return $command->setCode(function () use ($input, $output, $context): int {
$output->write($input->getOption('hello').' Command '.$context['SOME_VAR']);
+
+ return 0;
});
};
diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_0_to_1.php b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_0_to_1.php
index af6409dda62bc..2968e37ea02f4 100644
--- a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_0_to_1.php
+++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_0_to_1.php
@@ -20,6 +20,8 @@
require __DIR__.'/autoload.php';
-return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): void {
+return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): int {
$output->writeln($context['DEBUG_ENABLED']);
+
+ return 0;
});
diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_1_to_0.php b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_1_to_0.php
index 78a0bf29448b8..1f2fa3590e16f 100644
--- a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_1_to_0.php
+++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_debug_exists_1_to_0.php
@@ -20,6 +20,8 @@
require __DIR__.'/autoload.php';
-return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): void {
+return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): int {
$output->writeln($context['DEBUG_MODE']);
+
+ return 0;
});
diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_exists.php b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_exists.php
index 3e72372e5af06..8587f20f2382b 100644
--- a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_exists.php
+++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_env_exists.php
@@ -20,6 +20,8 @@
require __DIR__.'/autoload.php';
-return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): void {
+return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): int {
$output->writeln($context['ENV_MODE']);
+
+ return 0;
});
diff --git a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.php b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.php
index 3fe4f44d7967b..4ab7694298f95 100644
--- a/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.php
+++ b/src/Symfony/Component/Runtime/Tests/phpt/dotenv_overload_command_no_debug.php
@@ -19,6 +19,8 @@
require __DIR__.'/autoload.php';
-return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): void {
+return static fn (Command $command, OutputInterface $output, array $context): Command => $command->setCode(static function () use ($output, $context): int {
$output->writeln($context['DEBUG_ENABLED']);
+
+ return 0;
});
diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.be.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.be.xlf
index 194392935fcc1..6478e2a15caf7 100644
--- a/src/Symfony/Component/Security/Core/Resources/translations/security.be.xlf
+++ b/src/Symfony/Component/Security/Core/Resources/translations/security.be.xlf
@@ -76,7 +76,7 @@
Too many failed login attempts, please try again in %minutes% minutes.
- Занадта шмат няўдалых спробаў уваходу, калі ласка, паспрабуйце зноў праз %minutes% хвіліну.|Занадта шмат няўдалых спробаў уваходу, калі ласка, паспрабуйце зноў праз %minutes% хвіліны.|Занадта шмат няўдалых спробаў уваходу, калі ласка, паспрабуйце зноў праз %minutes% хвілін.
+ Занадта вялікая колькасць няўдалых спробаў уваходу. Калі ласка, паспрабуйце зноў праз %minutes% хвіліну.|Занадта вялікая колькасць няўдалых спробаў уваходу. Калі ласка, паспрабуйце зноў праз %minutes% хвіліны.|Занадта вялікая колькасць няўдалых спробаў уваходу. Калі ласка, паспрабуйце зноў праз %minutes% хвілін.