diff --git a/CHANGELOG-5.4.md b/CHANGELOG-5.4.md
index f4ed8a2960638..4eab46a4a882f 100644
--- a/CHANGELOG-5.4.md
+++ b/CHANGELOG-5.4.md
@@ -7,6 +7,21 @@ 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.14 (2022-10-12)
+
+ * bug #47621 [Serializer] Allow getting discriminated type by class name (TamasSzigeti)
+ * bug #47833 [TwigBridge] Remove empty spaces between choices when using checkbox-inline or checkbox-switch (simondaigre)
+ * bug #47808 [HttpClient] Fix seeking in not-yet-initialized requests (nicolas-grekas)
+ * bug #47798 [DoctrineBridge] Fix auto mapping for bundles that contain only embeddables (jorissae)
+ * bug #47702 [Messenger] Fix default serializer not handling DateTime objects properly (barton-webwings)
+ * bug #47779 [Console] Fix `Helper::removeDecoration` hyperlink bug (greew)
+ * bug #47753 [Mime] sync message serializer code for forward-compatibility (xabbuh)
+ * bug #47763 [PropertyInfo] a readonly property must not be reported as being writable (xabbuh)
+ * bug #47731 [WebProfiler] Fix overflow issue in Forms panel (zolikonta)
+ * bug #46956 [FrameworkBundle] Allow to specify `null` for exception mapping configuration values (andrew-demb)
+ * bug #47746 [HttpFoundation] Fix BinaryFileResponse content type detection logic (X-Coder264)
+ * bug #47626 [Notifier] [Expo] Throw exception on error-response from expo api (sdrewergutland)
+
* 5.4.13 (2022-09-30)
* bug #47317 [Security] Fix login url matching when app is not run with url rewriting or from a sub folder (sgehrig)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 167d559dc9e9f..d652d3de50a5c 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -16,15 +16,15 @@ The Symfony Connect username in parenthesis allows to get more information
- Grégoire Pineau (lyrixx)
- Wouter de Jong (wouterj)
- Maxime Steinhausser (ogizanagi)
- - Christophe Coevoet (stof)
- Kévin Dunglas (dunglas)
+ - Christophe Coevoet (stof)
- Jordi Boggiano (seldaek)
- Roland Franssen (ro0)
- Victor Berchet (victor)
- Yonel Ceruto (yonelceruto)
- Tobias Nyholm (tobias)
- - Oskar Stark (oskarstark)
- Javier Eguiluz (javier.eguiluz)
+ - Oskar Stark (oskarstark)
- Ryan Weaver (weaverryan)
- Johannes S (johannes)
- Jakub Zalas (jakubzalas)
@@ -52,8 +52,8 @@ The Symfony Connect username in parenthesis allows to get more information
- HypeMC (hypemc)
- Matthias Pigulla (mpdude)
- Laurent VOULLEMIER (lvo)
- - Pierre du Plessis (pierredup)
- Antoine Makdessi (amakdessi)
+ - Pierre du Plessis (pierredup)
- Grégoire Paris (greg0ire)
- Gabriel Ostrolucký (gadelat)
- Jonathan Wage (jwage)
@@ -76,14 +76,14 @@ The Symfony Connect username in parenthesis allows to get more information
- Bulat Shakirzyanov (avalanche123)
- Iltar van der Berg
- Miha Vrhovnik (mvrhov)
- - Saša Stamenković (umpirsky)
- Mathieu Piot (mpiot)
+ - Saša Stamenković (umpirsky)
- Alex Pott
- Guilhem N (guilhemn)
+ - Vincent Langlet (deviling)
- Vladimir Reznichenko (kalessil)
- Sarah Khalil (saro0h)
- Konstantin Kudryashov (everzet)
- - Vincent Langlet (deviling)
- Tomas Norkūnas (norkunas)
- Bilal Amarni (bamarni)
- Eriksen Costa
@@ -115,6 +115,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Maxime Helias (maxhelias)
- Ener-Getick
- Ruud Kamphuis (ruudk)
+ - Mathieu Lechat (mat_the_cat)
- Sebastiaan Stok (sstok)
- Jérôme Vasseur (jvasseur)
- Ion Bazan (ionbazan)
@@ -126,6 +127,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Bart van den Burg (burgov)
- Jordan Alliot (jalliot)
- Smaine Milianni (ismail1432)
+ - Antoine Lamirault
- John Wards (johnwards)
- Dariusz Ruminski
- Lars Strojny (lstrojny)
@@ -134,7 +136,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Konstantin.Myakshin
- Rokas Mikalkėnas (rokasm)
- Arman Hosseini (arman)
- - Antoine Lamirault
- Arnaud Le Blanc (arnaud-lb)
- Maxime STEINHAUSSER
- Peter Kokot (maastermedia)
@@ -147,7 +148,6 @@ The Symfony Connect username in parenthesis allows to get more information
- YaFou
- Gary PEGEOT (gary-p)
- Chris Wilkinson (thewilkybarkid)
- - Mathieu Lechat (mat_the_cat)
- Brice BERNARD (brikou)
- Roman Martinuk (a2a4)
- Gregor Harlan (gharlan)
@@ -159,13 +159,14 @@ The Symfony Connect username in parenthesis allows to get more information
- Jesse Rushlow (geeshoe)
- Théo FIDRY
- jeremyFreeAgent (jeremyfreeagent)
+ - Michael Babker (mbabker)
- Włodzimierz Gajda (gajdaw)
- Christian Scheb
- Guillaume (guill)
- Tugdual Saunier (tucksaun)
- Jacob Dreesen (jdreesen)
+ - Jeroen Spee (jeroens)
- Joel Wurtz (brouznouf)
- - Michael Babker (mbabker)
- Olivier Dolbeau (odolbeau)
- Florian Voutzinos (florianv)
- zairig imad (zairigimad)
@@ -173,7 +174,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Javier Spagnoletti (phansys)
- excelwebzone
- Jérôme Parmentier (lctrs)
- - Jeroen Spee (jeroens)
- HeahDude
- Richard van Laak (rvanlaak)
- Paráda József (paradajozsef)
@@ -181,6 +181,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Alexander Schwenn (xelaris)
- Fabien Pennequin (fabienpennequin)
- Gordon Franke (gimler)
+ - François-Xavier de Guillebon (de-gui_f)
- Christopher Hertel (chertel)
- Gabriel Caruso
- Anthony GRASSIOT (antograssiot)
@@ -195,6 +196,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Tigran Azatyan (tigranazatyan)
- Eric GELOEN (gelo)
- Matthieu Napoli (mnapoli)
+ - Andreas Schempp (aschempp)
- Tomáš Votruba (tomas_votruba)
- Joshua Thijssen
- Stefano Sala (stefano.sala)
@@ -211,13 +213,12 @@ The Symfony Connect username in parenthesis allows to get more information
- Timo Bakx (timobakx)
- Juti Noppornpitak (shiroyuki)
- Joe Bennett (kralos)
+ - Nate Wiebe (natewiebe13)
- Hugo Alliaume (kocal)
- Anthony MARTIN
- Colin O'Dell (colinodell)
- Sebastian Hörl (blogsh)
- Ben Davies (bendavies)
- - Andreas Schempp (aschempp)
- - François-Xavier de Guillebon (de-gui_f)
- Daniel Gomes (danielcsgomes)
- Michael Käfer (michael_kaefer)
- Hidenori Goto (hidenorigoto)
@@ -226,12 +227,12 @@ The Symfony Connect username in parenthesis allows to get more information
- Guilherme Blanco (guilhermeblanco)
- Chi-teck
- Antonio Pauletich (x-coder264)
- - Nate Wiebe (natewiebe13)
- Michael Voříšek
- SpacePossum
- Pablo Godel (pgodel)
- Romaric Drigon (romaricdrigon)
- Andréia Bohner (andreia)
+ - Dāvis Zālītis (k0d3r1s)
- Jannik Zschiesche
- Rafael Dohms (rdohms)
- George Mponos (gmponos)
@@ -257,7 +258,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Justin Hileman (bobthecow)
- Niels Keurentjes (curry684)
- Vyacheslav Pavlov
- - Dāvis Zālītis (k0d3r1s)
- Richard Shank (iampersistent)
- Andre Rømcke (andrerom)
- Dmitrii Poddubnyi (karser)
@@ -314,6 +314,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Wojciech Kania
- Alexey Kopytko (sanmai)
- Sergey Linnik (linniksa)
+ - Warxcell (warxcell)
- Richard Miller
- Leo Feyer (leofeyer)
- Mario A. Alvarez Garcia (nomack84)
@@ -346,6 +347,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Loick Piera (pyrech)
- Vitalii Ekert (comrade42)
- Clara van Miert
+ - Martin Auswöger
- Alexander Menshchikov
- Stepan Anchugov (kix)
- bronze1man
@@ -358,8 +360,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Edi Modrić (emodric)
- Philipp Wahala (hifi)
- Nikolay Labinskiy (e-moe)
- - Warxcell (warxcell)
- Martin Schuhfuß (usefulthink)
+ - Phil Taylor (prazgod)
- apetitpa
- Vladyslav Loboda
- Pierre Minnieur (pminnieur)
@@ -385,7 +387,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Markus Fasselt (digilist)
- Maxime Veber (nek-)
- Marcin Sikoń (marphi)
- - Martin Auswöger
- Sullivan SENECHAL (soullivaneuh)
- Rui Marinho (ruimarinho)
- Marc Weistroff (futurecat)
@@ -404,7 +405,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Alex Bowers
- Simon Podlipsky (simpod)
- Marcel Beerta (mazen)
- - Phil Taylor (prazgod)
- flack (flack)
- Craig Duncan (duncan3dc)
- Mantis Development
@@ -500,6 +500,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Chris Tanaskoski
- julien57
- Loïc Frémont (loic425)
+ - Ippei Sumida (ippey_s)
- Ben Ramsey (ramsey)
- Matthieu Auger (matthieuauger)
- Kévin THERAGE (kevin_therage)
@@ -536,6 +537,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Soufian EZ ZANTAR (soezz)
- Jan van Thoor (janvt)
- Martin Kirilov (wucdbm)
+ - Axel Guckelsberger (guite)
- Chris Smith (cs278)
- Florian Klein (docteurklein)
- Bilge
@@ -561,6 +563,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Yannick Ihmels (ihmels)
- Saif Eddin G
- Emmanuel BORGES (eborges78)
+ - siganushka (siganushka)
- Aurelijus Valeiša (aurelijus)
- Evert Harmeling (evertharmeling)
- Jan Decavele (jandc)
@@ -633,7 +636,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Andy Palmer (andyexeter)
- Marko H. Tamminen (gzumba)
- Francesco Levorato
- - Ippei Sumida (ippey_s)
- DerManoMann
- David Molineus
- Desjardins Jérôme (jewome62)
@@ -708,7 +710,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Steve Grunwell
-
- Alex (aik099)
- - Axel Guckelsberger (guite)
- Greg Thornton (xdissent)
- BENOIT POLASZEK (bpolaszek)
- Shaharia Azam
@@ -771,7 +772,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Shakhobiddin
- Kai
- Lee Rowlands
- - siganushka (siganushka)
- Alain Hippolyte (aloneh)
- Karoly Negyesi (chx)
- Xavier HAUSHERR
@@ -903,6 +903,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Damien Fa
- Kevin McBride
- Sergio Santoro
+ - AndrolGenhald
- Philipp Rieber (bicpi)
- Dennis Væversted (srnzitcom)
- Manuel de Ruiter (manuel)
@@ -917,6 +918,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Jon Dufresne
- Chad Sikorra (chadsikorra)
- Mathias Brodala (mbrodala)
+ - naitsirch (naitsirch)
- Evan S Kaufman (evanskaufman)
- Jonathan Sui Lioung Lee Slew (jlslew)
- mcben
@@ -989,6 +991,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ziumin
- Matthias Schmidt
- Lenar Lõhmus
+ - Ilija Tovilo (ilijatovilo)
- Samaël Villette (samadu61)
- Zach Badgett (zachbadgett)
- Loïc Faugeron
@@ -1001,6 +1004,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Philipp Keck
- Disquedur
- Markus S. (staabm)
+ - Guilherme Ferreira
- Geoffrey Tran (geoff)
- Elan Ruusamäe (glen)
- Brad Jones
@@ -1050,6 +1054,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Safonov Nikita (ns3777k)
- Simon DELICATA
- Thibault Buathier (gwemox)
+ - Julien Boudry
- vitaliytv
- Andreas Hennings
- Arnaud Frézet
@@ -1074,6 +1079,7 @@ The Symfony Connect username in parenthesis allows to get more information
- pizzaminded
- Matthieu Calie (matth--)
- Stéphane Escandell (sescandell)
+ - Philippe SEGATORI (tigitz)
- ivan
- linh
- Oleg Krasavin (okwinza)
@@ -1354,7 +1360,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Toni Peric (tperic)
- yclian
- Aleksey Prilipko
- - AndrolGenhald
- Andrew Berry
- Wybren Koelmans (wybren_koelmans)
- Dmytro Dzubenko
@@ -1432,6 +1437,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Dennis Hotson
- Andrew Tchircoff (andrewtch)
- Lars Vierbergen (vierbergenlars)
+ - Barney Hanlon
- Bart Wach
- Jos Elstgeest
- Kirill Lazarev
@@ -1568,6 +1574,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Gennadi Janzen
- SenTisso
- Joe Springe
+ - Ivan Kurnosov
- Flinsch
- botbotbot
- Timon van der Vorm
@@ -1675,7 +1682,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Cyrille Bourgois (cyrilleb)
- Gerard van Helden (drm)
- Johnny Peck (johnnypeck)
- - naitsirch (naitsirch)
- Geoffrey Monte (numerogeek)
- Martijn Boers (plebian)
- Plamen Mishev (pmishev)
@@ -1969,6 +1975,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Kirill Nesmeyanov (serafim)
- Reece Fowell (reecefowell)
- Guillaume Gammelin
+ - wuchen90
- Valérian Galliat
- d-ph
- Renan Taranto (renan-taranto)
@@ -1982,7 +1989,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Mikkel Paulson
- ergiegonzaga
- Liverbool (liverbool)
- - Julien Boudry
- Dalibor Karlović
- Sam Malone
- Ha Phan (haphan)
@@ -2083,8 +2089,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Troy McCabe
- Ville Mattila
- gr1ev0us
+ - Léo VINCENT
- mlazovla
- Max Beutel
+ - Nathan Sepulveda
- Antanas Arvasevicius
- Pierre Dudoret
- Michal Trojanowski
@@ -2187,6 +2195,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Vladimir Luchaninov (luchaninov)
- spdionis
- rchoquet
+ - v.shevelev
- gitlost
- Taras Girnyk
- Sergio
@@ -2201,6 +2210,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Gilles Doge (gido)
- abulford
- Philipp Kretzschmar
+ - Jairo Pastor
- Ilya Vertakov
- Brooks Boyd
- Axel Venet
@@ -2252,6 +2262,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Sergey Yuferev
- Tobias Stöckler
- Mario Young
+ - martkop26
- Sander Hagen
- Ilia (aliance)
- cilefen (cilefen)
@@ -2356,6 +2367,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Daniel Richter (richtermeister)
- Sandro Hopf (senaria)
- ChrisC
+ - jack.shpartko
- Kim Laï Trinh
- Jason Desrosiers
- m.chwedziak
@@ -2401,6 +2413,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Daniel González Zaballos (dem3trio)
- Emmanuel Vella (emmanuel.vella)
- Guillaume BRETOU (guiguiboy)
+ - nuryagdy mustapayev (nueron)
- Carsten Nielsen (phreaknerd)
- Jay Severson
- René Kerner
@@ -2614,6 +2627,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Jesper Noordsij
- DerStoffel
- Maciej Schmidt
+ - tatankat
- nuncanada
- Thierry Marianne
- František Bereň
@@ -2626,6 +2640,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Kamil Madejski (kmadejski)
- Nicolas Tallefourtané (nicolab)
- Botond Dani (picur)
+ - Radek Wionczek (rwionczek)
- Nick Stemerdink
- David Stone
- Grayson Koonce
@@ -2694,6 +2709,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Artem Lopata (bumz)
- alex
- Roman Orlov
+ - Andreas Allacher
- VolCh
- Alexey Popkov
- Gijs Kunze
@@ -2779,18 +2795,21 @@ The Symfony Connect username in parenthesis allows to get more information
- Alexander Zogheb
- Rémi Blaise
- Nicolas Séverin
+ - Houssem
- Joel Marcey
- zolikonta
- David Christmann
- root
- pf
- Vincent Chalnot
+ - Patrizio Bekerle
- Tom Maguire
- Mateusz Lerczak
- Richard Quadling
- David Zuelke
- Adrian
- neFAST
+ - Peter Gribanov
- Pierre Rineau
- Florian Morello
- Maxim Lovchikov
@@ -2840,7 +2859,6 @@ The Symfony Connect username in parenthesis allows to get more information
- David Windell
- Gabriel Birke
- Derek Bonner
- - Ivan Kurnosov
- martijn
- annesosensio
- NothingWeAre
@@ -3049,6 +3067,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Neophy7e
- bokonet
- Arrilot
+ - andrey-tech
- Shaun Simmons
- Markus Staab
- Pierre-Louis LAUNAY
@@ -3120,6 +3139,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Lin Lu
- arduanov
- sualko
+ - Martin Komischke
- ADmad
- Nicolas Roudaire
- Abdouni Karim (abdounikarim)
@@ -3154,7 +3174,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Gerry Vandermaesen (gerryvdm)
- Arash Tabrizian (ghost098)
- Vladislav Krupenkin (ideea)
- - Ilija Tovilo (ilijatovilo)
- Peter Orosz (ill_logical)
- Imangazaliev Muhammad (imangazaliev)
- j0k (j0k)
diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php
index b048423c8b469..ad4ba455919fe 100644
--- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php
+++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php
@@ -311,10 +311,16 @@ private function detectMappingType(string $directory, ContainerBuilder $containe
foreach ($glob as $file) {
$content = file_get_contents($file);
- if (preg_match('/^#\[.*'.$quotedMappingObjectName.'\b/m', $content)) {
+ if (
+ preg_match('/^#\[.*'.$quotedMappingObjectName.'\b/m', $content) ||
+ preg_match('/^#\[.*Embeddable\b/m', $content)
+ ) {
break;
}
- if (preg_match('/^ \* @.*'.$quotedMappingObjectName.'\b/m', $content)) {
+ if (
+ preg_match('/^ \* @.*'.$quotedMappingObjectName.'\b/m', $content) ||
+ preg_match('/^ \* @.*Embeddable\b/m', $content)
+ ) {
$type = 'annotation';
break;
}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php
index 294767b987fac..a7ed7ad5abadc 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php
@@ -279,8 +279,10 @@ public function testUnrecognizedCacheDriverException()
public function providerBundles()
{
yield ['AnnotationsBundle', 'annotation', '/Entity'];
+ yield ['FullEmbeddableAnnotationsBundle', 'annotation', '/Entity'];
if (\PHP_VERSION_ID >= 80000) {
yield ['AttributesBundle', 'attribute', '/Entity'];
+ yield ['FullEmbeddableAttributesBundle', 'attribute', '/Entity'];
}
yield ['XmlBundle', 'xml', '/Resources/config/doctrine'];
yield ['PhpBundle', 'php', '/Resources/config/doctrine'];
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php
new file mode 100644
index 0000000000000..d311a3f1ad1a1
--- /dev/null
+++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Fixtures\Bundles\FullEmbeddableAnnotationsBundle\Entity;
+
+use Doctrine\ORM\Mapping\Column;
+use Doctrine\ORM\Mapping\Embeddable;
+use Doctrine\ORM\Mapping\Id;
+
+/**
+ * @Embeddable
+ */
+class Address
+{
+
+ /** @Column(type="string") */
+ public $street;
+
+ /** @Column(type="string") */
+ public $zipCode;
+
+ /** @Column(type="string") */
+ public $city;
+
+ public function __construct($street, $zipCode, $city)
+ {
+ $this->street = $street;
+ $this->zipCode = $zipCode;
+ $this->city = $city;
+ }
+
+ public function __toString(): string
+ {
+ return sprintf('%s %s %s', $this->street, $this->zipCode, $this->city);
+ }
+}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/FullEmbeddableAnnotationsBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/FullEmbeddableAnnotationsBundle.php
new file mode 100644
index 0000000000000..ac03c9b0ce870
--- /dev/null
+++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/FullEmbeddableAnnotationsBundle.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Fixtures\Bundles\FullEmbeddableAnnotationsBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class FullEmbeddableAnnotationsBundle extends Bundle
+{
+}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php
new file mode 100644
index 0000000000000..c0c58d6a21ce2
--- /dev/null
+++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Fixtures\Bundles\FullEmbeddableAttributesBundle\Entity;
+
+use Doctrine\ORM\Mapping\Column;
+use Doctrine\ORM\Mapping\Embeddable;
+use Doctrine\ORM\Mapping\Id;
+
+#[Embeddable]
+class Address
+{
+
+ #[Column(type: 'string')]
+ public $street;
+
+ #[Column(type: 'string')]
+ public $zipCode;
+
+ #[Column(type: 'string')]
+ public $city;
+
+ public function __construct($street, $zipCode, $city)
+ {
+ $this->street = $street;
+ $this->zipCode = $zipCode;
+ $this->city = $city;
+ }
+
+ public function __toString(): string
+ {
+ return sprintf('%s %s %s', $this->street, $this->zipCode, $this->city);
+ }
+}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/FullEmbeddableAttributesBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/FullEmbeddableAttributesBundle.php
new file mode 100644
index 0000000000000..374f63dbf5422
--- /dev/null
+++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/FullEmbeddableAttributesBundle.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Fixtures\Bundles\FullEmbeddableAttributesBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class FullEmbeddableAttributesBundle extends Bundle
+{
+}
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
index 54f981856b653..f28933cf97357 100644
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
@@ -12,6 +12,10 @@
// Please update when phpunit needs to be reinstalled with fresh deps:
// Cache-Id: 2021-02-04 11:00 UTC
+if ('cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
error_reporting(-1);
global $argv, $argc;
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig
index eef6f606edc14..54903a5713082 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig
@@ -213,10 +213,10 @@
{%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%}
{%- set row_class = 'form-check' -%}
{%- if 'checkbox-inline' in parent_label_class %}
- {% set row_class = row_class ~ ' form-check-inline' %}
+ {%- set row_class = row_class ~ ' form-check-inline' -%}
{% endif -%}
{%- if 'checkbox-switch' in parent_label_class %}
- {% set row_class = row_class ~ ' form-switch' %}
+ {%- set row_class = row_class ~ ' form-switch' -%}
{% endif -%}
{{- form_label(form, null, { widget: parent() }) -}}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index a86bac9fb95e9..9e06c9f3c73e0 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -1227,15 +1227,19 @@ private function addExceptionsSection(ArrayNodeDefinition $rootNode)
->scalarNode('log_level')
->info('The level of log message. Null to let Symfony decide.')
->validate()
- ->ifTrue(function ($v) use ($logLevels) { return !\in_array($v, $logLevels); })
+ ->ifTrue(function ($v) use ($logLevels) { return null !== $v && !\in_array($v, $logLevels, true); })
->thenInvalid(sprintf('The log level is not valid. Pick one among "%s".', implode('", "', $logLevels)))
->end()
->defaultNull()
->end()
->scalarNode('status_code')
- ->info('The status code of the response. Null to let Symfony decide.')
+ ->info('The status code of the response. Null or 0 to let Symfony decide.')
+ ->beforeNormalization()
+ ->ifTrue(function ($v) { return 0 === $v; })
+ ->then(function ($v) { return null; })
+ ->end()
->validate()
- ->ifTrue(function ($v) { return $v < 100 || $v > 599; })
+ ->ifTrue(function ($v) { return null !== $v && ($v < 100 || $v > 599); })
->thenInvalid('The status code is not valid. Pick a value between 100 and 599.')
->end()
->defaultNull()
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php b/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php
index 4920c43ebe182..55658f5b19b76 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php
@@ -9,6 +9,10 @@
* file that was distributed with this source code.
*/
+if ('cli' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
require dirname(__DIR__, 6).'/vendor/autoload.php';
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\UnusedTagsPassUtils;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php
index 5d0dde0e0ac64..96b128f97b010 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php
@@ -1,6 +1,9 @@
loadFromExtension('framework', [
'exceptions' => [
@@ -8,5 +11,17 @@
'log_level' => 'info',
'status_code' => 422,
],
+ NotFoundHttpException::class => [
+ 'log_level' => 'info',
+ 'status_code' => null,
+ ],
+ ConflictHttpException::class => [
+ 'log_level' => 'info',
+ 'status_code' => 0,
+ ],
+ ServiceUnavailableHttpException::class => [
+ 'log_level' => null,
+ 'status_code' => 500,
+ ],
],
]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml
index cc73b8de3ced6..49878fc118b50 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml
@@ -8,6 +8,9 @@
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml
index 82fab4e04a9f9..3958c4c5fd3fb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml
@@ -3,3 +3,12 @@ framework:
Symfony\Component\HttpKernel\Exception\BadRequestHttpException:
log_level: info
status_code: 422
+ Symfony\Component\HttpKernel\Exception\NotFoundHttpException:
+ log_level: info
+ status_code: null
+ Symfony\Component\HttpKernel\Exception\ConflictHttpException:
+ log_level: info
+ status_code: 0
+ Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException:
+ log_level: null
+ status_code: 500
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index 3fb337b47aaee..abbe2d9a3e4c1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -552,6 +552,18 @@ public function testExceptionsConfig()
'log_level' => 'info',
'status_code' => 422,
],
+ \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class => [
+ 'log_level' => 'info',
+ 'status_code' => null,
+ ],
+ \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class => [
+ 'log_level' => 'info',
+ 'status_code' => null,
+ ],
+ \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class => [
+ 'log_level' => null,
+ 'status_code' => 500,
+ ],
], $container->getDefinition('exception_listener')->getArgument(3));
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig
index db97100e49b37..d99ad4f77946b 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig
@@ -90,7 +90,8 @@
cursor: pointer;
padding: 5px 7px 5px 22px;
position: relative;
-
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.tree .toggle-button {
/* provide a bigger clickable area than just 10x10px */
@@ -449,7 +450,7 @@
{% import _self as tree %}
{% set has_error = data.errors is defined and data.errors|length > 0 %}
-
+
{% if has_error %}
{{ data.errors|length }}
{% endif %}
diff --git a/src/Symfony/Component/Console/Helper/Helper.php b/src/Symfony/Component/Console/Helper/Helper.php
index 50f51c7902f9a..c7d3e25d0e33e 100644
--- a/src/Symfony/Component/Console/Helper/Helper.php
+++ b/src/Symfony/Component/Console/Helper/Helper.php
@@ -171,6 +171,8 @@ public static function removeDecoration(OutputFormatterInterface $formatter, ?st
$string = $formatter->format($string ?? '');
// remove already formatted characters
$string = preg_replace("/\033\[[^m]*m/", '', $string ?? '');
+ // remove terminal hyperlinks
+ $string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string ?? '');
$formatter->setDecorated($isDecorated);
return $string;
diff --git a/src/Symfony/Component/Console/Tests/Helper/HelperTest.php b/src/Symfony/Component/Console/Tests/Helper/HelperTest.php
index 184d86e092aba..b8c8910874ed8 100644
--- a/src/Symfony/Component/Console/Tests/Helper/HelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/HelperTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
class HelperTest extends TestCase
@@ -42,6 +43,16 @@ public function formatTimeProvider()
];
}
+ public function decoratedTextProvider()
+ {
+ return [
+ ['abc', 'abc'],
+ ['abc', 'abc'],
+ ["a\033[1;36mbc", 'abc'],
+ ["a\033]8;;http://url\033\\b\033]8;;\033\\c", 'abc'],
+ ];
+ }
+
/**
* @dataProvider formatTimeProvider
*
@@ -52,4 +63,12 @@ public function testFormatTime($secs, $expectedFormat)
{
$this->assertEquals($expectedFormat, Helper::formatTime($secs));
}
+
+ /**
+ * @dataProvider decoratedTextProvider
+ */
+ public function testRemoveDecoration(string $decoratedText, string $undecoratedText)
+ {
+ $this->assertEquals($undecoratedText, Helper::removeDecoration(new OutputFormatter(), $decoratedText));
+ }
}
diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php
index 9d944084b6029..2d70bd271f589 100644
--- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php
@@ -1595,6 +1595,31 @@ public function testWithColspanAndMaxWith()
| | | nsectetur |
+-----------------+-----------------+-----------------+
+TABLE;
+
+ $this->assertSame($expected, $this->getOutputContent($output));
+ }
+
+ public function testWithHyperlinkAndMaxWidth()
+ {
+ $table = new Table($output = $this->getOutputStream(true));
+ $table
+ ->setRows([
+ ['Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor>'],
+ ])
+ ;
+ $table->setColumnMaxWidth(0, 20);
+ $table->render();
+
+ $expected =
+ <<
assertSame($expected, $this->getOutputContent($output));
diff --git a/src/Symfony/Component/ErrorHandler/Resources/bin/extract-tentative-return-types.php b/src/Symfony/Component/ErrorHandler/Resources/bin/extract-tentative-return-types.php
index cc98f58b58fa0..ae2c703e1dae2 100755
--- a/src/Symfony/Component/ErrorHandler/Resources/bin/extract-tentative-return-types.php
+++ b/src/Symfony/Component/ErrorHandler/Resources/bin/extract-tentative-return-types.php
@@ -10,6 +10,10 @@
* file that was distributed with this source code.
*/
+if ('cli' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
// Run from the root of the php-src repository, this script generates
// a table with all the methods that have a tentative return type.
//
diff --git a/src/Symfony/Component/ErrorHandler/Resources/bin/patch-type-declarations b/src/Symfony/Component/ErrorHandler/Resources/bin/patch-type-declarations
index efcfcb25daa5a..824ef67990d1c 100755
--- a/src/Symfony/Component/ErrorHandler/Resources/bin/patch-type-declarations
+++ b/src/Symfony/Component/ErrorHandler/Resources/bin/patch-type-declarations
@@ -10,6 +10,10 @@
* file that was distributed with this source code.
*/
+if ('cli' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
if (\in_array('-h', $argv) || \in_array('--help', $argv)) {
echo implode(PHP_EOL, [
' Patches type declarations based on "@return" PHPDoc and triggers deprecations for',
diff --git a/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php b/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php
index c86e962526182..72b4d25f56d4f 100644
--- a/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php
+++ b/src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php
@@ -9,6 +9,10 @@
* file that was distributed with this source code.
*/
+if ('cli' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
$operators = ['not', '!', 'or', '||', '&&', 'and', '|', '^', '&', '==', '===', '!=', '!==', '<', '>', '>=', '<=', 'not in', 'in', '..', '+', '-', '~', '*', '/', '%', 'matches', '**'];
$operators = array_combine($operators, array_map('strlen', $operators));
arsort($operators);
diff --git a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
index 6942175b97507..50a7c36623ff1 100644
--- a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
+++ b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php
@@ -22,7 +22,7 @@
*/
class StreamWrapper
{
- /** @var resource|string|null */
+ /** @var resource|null */
public $context;
/** @var HttpClientInterface */
@@ -31,7 +31,7 @@ class StreamWrapper
/** @var ResponseInterface */
private $response;
- /** @var resource|null */
+ /** @var resource|string|null */
private $content;
/** @var resource|null */
@@ -89,6 +89,7 @@ public function bindHandles(&$handle, &$content): void
{
$this->handle = &$handle;
$this->content = &$content;
+ $this->offset = null;
}
public function stream_open(string $path, string $mode, int $options): bool
@@ -133,7 +134,7 @@ public function stream_read(int $count)
}
}
- if (0 !== fseek($this->content, $this->offset)) {
+ if (0 !== fseek($this->content, $this->offset ?? 0)) {
return false;
}
@@ -162,6 +163,11 @@ public function stream_read(int $count)
try {
$this->eof = true;
$this->eof = !$chunk->isTimeout();
+
+ if (!$this->eof && !$this->blocking) {
+ return '';
+ }
+
$this->eof = $chunk->isLast();
if ($chunk->isFirst()) {
@@ -204,7 +210,7 @@ public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
public function stream_tell(): int
{
- return $this->offset;
+ return $this->offset ?? 0;
}
public function stream_eof(): bool
@@ -214,6 +220,11 @@ public function stream_eof(): bool
public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
{
+ if (null === $this->content && null === $this->offset) {
+ $this->response->getStatusCode();
+ $this->offset = 0;
+ }
+
if (!\is_resource($this->content) || 0 !== fseek($this->content, 0, \SEEK_END)) {
return false;
}
@@ -221,7 +232,7 @@ public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
$size = ftell($this->content);
if (\SEEK_CUR === $whence) {
- $offset += $this->offset;
+ $offset += $this->offset ?? 0;
}
if (\SEEK_END === $whence || $size < $offset) {
diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
index eb68c55c0015a..9a1c177a533cb 100644
--- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
+++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
@@ -131,6 +131,18 @@ public function testNonBlockingStream()
$this->assertTrue(feof($stream));
}
+ public function testSeekAsyncStream()
+ {
+ $client = $this->getHttpClient(__FUNCTION__);
+ $response = $client->request('GET', 'http://localhost:8057/timeout-body');
+ $stream = $response->toStream(false);
+
+ $this->assertSame(0, fseek($stream, 0, \SEEK_CUR));
+ $this->assertSame('<1>', fread($stream, 8192));
+ $this->assertFalse(feof($stream));
+ $this->assertSame('<2>', stream_get_contents($stream));
+ }
+
public function testResponseStreamRewind()
{
$client = $this->getHttpClient(__FUNCTION__);
diff --git a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
index 45de4e120e6dc..e06575cfc763f 100644
--- a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
@@ -443,6 +443,7 @@ protected function getHttpClient(string $testCase): HttpClientInterface
return $client;
case 'testNonBlockingStream':
+ case 'testSeekAsyncStream':
$responses[] = new MockResponse((function () { yield '<1>'; yield ''; yield '<2>'; })(), ['response_headers' => $headers]);
break;
diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
index aff851956d4bc..6d7b80ad1212c 100644
--- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
+++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
@@ -206,9 +206,9 @@ public function setContentDisposition(string $disposition, string $filename = ''
*/
public function prepare(Request $request)
{
- parent::prepare($request);
-
if ($this->isInformational() || $this->isEmpty()) {
+ parent::prepare($request);
+
$this->maxlen = 0;
return $this;
@@ -218,6 +218,8 @@ public function prepare(Request $request)
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
}
+ parent::prepare($request);
+
$this->offset = 0;
$this->maxlen = -1;
diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
index fa5172ef61e36..65d6fe0854ab0 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
@@ -13,6 +13,7 @@
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\Stream;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
@@ -411,6 +412,40 @@ public function testPrepareNotAddingContentTypeHeaderIfNoContentResponse()
$this->assertFalse($response->headers->has('Content-Type'));
}
+ public function testContentTypeIsCorrectlyDetected()
+ {
+ $file = new File(__DIR__.'/File/Fixtures/test.gif');
+
+ try {
+ $file->getMimeType();
+ } catch (\LogicException $e) {
+ $this->markTestSkipped('Guessing the mime type is not possible');
+ }
+
+ $response = new BinaryFileResponse($file);
+
+ $request = Request::create('/');
+ $response->prepare($request);
+
+ $this->assertSame(200, $response->getStatusCode());
+ $this->assertSame('image/gif', $response->headers->get('Content-Type'));
+ }
+
+ public function testContentTypeIsNotGuessedWhenTheFileWasNotModified()
+ {
+ $response = new BinaryFileResponse(__DIR__.'/File/Fixtures/test.gif');
+ $response->setAutoLastModified();
+
+ $request = Request::create('/');
+ $request->headers->set('If-Modified-Since', $response->getLastModified()->format('D, d M Y H:i:s').' GMT');
+ $isNotModified = $response->isNotModified($request);
+ $this->assertTrue($isNotModified);
+ $response->prepare($request);
+
+ $this->assertSame(304, $response->getStatusCode());
+ $this->assertFalse($response->headers->has('Content-Type'));
+ }
+
protected function provideResponse()
{
return new BinaryFileResponse(__DIR__.'/../README.md', 200, ['Content-Type' => 'application/octet-stream']);
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php
index 9dc3871c25df7..b6fd0a357d6fe 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php
@@ -33,8 +33,14 @@ class ErrorListener implements EventSubscriberInterface
protected $controller;
protected $logger;
protected $debug;
+ /**
+ * @var array|null}>
+ */
protected $exceptionsMapping;
+ /**
+ * @param array|null}> $exceptionsMapping
+ */
public function __construct($controller, LoggerInterface $logger = null, bool $debug = false, array $exceptionsMapping = [])
{
$this->controller = $controller;
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 93068eda13b69..25370cef3f3de 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.13';
- public const VERSION_ID = 50413;
+ public const VERSION = '5.4.14';
+ public const VERSION_ID = 50414;
public const MAJOR_VERSION = 5;
public const MINOR_VERSION = 4;
- public const RELEASE_VERSION = 13;
+ public const RELEASE_VERSION = 14;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '11/2024';
diff --git a/src/Symfony/Component/Intl/Resources/bin/common.php b/src/Symfony/Component/Intl/Resources/bin/common.php
index cf47fa448b034..89d3459143d96 100644
--- a/src/Symfony/Component/Intl/Resources/bin/common.php
+++ b/src/Symfony/Component/Intl/Resources/bin/common.php
@@ -9,6 +9,10 @@
* file that was distributed with this source code.
*/
+if ('cli' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
define('LINE_WIDTH', 75);
define('LINE', str_repeat('-', LINE_WIDTH)."\n");
diff --git a/src/Symfony/Component/Intl/Resources/bin/update-data.php b/src/Symfony/Component/Intl/Resources/bin/update-data.php
index 2fffa4ec4f632..2aeb141d2cc26 100644
--- a/src/Symfony/Component/Intl/Resources/bin/update-data.php
+++ b/src/Symfony/Component/Intl/Resources/bin/update-data.php
@@ -23,6 +23,10 @@
use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Util\GitRepository;
+if ('cli' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
require_once __DIR__.'/common.php';
require_once __DIR__.'/autoload.php';
diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
index 614226643d3c5..8106506d27e97 100644
--- a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
+++ b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
@@ -21,6 +21,7 @@
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
+use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer as SymfonySerializer;
use Symfony\Component\Serializer\SerializerInterface as SymfonySerializerInterface;
@@ -51,7 +52,7 @@ public static function create(): self
}
$encoders = [new XmlEncoder(), new JsonEncoder()];
- $normalizers = [new ArrayDenormalizer(), new ObjectNormalizer()];
+ $normalizers = [new DateTimeNormalizer(), new ArrayDenormalizer(), new ObjectNormalizer()];
$serializer = new SymfonySerializer($normalizers, $encoders);
return new self($serializer);
diff --git a/src/Symfony/Component/Mime/Resources/bin/update_mime_types.php b/src/Symfony/Component/Mime/Resources/bin/update_mime_types.php
index 9f13bf3e1f968..5586f097f7ee8 100644
--- a/src/Symfony/Component/Mime/Resources/bin/update_mime_types.php
+++ b/src/Symfony/Component/Mime/Resources/bin/update_mime_types.php
@@ -9,6 +9,10 @@
* file that was distributed with this source code.
*/
+if ('cli' !== \PHP_SAPI) {
+ throw new Exception('This script must be run from the command line.');
+}
+
// load new map
$data = json_decode(file_get_contents('https://cdn.jsdelivr.net/gh/jshttp/mime-db@v1.49.0/db.json'), true);
$new = [];
diff --git a/src/Symfony/Component/Mime/Tests/MessageTest.php b/src/Symfony/Component/Mime/Tests/MessageTest.php
index 6ed5aabdbe680..f35590ce9e174 100644
--- a/src/Symfony/Component/Mime/Tests/MessageTest.php
+++ b/src/Symfony/Component/Mime/Tests/MessageTest.php
@@ -202,7 +202,6 @@ public function testSymfonySerialize()
"disposition": null,
"name": null,
"encoding": "quoted-printable",
- "seekable": null,
"headers": [],
"class": "Symfony\\\\Component\\\\Mime\\\\Part\\\TextPart"
},
@@ -213,7 +212,6 @@ public function testSymfonySerialize()
"disposition": null,
"name": null,
"encoding": "quoted-printable",
- "seekable": null,
"headers": [],
"class": "Symfony\\\\Component\\\\Mime\\\\Part\\\\TextPart"
}
@@ -224,15 +222,12 @@ public function testSymfonySerialize()
{
"filename": "text.txt",
"mediaType": "application",
- "cid": null,
- "handle": null,
"body": "text data",
"charset": null,
"subtype": "octet-stream",
"disposition": "attachment",
"name": "text.txt",
"encoding": "base64",
- "seekable": null,
"headers": [],
"class": "Symfony\\\\Component\\\\Mime\\\\Part\\\\DataPart"
}
diff --git a/src/Symfony/Component/Mime/composer.json b/src/Symfony/Component/Mime/composer.json
index cd04969b9a9cc..ec96dff5d0b61 100644
--- a/src/Symfony/Component/Mime/composer.json
+++ b/src/Symfony/Component/Mime/composer.json
@@ -28,13 +28,14 @@
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/property-access": "^4.4|^5.1|^6.0",
"symfony/property-info": "^4.4|^5.1|^6.0",
- "symfony/serializer": "^5.2|^6.0"
+ "symfony/serializer": "^5.4.14|~6.0.14|^6.1.6"
},
"conflict": {
"egulias/email-validator": "~3.0.0",
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
- "symfony/mailer": "<4.4"
+ "symfony/mailer": "<4.4",
+ "symfony/serializer": "<5.4.14|>=6.0,<6.0.14|>=6.1,<6.1.6"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Mime\\": "" },
diff --git a/src/Symfony/Component/Notifier/Bridge/Expo/ExpoTransport.php b/src/Symfony/Component/Notifier/Bridge/Expo/ExpoTransport.php
index 455b3888f74a5..0dc316fb56171 100644
--- a/src/Symfony/Component/Notifier/Bridge/Expo/ExpoTransport.php
+++ b/src/Symfony/Component/Notifier/Bridge/Expo/ExpoTransport.php
@@ -50,6 +50,9 @@ public function supports(MessageInterface $message): bool
return $message instanceof PushMessage;
}
+ /**
+ * @see https://docs.expo.dev/push-notifications/sending-notifications/#http2-api
+ */
protected function doSend(MessageInterface $message): SentMessage
{
if (!$message instanceof PushMessage) {
@@ -91,10 +94,14 @@ protected function doSend(MessageInterface $message): SentMessage
throw new TransportException('Unable to post the Expo message: '.$errorMessage, $response);
}
- $success = $response->toArray(false);
+ $result = $response->toArray(false);
+
+ if ('error' === $result['data']['status']) {
+ throw new TransportException(sprintf('Unable to post the Expo message: "%s" (%s)', $result['data']['message'], $result['data']['details']['error']), $response);
+ }
$sentMessage = new SentMessage($message, (string) $this);
- $sentMessage->setMessageId($success['data']['id']);
+ $sentMessage->setMessageId($result['data']['id']);
return $sentMessage;
}
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index 9241d2c93bf45..bdb2172f9633e 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -218,7 +218,7 @@ public function isReadable(string $class, string $property, array $context = [])
*/
public function isWritable(string $class, string $property, array $context = []): ?bool
{
- if ($this->isAllowedProperty($class, $property)) {
+ if ($this->isAllowedProperty($class, $property, true)) {
return true;
}
@@ -608,11 +608,15 @@ private function isNullableProperty(string $class, string $property): bool
return false;
}
- private function isAllowedProperty(string $class, string $property): bool
+ private function isAllowedProperty(string $class, string $property, bool $writeAccessRequired = false): bool
{
try {
$reflectionProperty = new \ReflectionProperty($class, $property);
+ if (\PHP_VERSION_ID >= 80100 && $writeAccessRequired && $reflectionProperty->isReadOnly()) {
+ return false;
+ }
+
return (bool) ($reflectionProperty->getModifiers() & $this->propertyReflectionFlags);
} catch (\ReflectionException $e) {
// Return false if the property doesn't exist
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
index 900f27fe919a2..e17e203c1523c 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
@@ -26,6 +26,7 @@
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php74Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php7ParentDummy;
+use Symfony\Component\PropertyInfo\Tests\Fixtures\Php81Dummy;
use Symfony\Component\PropertyInfo\Type;
/**
@@ -267,6 +268,7 @@ public function php71TypesProvider()
/**
* @dataProvider php80TypesProvider
+ *
* @requires PHP 8
*/
public function testExtractPhp80Type($property, array $type = null)
@@ -290,6 +292,7 @@ public function php80TypesProvider()
/**
* @dataProvider php81TypesProvider
+ *
* @requires PHP 8.1
*/
public function testExtractPhp81Type($property, array $type = null)
@@ -305,8 +308,17 @@ public function php81TypesProvider()
];
}
+ /**
+ * @requires PHP 8.1
+ */
+ public function testReadonlyPropertiesAreNotWriteable()
+ {
+ $this->assertFalse($this->extractor->isWritable(Php81Dummy::class, 'foo'));
+ }
+
/**
* @dataProvider php82TypesProvider
+ *
* @requires PHP 8.2
*/
public function testExtractPhp82Type($property, array $type = null)
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php81Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php81Dummy.php
index 842f59fbfd47b..13713b69dde03 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php81Dummy.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php81Dummy.php
@@ -13,6 +13,10 @@
class Php81Dummy
{
+ public function __construct(public readonly string $foo)
+ {
+ }
+
public function getNothing(): never
{
throw new \Exception('Oops');
diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf
index 89ca44fa88f26..1c8e065d71d70 100644
--- a/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf
+++ b/src/Symfony/Component/Security/Core/Resources/translations/security.nn.xlf
@@ -70,6 +70,14 @@
Invalid or expired login link.Innloggingslenka er ugyldig eller utgjengen.
+
+ Too many failed login attempts, please try again in %minutes% minute.
+ For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutt.
+
+
+ Too many failed login attempts, please try again in %minutes% minutes.
+ For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutter.
+