diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 853399385adc0..6d5d4c2dfa0a5 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -29,9 +29,11 @@
'@Symfony' => true,
'@Symfony:risky' => true,
'protected_to_private' => false,
- 'native_constant_invocation' => ['strict' => false],
- 'nullable_type_declaration_for_default_null_value' => true,
'header_comment' => ['header' => $fileHeaderComment],
+ // TODO: Remove once the "compiler_optimized" set includes "sprintf"
+ 'native_function_invocation' => ['include' => ['@compiler_optimized', 'sprintf'], 'scope' => 'namespaced', 'strict' => true],
+ 'nullable_type_declaration' => true,
+ 'nullable_type_declaration_for_default_null_value' => true,
])
->setRiskyAllowed(true)
->setFinder(
@@ -40,29 +42,27 @@
->append([__FILE__])
->notPath('#/Fixtures/#')
->exclude([
- // directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code
- // fixture templates
- 'Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom',
- // resource templates
- 'Symfony/Bundle/FrameworkBundle/Resources/views/Form',
// explicit trigger_error tests
'Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/',
'Symfony/Component/Intl/Resources/data/',
])
+ // explicit tests for ommited @param type, against `no_superfluous_phpdoc_tags`
+ ->notPath('Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php')
+ ->notPath('Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php')
// Support for older PHPunit version
->notPath('Symfony/Bridge/PhpUnit/SymfonyTestsListener.php')
->notPath('#Symfony/Bridge/PhpUnit/.*Mock\.php#')
->notPath('#Symfony/Bridge/PhpUnit/.*Legacy#')
// file content autogenerated by `var_export`
->notPath('Symfony/Component/Translation/Tests/fixtures/resources.php')
- // file content autogenerated by `VarExporter::export`
- ->notPath('Symfony/Component/Serializer/Tests/Fixtures/serializer.class.metadata.php')
- // test template
- ->notPath('Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php')
// explicit trigger_error tests
->notPath('Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php')
// stop removing spaces on the end of the line in strings
->notPath('Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php')
+ // svg
+ ->notPath('Symfony/Component/ErrorHandler/Resources/assets/images/symfony-ghost.svg.php')
+ // HTML templates
+ ->notPath('#Symfony/.*\.html\.php#')
)
->setCacheFile('.php-cs-fixer.cache')
;
diff --git a/CHANGELOG-5.4.md b/CHANGELOG-5.4.md
index 3e2bcd6d8360d..c160dd4fef756 100644
--- a/CHANGELOG-5.4.md
+++ b/CHANGELOG-5.4.md
@@ -7,6 +7,31 @@ 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.41 (2024-06-28)
+
+ * bug #57497 [String] Fixed u()->snake(), b()->snake() and s()->snake() methods (arczinosek)
+ * bug #57574 [Filesystem] Fix Filesystem::remove() on Windows (nicolas-grekas)
+ * bug #57572 [DoctrineBridge] Fix compat with DI >= 6.4 (nicolas-grekas)
+ * bug #57538 [String] Add `alias` case to `EnglishInflector` (alexandre-daubois)
+ * feature #57557 Ibexa is sponsoring Symfony 5.4, thanks to them! \o/ (nicolas-grekas)
+ * bug #57569 [HttpClient][Mailer] Revert "Let curl handle transfer encoding", use HTTP/1.1 for Mailgun (nicolas-grekas)
+ * bug #57453 [HttpClient] Fix parsing SSE (fancyweb)
+ * bug #57467 [SecurityBundle] Add `provider` XML attribute to the authenticators it’s missing from (MatTheCat)
+ * bug #57384 [Notifier] Fix thread key in GoogleChat bridge (romain-jacquart)
+ * bug #57372 [HttpKernel][Security] Fix accessing session for stateless request (VincentLanglet)
+ * bug #57112 [Messenger] Handle `AMQPConnectionException` when publishing a message (jwage)
+ * bug #57341 [Serializer] properly handle invalid data for false/true types (xabbuh)
+ * bug #57187 [Serializer] Fix `ObjectNormalizer` with property path (HypeMC)
+ * bug #57355 [ErrorHandler] Fix rendered exception code highlighting on PHP 8.3 (tscni)
+ * bug #57273 [FrameworkBundle] Fix setting default context for certain normalizers (HypeMC)
+ * bug #52699 [Serializer] [PropertyAccessor] Ignore non-collection interface generics (mtarld)
+ * bug #54634 [String] Fix #54611 pluralization of -on ending words + singularization of -a ending foreign words (Geordie, DesLynx)
+ * bug #57213 [Validator] [UniqueValidator] Use correct variable as parameter in (custom) error message (seho-nl, Sebastien Hoek)
+ * bug #54920 [Messenger] Comply with Amazon SQS requirements for message body (VincentLanglet)
+ * bug #57110 [PhpUnitBridge] Fix error handler triggered outside of tests (HypeMC)
+ * bug #57297 [FrameworkBundle] not registered definitions must not be modified (xabbuh)
+ * bug #57234 [String] Fix Inflector for 'hardware' (podhy)
+
* 5.4.40 (2024-06-02)
* bug #57275 Fix autoload configs to avoid warnings when building optimized autoloaders (Seldaek)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 92dac23ccbd1c..8542f255df6a1 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -33,9 +33,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Hugo Hamon (hhamon)
- Tobias Nyholm (tobias)
- Jérôme Tamarelle (gromnan)
+ - HypeMC (hypemc)
- Samuel ROZE (sroze)
- Antoine Lamirault (alamirault)
- - HypeMC (hypemc)
- Pascal Borreli (pborreli)
- Romain Neutron
- Joseph Bielawski (stloyd)
@@ -54,8 +54,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Gabriel Ostrolucký (gadelat)
- Matthias Pigulla (mpdude)
- Jonathan Wage (jwage)
- - Valentin Udaltsov (vudaltsov)
- Vincent Langlet (deviling)
+ - Valentin Udaltsov (vudaltsov)
- Alexandre Salomé (alexandresalome)
- Grégoire Paris (greg0ire)
- William DURAND
@@ -72,11 +72,11 @@ The Symfony Connect username in parenthesis allows to get more information
- Pierre du Plessis (pierredup)
- David Maicher (dmaicher)
- Bulat Shakirzyanov (avalanche123)
+ - Mathias Arlaud (mtarld)
- Iltar van der Berg
- Miha Vrhovnik (mvrhov)
- Tomasz Kowalczyk (thunderer)
- Gary PEGEOT (gary-p)
- - Mathias Arlaud (mtarld)
- Saša Stamenković (umpirsky)
- Allison Guilhem (a_guilhem)
- Mathieu Piot (mpiot)
@@ -90,9 +90,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Bilal Amarni (bamarni)
- Eriksen Costa
- Florin Patan (florinpatan)
+ - Dariusz Ruminski
- Vladimir Reznichenko (kalessil)
- Peter Rehm (rpet)
- - Dariusz Ruminski
- Henrik Bjørnskov (henrikbjorn)
- David Buchmann (dbu)
- Ruud Kamphuis (ruudk)
@@ -107,16 +107,16 @@ The Symfony Connect username in parenthesis allows to get more information
- Douglas Greenshields (shieldo)
- Frank A. Fiebig (fafiebig)
- Baldini
+ - Tomas Norkūnas (norkunas)
- Alex Pott
- Fran Moreno (franmomu)
- Charles Sarrazin (csarrazi)
- - Tomas Norkūnas (norkunas)
- Henrik Westphal (snc)
- Dariusz Górecki (canni)
- - Ener-Getick
- Hubert Lenoir (hubert_lenoir)
- - Graham Campbell (graham)
+ - Ener-Getick
- Antoine Makdessi (amakdessi)
+ - Graham Campbell (graham)
- Tugdual Saunier (tucksaun)
- Lee McDermott
- Brandon Turner
@@ -171,17 +171,17 @@ The Symfony Connect username in parenthesis allows to get more information
- Gordon Franke (gimler)
- Malte Schlüter (maltemaltesich)
- jeremyFreeAgent (jeremyfreeagent)
+ - Michael Babker (mbabker)
+ - Maximilian Beckers (maxbeckers)
+ - Valtteri R (valtzu)
- Joshua Thijssen
- Vasilij Dusko
- Daniel Wehner (dawehner)
- Maxime Helias (maxhelias)
- Robert Schönthal (digitalkaoz)
- Smaine Milianni (ismail1432)
- - Michael Babker (mbabker)
- François-Xavier de Guillebon (de-gui_f)
- - Maximilian Beckers (maxbeckers)
- noniagriconomie
- - Valtteri R (valtzu)
- Eric GELOEN (gelo)
- Gabriel Caruso
- Stefano Sala (stefano.sala)
@@ -405,6 +405,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Danny Berger (dpb587)
- Alif Rachmawadi
- Anton Chernikov (anton_ch1989)
+ - Stiven Llupa (sllupa)
- Pierre-Yves Lebecq (pylebecq)
- Benjamin Leveque (benji07)
- Jordan Samouh (jordansamouh)
@@ -438,6 +439,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Dane Powell
- Sebastien Morel (plopix)
- Christopher Davis (chrisguitarguy)
+ - Jonathan H. Wage
- Loïc Frémont (loic425)
- Matthieu Auger (matthieuauger)
- Sergey Belyshkin (sbelyshkin)
@@ -468,11 +470,11 @@ The Symfony Connect username in parenthesis allows to get more information
- Wouter Van Hecke
- Baptiste Lafontaine (magnetik)
- Iker Ibarguren (ikerib)
+ - Michael Hirschler (mvhirsch)
- Michael Holm (hollo)
- Blanchon Vincent (blanchonvincent)
- Christian Schmidt
- Ben Hakim
- - Stiven Llupa (sllupa)
- Marco Petersen (ocrampete16)
- Bohan Yang (brentybh)
- Vilius Grigaliūnas
@@ -520,7 +522,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Thierry T (lepiaf)
- Lorenz Schori
- Lukáš Holeczy (holicz)
- - Jonathan H. Wage
- Jeremy Livingston (jeremylivingston)
- ivan
- SUMIDA, Ippei (ippey_s)
@@ -541,7 +542,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Francesco Levorato
- Vitaliy Zakharov (zakharovvi)
- Tobias Sjösten (tobiassjosten)
- - Michael Hirschler (mvhirsch)
- Gyula Sallai (salla)
- Hendrik Luup (hluup)
- Inal DJAFAR (inalgnu)
@@ -582,6 +582,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Sanpi (sanpi)
- Eduardo Gulias (egulias)
- giulio de donato (liuggio)
+ - Ivan Mezinov
- ShinDarth
- Stéphane PY (steph_py)
- Cătălin Dan (dancatalin)
@@ -649,6 +650,7 @@ The Symfony Connect username in parenthesis allows to get more information
- quentin neyrat (qneyrat)
- Chris Tanaskoski (devristo)
- Steffen Roßkamp
+ - Andrey Lebedev (alebedev)
- Alexandru Furculita (afurculita)
- Michel Salib (michelsalib)
- Ben Roberts (benr77)
@@ -750,7 +752,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Roberto Espinoza (respinoza)
- Pierre Rineau
- Soufian EZ ZANTAR (soezz)
- - Ivan Mezinov
- Marek Zajac
- Adam Harvey
- ilyes kooli (skafandri)
@@ -764,6 +765,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Patrick Reimers (preimers)
- Brayden Williams (redstar504)
- insekticid
+ - Kieran Brahney
- Jérémy M (th3mouk)
- Trent Steel (trsteel88)
- boombatower
@@ -785,6 +787,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Matthew Grasmick
- Miroslav Šustek (sustmi)
- Pablo Díez (pablodip)
+ - Kev
- Kevin McBride
- Sergio Santoro
- Philipp Rieber (bicpi)
@@ -909,7 +912,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Ramunas Pabreza (doobas)
- Yuriy Vilks (igrizzli)
- Terje Bråten
- - Andrey Lebedev (alebedev)
- Sebastian Krebs
- Piotr Stankowski
- Pierre-Emmanuel Tanguy (petanguy)
@@ -1011,6 +1013,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Aurimas Niekis (gcds)
- Vincent Chalamon
- Matthieu Calie (matth--)
+ - Sem Schidler (xvilo)
- Benjamin Schoch (bschoch)
- Martins Sipenko
- Guilherme Augusto Henschel
@@ -1057,6 +1060,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ivan Grigoriev (greedyivan)
- Johann Saunier (prophet777)
- Kevin SCHNEKENBURGER
+ - Geordie
- Fabien Salles (blacked)
- Andreas Erhard (andaris)
- alexpozzi
@@ -1135,11 +1139,11 @@ The Symfony Connect username in parenthesis allows to get more information
- Aleksandr Volochnev (exelenz)
- Robin van der Vleuten (robinvdvleuten)
- Grinbergs Reinis (shima5)
- - Kieran Brahney
- Klaus Silveira (klaussilveira)
- Michael Piecko (michael.piecko)
- Toni Peric (tperic)
- yclian
+ - Nicolas DOUSSON
- radar3301
- Aleksey Prilipko
- Jelle Raaijmakers (gmta)
@@ -1148,7 +1152,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Wybren Koelmans (wybren_koelmans)
- Roberto Nygaard
- victor-prdh
- - Kev
- Davide Borsatto (davide.borsatto)
- Florian Hermann (fhermann)
- zenas1210
@@ -1182,6 +1185,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Mathias Brodala (mbrodala)
- Robert Fischer (sandoba)
- Tarjei Huse (tarjei)
+ - Travis Carden (traviscarden)
- mfettig
- Besnik Br
- Issam Raouf (iraouf)
@@ -1243,6 +1247,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Volodymyr Panivko
- kick-the-bucket
- fedor.f
+ - roman joly (eltharin)
- Yosmany Garcia (yosmanyga)
- Jeremiasz Major
- Jibé Barth (jibbarth)
@@ -1262,6 +1267,7 @@ The Symfony Connect username in parenthesis allows to get more information
- possum
- Denis Zunke (donalberto)
- Adrien Roches (neirda24)
+ - Thomas Trautner (thomastr)
- _sir_kane (waly)
- Olivier Maisonneuve
- Gálik Pál
@@ -1292,6 +1298,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Zhuravlev Alexander (scif)
- Stefano Degenkamp (steef)
- James Michael DuPont
+ - Tinjo Schöni
- Carlos Buenosvinos (carlosbuenosvinos)
- Christian Gripp (core23)
- Jake (jakesoft)
@@ -1350,6 +1357,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ana Raro
- Ana Raro
- Tony Malzhacker
+ - Cosmin Sandu
- Andreas Lutro (anlutro)
- DUPUCH (bdupuch)
- Cyril Quintin (cyqui)
@@ -1399,6 +1407,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Matthieu Mota (matthieumota)
- Maxime Pinot (maximepinot)
- Jean-Baptiste GOMOND (mjbgo)
+ - Jakub Podhorsky (podhy)
- abdul malik ikhsan (samsonasik)
- Henry Snoek (snoek09)
- Morgan Auchede
@@ -1430,6 +1439,7 @@ 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
@@ -1470,6 +1480,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Nicolas de Marqué (nicola)
- Thiago Cordeiro (thiagocordeiro)
- Matthieu Bontemps
+ - Ian Irlen
- Rootie
- Sébastien Santoro (dereckson)
- Daniel Alejandro Castro Arellano (lexcast)
@@ -1671,6 +1682,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Bruno Rodrigues de Araujo (brunosinister)
- Máximo Cuadros (mcuadros)
- Jacek Wilczyński (jacekwilczynski)
+ - Christoph Kappestein
- Camille Baronnet
- EXT - THERAGE Kevin
- tamirvs
@@ -1832,11 +1844,11 @@ The Symfony Connect username in parenthesis allows to get more information
- Peter Smeets (darkspartan)
- Julien Bianchi (jubianchi)
- Michael Dawart (mdawart)
- - Sem Schidler (xvilo)
- Robert Meijers
- Tijs Verkoyen
- James Sansbury
- Marcin Chwedziak
+ - Dan Kadera
- hjkl
- Dan Wilga
- Thijs Reijgersberg
@@ -2271,7 +2283,6 @@ The Symfony Connect username in parenthesis allows to get more information
- DidierLmn
- Pedro Silva
- Chihiro Adachi (chihiro-adachi)
- - Thomas Trautner (thomastr)
- Jeroen de Graaf
- Ulrik McArdle
- BiaDd
@@ -2292,6 +2303,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Fabian Haase
- roog
- parinz1234
+ - seho-nl
- Romain Geissler
- Martin Auswöger
- Adrien Moiruad
@@ -2396,6 +2408,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Raphael Hardt
- Ivan Nemets
- Qingshan Luo
+ - Michael Olšavský
- Ergie Gonzaga
- Matthew J Mucklo
- AnrDaemon
@@ -2501,6 +2514,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Nicolas Badey (nico-b)
- Olivier Scherler (oscherler)
- Flo Gleixner (redflo)
+ - Romain Jacquart (romainjacquart)
- Shane Preece (shane)
- Stephan Wentz (temp)
- Johannes Goslar
@@ -2680,7 +2694,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Flinsch
- Maciej Schmidt
- botbotbot
- - Cosmin Sandu
- tatankat
- Cláudio Cesar
- Timon van der Vorm
@@ -2846,7 +2859,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Boudry Julien
- amcastror
- Bram Van der Sype (brammm)
- - roman joly (eltharin)
- Guile (guile)
- Mark Beech (jaybizzle)
- Julien Moulin (lizjulien)
@@ -2907,6 +2919,7 @@ The Symfony Connect username in parenthesis allows to get more information
- J Bruni
- Alexey Prilipko
- vlakoff
+ - Anthony Tenneriello
- thib92
- Yiorgos Kalligeros
- Rudolf Ratusiński
@@ -2923,6 +2936,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Frédéric G. Marand (fgm)
- Freek Van der Herten (freekmurze)
- Luca Genuzio (genuzio)
+ - Ben Gamra Housseine (hbgamra)
- Andrew Marcinkevičius (ifdattic)
- Ioana Hazsda (ioana-hazsda)
- Jan Marek (janmarek)
@@ -3175,13 +3189,11 @@ The Symfony Connect username in parenthesis allows to get more information
- Olivier Laviale (olvlvl)
- Pierre Gasté (pierre_g)
- Pablo Monterde Perez (plebs)
- - Jakub Podhorsky (podhy)
- Pierre-Olivier Vares (povares)
- Jimmy Leger (redpanda)
- Ronny López (ronnylt)
- Julius (sakalys)
- Sébastien JEAN (sebastien76)
- - Mokhtar Tlili (sf-djuba)
- Dmitry (staratel)
- Marcin Szepczynski (szepczynski)
- Tito Miguel Costa (titomiguelcosta)
@@ -3193,7 +3205,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Yorkie Chadwick (yorkie76)
- Zakaria AMMOURA (zakariaamm)
- Maxime Aknin (3m1x4m)
- - Geordie
- Pavel Barton
- Exploit.cz
- GuillaumeVerdon
@@ -3203,7 +3214,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Youpie
- Jason Stephens
- srsbiz
- - Tinjo Schöni
- Taylan Kasap
- Michael Orlitzky
- Nicolas A. Bérard-Nault
diff --git a/README.md b/README.md
index 7c4f1a85899bb..8ef86422a84ae 100644
--- a/README.md
+++ b/README.md
@@ -17,13 +17,17 @@ Installation
Sponsor
-------
-Symfony 5.4 is [backed][27] by [Private Packagist][28].
+Symfony 5.4 is [backed][27] by [Private Packagist][28] and [Ibexa][29].
Private Packagist is a fast, reliable, and secure Composer repository for your
private packages. It mirrors all your open-source dependencies for better
availability and monitors them for security vulnerabilities.
-Help Symfony by [sponsoring][29] its development!
+Ibexa is the leading DXP for Symfony developers. Ibexa DXP is used across the
+world by thousands of websites/shops/portals and supported by a fantastic,
+passionate community of developers, agencies, and users. They love Symfony!
+
+Help Symfony by [sponsoring][30] its development!
Documentation
-------------
@@ -87,4 +91,5 @@ and supported by [Symfony contributors][19].
[26]: https://symfony.com/book
[27]: https://symfony.com/backers
[28]: https://packagist.com/
-[29]: https://symfony.com/sponsor
+[29]: https://ibexa.co/
+[30]: https://symfony.com/sponsor
diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php
index c3d48fc558518..b290ae5d9b039 100644
--- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php
+++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php
@@ -72,6 +72,8 @@ function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) {
}
if (isset($this->fileMap[$name])) {
$wrappedInstance = $this->load($this->fileMap[$name], false);
+ } elseif ((new \ReflectionMethod($this, $this->methodMap[$name]))->isStatic()) {
+ $wrappedInstance = $this->{$this->methodMap[$name]}($this, false);
} else {
$wrappedInstance = $this->{$this->methodMap[$name]}(false);
}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php
new file mode 100644
index 0000000000000..04e5a2acdd334
--- /dev/null
+++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php
@@ -0,0 +1,69 @@
+testDumpContainerWithProxyServiceWillShareProxies();
+ $container = new ContainerBuilder();
+
+ $container->register('foo', DummyManager::class)->setPublic(true);
+ $container->getDefinition('foo')->setLazy(true);
+ $container->compile();
+
+ $dumper = new PhpDumper($container);
+ $dumper->setProxyDumper(new ProxyDumper());
+ eval('?>'.$dumper->dump(['class' => 'LazyServiceDoctrineBridgeContainer']));
}
public function testResetService()
{
- $container = new \LazyServiceProjectServiceContainer();
+ $container = new \LazyServiceDoctrineBridgeContainer();
$registry = new TestManagerRegistry('name', [], ['defaultManager' => 'foo'], 'defaultConnection', 'defaultManager', 'proxyInterfaceName');
$registry->setTestContainer($container);
@@ -44,8 +51,8 @@ public function testResetService()
$registry->resetManager();
$this->assertSame($foo, $container->get('foo'));
- $this->assertInstanceOf(\stdClass::class, $foo);
- $this->assertFalse(property_exists($foo, 'bar'));
+ $this->assertInstanceOf(DummyManager::class, $foo);
+ $this->assertFalse(isset($foo->bar));
}
/**
@@ -77,7 +84,7 @@ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther()
$service = $container->get('foo');
- self::assertInstanceOf(\stdClass::class, $service);
+ self::assertInstanceOf(DummyManager::class, $service);
self::assertInstanceOf(LazyLoadingInterface::class, $service);
self::assertInstanceOf(ValueHolderInterface::class, $service);
self::assertFalse($service->isProxyInitialized());
@@ -91,7 +98,7 @@ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther()
$service->initializeProxy();
$wrappedValue = $service->getWrappedValueHolderValue();
- self::assertInstanceOf(\stdClass::class, $wrappedValue);
+ self::assertInstanceOf(DummyManager::class, $wrappedValue);
self::assertNotInstanceOf(LazyLoadingInterface::class, $wrappedValue);
self::assertNotInstanceOf(ValueHolderInterface::class, $wrappedValue);
}
@@ -104,7 +111,7 @@ private function dumpLazyServiceProjectAsFilesServiceContainer()
$container = new ContainerBuilder();
- $container->register('foo', \stdClass::class)
+ $container->register('foo', DummyManager::class)
->setPublic(true)
->setLazy(true);
$container->compile();
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php
index 2c9c3815654ba..5d9edce2408c2 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php
@@ -63,6 +63,13 @@ public function testAttributeWithGroupsAndPaylod()
self::assertSame('some attached data', $constraint->payload);
self::assertSame(['some_group'], $constraint->groups);
}
+
+ public function testValueOptionConfiguresFields()
+ {
+ $constraint = new UniqueEntity(['value' => 'email']);
+
+ $this->assertSame('email', $constraint->fields);
+ }
}
#[UniqueEntity(['email'], message: 'myMessage')]
diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
index 05c67b7b37e6e..2821d92e358f4 100644
--- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
+++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
@@ -11,6 +11,7 @@
namespace Symfony\Bridge\PhpUnit;
+use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestResult;
use PHPUnit\Runner\ErrorHandler;
use PHPUnit\Util\Error\Handler;
@@ -368,22 +369,26 @@ private static function getPhpUnitErrorHandler()
if ('PHPUnit\Util\ErrorHandler::handleError' === $eh) {
return $eh;
- } elseif (ErrorHandler::class === $eh) {
- return function (int $errorNumber, string $errorString, string $errorFile, int $errorLine) {
- ErrorHandler::instance()($errorNumber, $errorString, $errorFile, $errorLine);
-
- return true;
- };
}
foreach (debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
- if (isset($frame['object']) && $frame['object'] instanceof TestResult) {
+ if (!isset($frame['object'])) {
+ continue;
+ }
+
+ if ($frame['object'] instanceof TestResult) {
return new $eh(
$frame['object']->getConvertDeprecationsToExceptions(),
$frame['object']->getConvertErrorsToExceptions(),
$frame['object']->getConvertNoticesToExceptions(),
$frame['object']->getConvertWarningsToExceptions()
);
+ } elseif (ErrorHandler::class === $eh && $frame['object'] instanceof TestCase) {
+ return function (int $errorNumber, string $errorString, string $errorFile, int $errorLine) {
+ ErrorHandler::instance()($errorNumber, $errorString, $errorFile, $errorLine);
+
+ return true;
+ };
}
}
diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
index d76924633efe0..e7a3329478d98 100644
--- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
+++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
@@ -129,12 +129,10 @@ public function fileExcerpt(string $file, int $line, int $srcContext = 3): ?stri
if (\PHP_VERSION_ID >= 80300) {
// remove main pre/code tags
$code = preg_replace('#^
\s*(.*)\s*#s', '\\1', $code);
- // split multiline code tags
- $code = preg_replace_callback('#]++)>((?:[^<]*+\\n)++[^<]*+)#', function ($m) {
- return "".str_replace("\n", "\n", $m[2]).'';
+ // split multiline span tags
+ $code = preg_replace_callback('#]++)>((?:[^<\\n]*+\\n)++[^<]*+)#', function ($m) {
+ return "".str_replace("\n", "\n", $m[2]).'';
}, $code);
- // Convert spaces to html entities to preserve indentation when rendered
- $code = str_replace(' ', ' ', $code);
$content = explode("\n", $code);
} else {
// remove main code/span tags
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index f7ab7e3ed5835..71505f2519340 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -1854,19 +1854,25 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
$container->setParameter('serializer.default_context', $defaultContext);
}
+ if (!$container->hasDefinition('serializer.normalizer.object')) {
+ return;
+ }
+
$arguments = $container->getDefinition('serializer.normalizer.object')->getArguments();
- $context = [];
+ $context = $arguments[6] ?? $defaultContext;
if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) {
- $context += ($arguments[6] ?? $defaultContext) + ['circular_reference_handler' => new Reference($config['circular_reference_handler'])];
+ $context += ['circular_reference_handler' => new Reference($config['circular_reference_handler'])];
$container->getDefinition('serializer.normalizer.object')->setArgument(5, null);
}
if ($config['max_depth_handler'] ?? false) {
- $context += ($arguments[6] ?? $defaultContext) + ['max_depth_handler' => new Reference($config['max_depth_handler'])];
+ $context += ['max_depth_handler' => new Reference($config['max_depth_handler'])];
}
$container->getDefinition('serializer.normalizer.object')->setArgument(6, $context);
+
+ $container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext);
}
private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader)
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php
index 7762e5a64ca86..2fb42027fd61e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php
@@ -139,7 +139,6 @@
service('property_info')->ignoreOnInvalid(),
service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(),
null,
- [],
])
->alias(PropertyNormalizer::class, 'serializer.normalizer.property')
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php
index bce53b8668251..30ca91d1ee5b7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php
@@ -52,7 +52,7 @@ protected static function getKernelClass(): string
protected static function createKernel(array $options = []): KernelInterface
{
- $class = self::getKernelClass();
+ $class = static::getKernelClass();
if (!isset($options['test_case'])) {
throw new \InvalidArgumentException('The option "test_case" must be set.');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php
index 9a6527b14dd62..7ce9b0735e134 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php
@@ -11,6 +11,10 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
+use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\AppKernel;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
/**
* @author Kévin Dunglas
*/
@@ -33,39 +37,55 @@ public function testDeserializeArrayOfObject()
$this->assertEquals($expected, $result);
}
- /**
- * @dataProvider provideNormalizersAndEncodersWithDefaultContextOption
- */
- public function testNormalizersAndEncodersUseDefaultContextConfigOption(string $normalizerId)
+ public function testNormalizersAndEncodersUseDefaultContextConfigOption()
{
- static::bootKernel(['test_case' => 'Serializer']);
+ /** @var SerializerKernel $kernel */
+ $kernel = static::bootKernel(['test_case' => 'Serializer', 'root_config' => 'default_context.yaml']);
+
+ foreach ($kernel->normalizersAndEncoders as $normalizerOrEncoderId) {
+ $normalizerOrEncoder = static::getContainer()->get($normalizerOrEncoderId);
- $normalizer = static::getContainer()->get($normalizerId);
+ $reflectionObject = new \ReflectionObject($normalizerOrEncoder);
+ $property = $reflectionObject->getProperty('defaultContext');
+ $property->setAccessible(true);
- $reflectionObject = new \ReflectionObject($normalizer);
- $property = $reflectionObject->getProperty('defaultContext');
- $property->setAccessible(true);
+ $defaultContext = $property->getValue($normalizerOrEncoder);
- $defaultContext = $property->getValue($normalizer);
+ self::assertArrayHasKey('fake_context_option', $defaultContext);
+ self::assertEquals('foo', $defaultContext['fake_context_option']);
+ }
+ }
- self::assertArrayHasKey('fake_context_option', $defaultContext);
- self::assertEquals('foo', $defaultContext['fake_context_option']);
+ protected static function getKernelClass(): string
+ {
+ return SerializerKernel::class;
}
+}
+
+class SerializerKernel extends AppKernel implements CompilerPassInterface
+{
+ public $normalizersAndEncoders = [
+ 'serializer.normalizer.property.alias', // Special case as this normalizer isn't tagged
+ ];
- public static function provideNormalizersAndEncodersWithDefaultContextOption(): array
+ public function process(ContainerBuilder $container)
{
- return [
- ['serializer.normalizer.constraint_violation_list.alias'],
- ['serializer.normalizer.dateinterval.alias'],
- ['serializer.normalizer.datetime.alias'],
- ['serializer.normalizer.json_serializable.alias'],
- ['serializer.normalizer.problem.alias'],
- ['serializer.normalizer.uid.alias'],
- ['serializer.normalizer.object.alias'],
- ['serializer.encoder.xml.alias'],
- ['serializer.encoder.yaml.alias'],
- ['serializer.encoder.csv.alias'],
- ];
+ $services = array_merge(
+ $container->findTaggedServiceIds('serializer.normalizer'),
+ $container->findTaggedServiceIds('serializer.encoder')
+ );
+ foreach ($services as $serviceId => $attributes) {
+ $class = $container->getDefinition($serviceId)->getClass();
+ if (null === $reflectionConstructor = (new \ReflectionClass($class))->getConstructor()) {
+ continue;
+ }
+ foreach ($reflectionConstructor->getParameters() as $reflectionParam) {
+ if ('array $defaultContext' === $reflectionParam->getType()->getName().' $'.$reflectionParam->getName()) {
+ $this->normalizersAndEncoders[] = $serviceId.'.alias';
+ break;
+ }
+ }
+ }
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php
index 49fb0ca2e6f8d..0c3c9cc3564e4 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php
@@ -49,6 +49,11 @@ public function __construct($varDir, $testCase, $rootConfig, $environment, $debu
parent::__construct($environment, $debug);
}
+ protected function getContainerClass(): string
+ {
+ return parent::getContainerClass().substr(md5($this->rootConfig), -16);
+ }
+
public function registerBundles(): iterable
{
if (!file_exists($filename = $this->getProjectDir().'/'.$this->testCase.'/bundles.php')) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml
index f023f6341970d..c22edfccef331 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml
@@ -8,7 +8,6 @@ framework:
max_depth_handler: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\MaxDepthHandler
default_context:
enable_max_depth: true
- fake_context_option: foo
property_info: { enabled: true }
services:
@@ -16,50 +15,6 @@ services:
alias: serializer
public: true
- serializer.normalizer.constraint_violation_list.alias:
- alias: serializer.normalizer.constraint_violation_list
- public: true
-
- serializer.normalizer.dateinterval.alias:
- alias: serializer.normalizer.dateinterval
- public: true
-
- serializer.normalizer.datetime.alias:
- alias: serializer.normalizer.datetime
- public: true
-
- serializer.normalizer.json_serializable.alias:
- alias: serializer.normalizer.json_serializable
- public: true
-
- serializer.normalizer.problem.alias:
- alias: serializer.normalizer.problem
- public: true
-
- serializer.normalizer.uid.alias:
- alias: serializer.normalizer.uid
- public: true
-
- serializer.normalizer.property.alias:
- alias: serializer.normalizer.property
- public: true
-
- serializer.normalizer.object.alias:
- alias: serializer.normalizer.object
- public: true
-
- serializer.encoder.xml.alias:
- alias: serializer.encoder.xml
- public: true
-
- serializer.encoder.yaml.alias:
- alias: serializer.encoder.yaml
- public: true
-
- serializer.encoder.csv.alias:
- alias: serializer.encoder.csv
- public: true
-
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\CircularReferenceHandler: ~
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\MaxDepthHandler: ~
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/default_context.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/default_context.yaml
new file mode 100644
index 0000000000000..de6114c5d4bb8
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/default_context.yaml
@@ -0,0 +1,59 @@
+imports:
+ - { resource: ../config/default.yml }
+
+framework:
+ serializer:
+ enabled: true
+ circular_reference_handler: ~ # This must be null
+ max_depth_handler: ~ # This must be null
+ default_context:
+ fake_context_option: foo
+
+services:
+ serializer.normalizer.constraint_violation_list.alias:
+ alias: serializer.normalizer.constraint_violation_list
+ public: true
+
+ serializer.normalizer.dateinterval.alias:
+ alias: serializer.normalizer.dateinterval
+ public: true
+
+ serializer.normalizer.datetime.alias:
+ alias: serializer.normalizer.datetime
+ public: true
+
+ serializer.normalizer.json_serializable.alias:
+ alias: serializer.normalizer.json_serializable
+ public: true
+
+ serializer.normalizer.object.alias:
+ alias: serializer.normalizer.object
+ public: true
+
+ serializer.normalizer.problem.alias:
+ alias: serializer.normalizer.problem
+ public: true
+
+ serializer.normalizer.property.alias:
+ alias: serializer.normalizer.property
+ public: true
+
+ serializer.normalizer.uid.alias:
+ alias: serializer.normalizer.uid
+ public: true
+
+ serializer.encoder.csv.alias:
+ alias: serializer.encoder.csv
+ public: true
+
+ serializer.encoder.json.alias:
+ alias: serializer.encoder.json
+ public: true
+
+ serializer.encoder.xml.alias:
+ alias: serializer.encoder.xml
+ public: true
+
+ serializer.encoder.yaml.alias:
+ alias: serializer.encoder.yaml
+ public: true
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd
index 70b682e4065ca..1a367b8397213 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd
@@ -230,6 +230,7 @@
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php
index d79d0b7a1df53..a04101626c916 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php
@@ -41,7 +41,7 @@ private function getForFirewall(): object
if (!$this->locator->has($firewallName)) {
$message = 'No '.$serviceIdentifier.' found for this firewall.';
if (\defined(static::class.'::FIREWALL_OPTION')) {
- $message .= sprintf('Did you forget to add a "'.static::FIREWALL_OPTION.'" key under your "%s" firewall?', $firewallName);
+ $message .= sprintf(' Did you forget to add a "'.static::FIREWALL_OPTION.'" key under your "%s" firewall?', $firewallName);
}
throw new \LogicException($message);
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml
index c97dd5bf7ebf0..01ecdbaecc5c4 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml
@@ -64,9 +64,8 @@
-
+
- app.user_checkerROLE_USER
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml
index 6f74984045970..66da3c4a28307 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml
index a80f613e00331..a55ffdacc2fc3 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml
index ed7afe5e833ee..15f27b4ff1351 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml
@@ -66,10 +66,9 @@
-
+
- app.user_checkerROLE_USER
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml
index a362a59a15b80..cb5c04b7f82aa 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml
@@ -66,10 +66,9 @@
-
+
- app.user_checkerROLE_USER
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml
index b45f378a5ba68..d4a6a1d41aa47 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml
index bdf9d5ec837f0..312cb803960d2 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml
index c4dea529ba452..fe81171b56977 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml
@@ -22,7 +22,6 @@
-
diff --git a/src/Symfony/Component/Console/Completion/CompletionInput.php b/src/Symfony/Component/Console/Completion/CompletionInput.php
index 368b945079484..2f631bcd8484f 100644
--- a/src/Symfony/Component/Console/Completion/CompletionInput.php
+++ b/src/Symfony/Component/Console/Completion/CompletionInput.php
@@ -53,7 +53,7 @@ public static function fromString(string $inputStr, int $currentIndex): self
* Create an input based on an COMP_WORDS token list.
*
* @param string[] $tokens the set of split tokens (e.g. COMP_WORDS or argv)
- * @param $currentIndex the index of the cursor (e.g. COMP_CWORD)
+ * @param int $currentIndex the index of the cursor (e.g. COMP_CWORD)
*/
public static function fromTokens(array $tokens, int $currentIndex): self
{
diff --git a/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php b/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php
index d98da682cd90d..65708d3ec8659 100644
--- a/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php
+++ b/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php
@@ -133,4 +133,19 @@ public static function provideFromStringData()
yield ['bin/console cache:clear "multi word string"', ['bin/console', 'cache:clear', '"multi word string"']];
yield ['bin/console cache:clear \'multi word string\'', ['bin/console', 'cache:clear', '\'multi word string\'']];
}
+
+ public function testToString()
+ {
+ $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 0);
+ $this->assertSame('foo| bar baz', (string) $input);
+
+ $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 1);
+ $this->assertSame('foo bar| baz', (string) $input);
+
+ $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 2);
+ $this->assertSame('foo bar baz|', (string) $input);
+
+ $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 11);
+ $this->assertSame('foo bar baz |', (string) $input);
+ }
}
diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
index 5b264fa5a7e90..05cbeec166b6e 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
@@ -274,12 +274,10 @@ private function fileExcerpt(string $file, int $line, int $srcContext = 3): stri
if (\PHP_VERSION_ID >= 80300) {
// remove main pre/code tags
$code = preg_replace('#^\s*(.*)\s*#s', '\\1', $code);
- // split multiline code tags
- $code = preg_replace_callback('#]++)>((?:[^<]*+\\n)++[^<]*+)#', function ($m) {
- return "".str_replace("\n", "\n", $m[2]).'';
+ // split multiline span tags
+ $code = preg_replace_callback('#]++)>((?:[^<\\n]*+\\n)++[^<]*+)#', function ($m) {
+ return "".str_replace("\n", "\n", $m[2]).'';
}, $code);
- // Convert spaces to html entities to preserve indentation when rendered
- $code = str_replace(' ', ' ', $code);
$content = explode("\n", $code);
} else {
// remove main code/span tags
diff --git a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css
index 7cb3206da2055..2d05a5e6a6620 100644
--- a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css
+++ b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css
@@ -242,7 +242,7 @@ header .container { display: flex; justify-content: space-between; }
.trace-code li { color: #969896; margin: 0; padding-left: 10px; float: left; width: 100%; }
.trace-code li + li { margin-top: 5px; }
.trace-code li.selected { background: var(--trace-selected-background); margin-top: 2px; }
-.trace-code li code { color: var(--base-6); white-space: nowrap; }
+.trace-code li code { color: var(--base-6); white-space: pre; }
.trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; }
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index 700311d5843fc..958ef178db2fb 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -173,7 +173,7 @@ private static function doRemove(array $files, bool $isRecursive): void
}
} elseif (is_dir($file)) {
if (!$isRecursive) {
- $tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-_'));
+ $tmpName = \dirname(realpath($file)).'/.!'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-!'));
if (file_exists($tmpName)) {
try {
diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
index 101f30f898714..eea5fe1a68952 100644
--- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
+++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
@@ -171,17 +171,17 @@ public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToCopy()
}
$finder = new PhpExecutableFinder();
- $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8057']));
+ $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8857']));
$process->setWorkingDirectory(__DIR__.'/Fixtures/web');
$process->start();
do {
usleep(50000);
- } while (!@fopen('http://localhost:8057', 'r'));
+ } while (!@fopen('http://localhost:8857', 'r'));
try {
- $sourceFilePath = 'http://localhost:8057/logo_symfony_header.png';
+ $sourceFilePath = 'http://localhost:8857/logo_symfony_header.png';
$targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file';
file_put_contents($targetFilePath, 'TARGET FILE');
$this->filesystem->copy($sourceFilePath, $targetFilePath, false);
diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php
index 4c5ced322d5de..3a2fba025aeff 100644
--- a/src/Symfony/Component/HttpClient/CurlHttpClient.php
+++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php
@@ -246,8 +246,9 @@ public function request(string $method, string $url, array $options = []): Respo
if (isset($options['normalized_headers']['content-length'][0])) {
$curlopts[\CURLOPT_INFILESIZE] = (int) substr($options['normalized_headers']['content-length'][0], \strlen('Content-Length: '));
- } elseif (!isset($options['normalized_headers']['transfer-encoding'])) {
- $curlopts[\CURLOPT_INFILESIZE] = -1;
+ }
+ if (!isset($options['normalized_headers']['transfer-encoding'])) {
+ $curlopts[\CURLOPT_HTTPHEADER][] = 'Transfer-Encoding:'.(isset($curlopts[\CURLOPT_INFILESIZE]) ? '' : ' chunked');
}
if ('POST' !== $method) {
diff --git a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php
index 89d12e87764fa..6626cbeba6ba5 100644
--- a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php
+++ b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\HttpClient;
+use Symfony\Component\HttpClient\Chunk\DataChunk;
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
use Symfony\Component\HttpClient\Exception\EventSourceException;
use Symfony\Component\HttpClient\Response\AsyncContext;
@@ -121,17 +122,30 @@ public function request(string $method, string $url, array $options = []): Respo
return;
}
- $rx = '/((?:\r\n){2,}|\r{2,}|\n{2,})/';
- $content = $state->buffer.$chunk->getContent();
-
if ($chunk->isLast()) {
- $rx = substr_replace($rx, '|$', -2, 0);
+ if ('' !== $content = $state->buffer) {
+ $state->buffer = '';
+ yield new DataChunk(-1, $content);
+ }
+
+ yield $chunk;
+
+ return;
}
- $events = preg_split($rx, $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
+
+ $content = $state->buffer.$chunk->getContent();
+ $events = preg_split('/((?:\r\n){2,}|\r{2,}|\n{2,})/', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
$state->buffer = array_pop($events);
for ($i = 0; isset($events[$i]); $i += 2) {
- $event = new ServerSentEvent($events[$i].$events[1 + $i]);
+ $content = $events[$i].$events[1 + $i];
+ if (!preg_match('/(?:^|\r\n|[\r\n])[^:\r\n]/', $content)) {
+ yield new DataChunk(-1, $content);
+
+ continue;
+ }
+
+ $event = new ServerSentEvent($content);
if ('' !== $event->getId()) {
$context->setInfo('last_event_id', $state->lastEventId = $event->getId());
@@ -143,17 +157,6 @@ public function request(string $method, string $url, array $options = []): Respo
yield $event;
}
-
- if (preg_match('/^(?::[^\r\n]*+(?:\r\n|[\r\n]))+$/m', $state->buffer)) {
- $content = $state->buffer;
- $state->buffer = '';
-
- yield $context->createChunk($content);
- }
-
- if ($chunk->isLast()) {
- yield $chunk;
- }
});
}
}
diff --git a/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php
index fff3a0e7c18b7..536979e864672 100644
--- a/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php
@@ -15,9 +15,11 @@
use Symfony\Component\HttpClient\Chunk\DataChunk;
use Symfony\Component\HttpClient\Chunk\ErrorChunk;
use Symfony\Component\HttpClient\Chunk\FirstChunk;
+use Symfony\Component\HttpClient\Chunk\LastChunk;
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
use Symfony\Component\HttpClient\EventSourceHttpClient;
use Symfony\Component\HttpClient\Exception\EventSourceException;
+use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\HttpClient\Response\ResponseStream;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -34,7 +36,11 @@ class EventSourceHttpClientTest extends TestCase
*/
public function testGetServerSentEvents(string $sep)
{
- $rawData = <<assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']);
+
+ return new MockResponse([
+ str_replace("\n", $sep, << false, 'http_method' => 'GET', 'url' => 'http://localhost:8080/events', 'response_headers' => ['content-type: text/event-stream']]);
- $responseStream = new ResponseStream((function () use ($response, $chunk) {
- yield $response => new FirstChunk();
- yield $response => $chunk;
- yield $response => new ErrorChunk(0, 'timeout');
- })());
-
- $hasCorrectHeaders = function ($options) {
- $this->assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']);
-
- return true;
- };
-
- $httpClient = $this->createMock(HttpClientInterface::class);
- $httpClient->method('request')->with('GET', 'http://localhost:8080/events', $this->callback($hasCorrectHeaders))->willReturn($response);
-
- $httpClient->method('stream')->willReturn($responseStream);
-
- $es = new EventSourceHttpClient($httpClient);
+TXT
+ ),
+ ], [
+ 'canceled' => false,
+ 'http_method' => 'GET',
+ 'url' => 'http://localhost:8080/events',
+ 'response_headers' => ['content-type: text/event-stream'],
+ ]);
+ }));
$res = $es->connect('http://localhost:8080/events');
$expected = [
new FirstChunk(),
new ServerSentEvent(str_replace("\n", $sep, "event: builderror\nid: 46\ndata: {\"foo\": \"bar\"}\n\n")),
new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 47\ndata: {}\n\n")),
- new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 48\ndata: {}\n\n")),
+ new DataChunk(-1, str_replace("\n", $sep, ": this is a oneline comment\n\n")),
+ new DataChunk(-1, str_replace("\n", $sep, ": this is a\n: multiline comment\n\n")),
+ new ServerSentEvent(str_replace("\n", $sep, ": comments are ignored\nevent: reload\n: anywhere\nid: 48\ndata: {}\n\n")),
new ServerSentEvent(str_replace("\n", $sep, "data: test\ndata:test\nid: 49\nevent: testEvent\n\n\n")),
new ServerSentEvent(str_replace("\n", $sep, "id: 50\ndata: \ndata\ndata: \ndata\ndata: \n\n")),
+ new DataChunk(-1, str_replace("\n", $sep, "id: 60\ndata")),
+ new LastChunk("\r\n" === $sep ? 355 : 322),
];
- $i = 0;
-
- $this->expectExceptionMessage('Response has been canceled');
- while ($res) {
- if ($i > 0) {
- $res->cancel();
- }
- foreach ($es->stream($res) as $chunk) {
- if ($chunk->isTimeout()) {
- continue;
- }
-
- if ($chunk->isLast()) {
- continue;
- }
-
- $this->assertEquals($expected[$i++], $chunk);
- }
+ foreach ($es->stream($res) as $chunk) {
+ $this->assertEquals(array_shift($expected), $chunk);
}
+ $this->assertSame([], $expected);
}
/**
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
index 6931336f06d17..2a4392aa8c340 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
@@ -66,7 +66,7 @@ public function collect(Request $request, Response $response, ?\Throwable $excep
$sessionMetadata = [];
$sessionAttributes = [];
$flashes = [];
- if ($request->hasSession()) {
+ if (!$request->attributes->getBoolean('_stateless') && $request->hasSession()) {
$session = $request->getSession();
if ($session->isStarted()) {
$sessionMetadata['Created'] = date(\DATE_RFC822, $session->getMetadataBag()->getCreated());
diff --git a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php
index d9d425e114b93..87933187a32c7 100644
--- a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php
@@ -16,7 +16,7 @@
use Symfony\Contracts\EventDispatcher\Event;
/**
- * Base class for events thrown in the HttpKernel component.
+ * Base class for events dispatched in the HttpKernel component.
*
* @author Bernhard Schussek
*/
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
index e4261871b0e72..c7950b8365b57 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php
@@ -97,7 +97,7 @@ public function onKernelResponse(ResponseEvent $event)
return;
}
- $session = $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null;
+ $session = !$request->attributes->getBoolean('_stateless') && $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null;
if ($session instanceof Session) {
$usageIndexValue = $usageIndexReference = &$session->getUsageIndex();
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index d25647c87d157..9a612d805283f 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.40';
- public const VERSION_ID = 50440;
+ public const VERSION = '5.4.41';
+ public const VERSION_ID = 50441;
public const MAJOR_VERSION = 5;
public const MINOR_VERSION = 4;
- public const RELEASE_VERSION = 40;
+ public const RELEASE_VERSION = 41;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '11/2024';
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php
index 57f8f53b1e9f7..fdf550d0ecd41 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php
@@ -40,8 +40,8 @@ public function testKernelTerminate()
->willReturn($profile);
$kernel = $this->createMock(HttpKernelInterface::class);
- $mainRequest = $this->createMock(Request::class);
- $subRequest = $this->createMock(Request::class);
+ $mainRequest = new Request();
+ $subRequest = new Request();
$response = $this->createMock(Response::class);
$requestStack = new RequestStack();
diff --git a/src/Symfony/Component/Inflector/Tests/InflectorTest.php b/src/Symfony/Component/Inflector/Tests/InflectorTest.php
index 0702c717e3495..d637e3d72d1eb 100644
--- a/src/Symfony/Component/Inflector/Tests/InflectorTest.php
+++ b/src/Symfony/Component/Inflector/Tests/InflectorTest.php
@@ -37,7 +37,7 @@ public static function singularizeProvider()
['atlases', ['atlas', 'atlase', 'atlasis']],
['axes', ['ax', 'axe', 'axis']],
['babies', 'baby'],
- ['bacteria', ['bacterion', 'bacterium']],
+ ['bacteria', 'bacterium'],
['bases', ['bas', 'base', 'basis']],
['batches', ['batch', 'batche']],
['beaux', 'beau'],
@@ -48,6 +48,7 @@ public static function singularizeProvider()
['bureaux', 'bureau'],
['buses', ['bus', 'buse', 'busis']],
['bushes', ['bush', 'bushe']],
+ ['buttons', 'button'],
['calves', ['calf', 'calve', 'calff']],
['cars', 'car'],
['cassettes', ['cassett', 'cassette']],
@@ -58,10 +59,12 @@ public static function singularizeProvider()
['circuses', ['circus', 'circuse', 'circusis']],
['cliffs', 'cliff'],
['committee', 'committee'],
+ ['corpora', 'corpus'],
+ ['coupons', 'coupon'],
['crises', ['cris', 'crise', 'crisis']],
- ['criteria', ['criterion', 'criterium']],
+ ['criteria', 'criterion'],
['cups', 'cup'],
- ['coupons', 'coupon'],
+ ['curricula', 'curriculum'],
['data', 'data'],
['days', 'day'],
['discos', 'disco'],
@@ -87,6 +90,7 @@ public static function singularizeProvider()
['funguses', ['fungus', 'funguse', 'fungusis']],
['garages', ['garag', 'garage']],
['geese', 'goose'],
+ ['genera', 'genus'],
['halves', ['half', 'halve', 'halff']],
['hats', 'hat'],
['heroes', ['hero', 'heroe']],
@@ -107,6 +111,8 @@ public static function singularizeProvider()
['lives', 'life'],
['matrices', ['matrex', 'matrix', 'matrice']],
['matrixes', 'matrix'],
+ ['media', 'medium'],
+ ['memoranda', 'memorandum'],
['men', 'man'],
['mice', 'mouse'],
['moves', 'move'],
@@ -120,7 +126,7 @@ public static function singularizeProvider()
['parties', 'party'],
['people', 'person'],
['persons', 'person'],
- ['phenomena', ['phenomenon', 'phenomenum']],
+ ['phenomena', 'phenomenon'],
['photos', 'photo'],
['pianos', 'piano'],
['plateaux', 'plateau'],
@@ -144,7 +150,7 @@ public static function singularizeProvider()
['spies', 'spy'],
['staves', ['staf', 'stave', 'staff']],
['stories', 'story'],
- ['strata', ['straton', 'stratum']],
+ ['strata', 'stratum'],
['suitcases', ['suitcas', 'suitcase', 'suitcasis']],
['syllabi', 'syllabus'],
['tags', 'tag'],
@@ -195,7 +201,9 @@ public static function pluralizeProvider()
['bureau', ['bureaus', 'bureaux']],
['bus', 'buses'],
['bush', 'bushes'],
+ ['button', 'buttons'],
['calf', ['calfs', 'calves']],
+ ['campus', 'campuses'],
['car', 'cars'],
['cassette', 'cassettes'],
['cave', 'caves'],
@@ -205,10 +213,11 @@ public static function pluralizeProvider()
['circus', 'circuses'],
['cliff', 'cliffs'],
['committee', 'committees'],
+ ['coupon', 'coupons'],
['crisis', 'crises'],
- ['criteria', 'criterion'],
+ ['criterion', 'criteria'],
['cup', 'cups'],
- ['coupon', 'coupons'],
+ ['curriculum', 'curricula'],
['data', 'data'],
['day', 'days'],
['disco', 'discos'],
@@ -232,10 +241,12 @@ public static function pluralizeProvider()
['half', ['halfs', 'halves']],
['hat', 'hats'],
['hero', 'heroes'],
+ ['hippocampus', 'hippocampi'],
['hippopotamus', 'hippopotami'], // hippopotamuses
['hoax', 'hoaxes'],
['hoof', ['hoofs', 'hooves']],
['house', 'houses'],
+ ['icon', 'icons'],
['index', ['indicies', 'indexes']],
['ion', 'ions'],
['iris', 'irises'],
@@ -248,6 +259,8 @@ public static function pluralizeProvider()
['louse', 'lice'],
['man', 'men'],
['matrix', ['matricies', 'matrixes']],
+ ['medium', 'media'],
+ ['memorandum', 'memoranda'],
['mouse', 'mice'],
['move', 'moves'],
['movie', 'movies'],
diff --git a/src/Symfony/Component/Inflector/composer.json b/src/Symfony/Component/Inflector/composer.json
index 5b7280c1f42ce..6b46f7cb918b1 100644
--- a/src/Symfony/Component/Inflector/composer.json
+++ b/src/Symfony/Component/Inflector/composer.json
@@ -26,7 +26,7 @@
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/polyfill-php80": "^1.16",
- "symfony/string": "^5.3.10|^6.0"
+ "symfony/string": "^5.4.41|^6.4.9"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Inflector\\": "" },
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
index c621ae5b16a77..5fa28ef0e494b 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
@@ -63,6 +63,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
$endpoint = sprintf('%s/v3/%s/messages.mime', $this->getEndpoint(), urlencode($this->domain));
$response = $this->client->request('POST', 'https://'.$endpoint, [
+ 'http_version' => '1.1',
'auth_basic' => 'api:'.$this->key,
'headers' => $headers,
'body' => $body->bodyToIterable(),
diff --git a/src/Symfony/Component/Mailer/MailerInterface.php b/src/Symfony/Component/Mailer/MailerInterface.php
index 8d9540a3e5e3f..ebac4b53efa4e 100644
--- a/src/Symfony/Component/Mailer/MailerInterface.php
+++ b/src/Symfony/Component/Mailer/MailerInterface.php
@@ -15,7 +15,7 @@
use Symfony\Component\Mime\RawMessage;
/**
- * Interface for mailers able to send emails synchronous and/or asynchronous.
+ * Interface for mailers able to send emails synchronously and/or asynchronously.
*
* Implementations must support synchronous and asynchronous sending.
*
diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php
index 80840c859cb05..d11a5d8037b27 100644
--- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php
@@ -72,4 +72,19 @@ public function testSendWithAmazonSqsXrayTraceHeaderStamp()
$sender = new AmazonSqsSender($connection, $serializer);
$sender->send($envelope);
}
+
+ public function testSendEncodeBodyToRespectAmazonRequirements()
+ {
+ $envelope = new Envelope(new DummyMessage('Oy'));
+ $encoded = ['body' => "\x7", 'headers' => ['type' => DummyMessage::class]];
+
+ $connection = $this->createMock(Connection::class);
+ $connection->expects($this->once())->method('send')->with(base64_encode($encoded['body']), $encoded['headers']);
+
+ $serializer = $this->createMock(SerializerInterface::class);
+ $serializer->method('encode')->with($envelope)->willReturn($encoded);
+
+ $sender = new AmazonSqsSender($connection, $serializer);
+ $sender->send($envelope);
+ }
}
diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php
index 1994313720e0d..b253c82e97e30 100644
--- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php
+++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php
@@ -38,6 +38,7 @@ public function __construct(Connection $connection, SerializerInterface $seriali
public function send(Envelope $envelope): Envelope
{
$encodedMessage = $this->serializer->encode($envelope);
+ $encodedMessage = $this->complyWithAmazonSqsRequirements($encodedMessage);
/** @var DelayStamp|null $delayStamp */
$delayStamp = $envelope->last(DelayStamp::class);
@@ -75,4 +76,20 @@ public function send(Envelope $envelope): Envelope
return $envelope;
}
+
+ /**
+ * @see https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html
+ *
+ * @param array{body: string, headers?: array} $encodedMessage
+ *
+ * @return array{body: string, headers?: array}
+ */
+ private function complyWithAmazonSqsRequirements(array $encodedMessage): array
+ {
+ if (preg_match('/[^\x20-\x{D7FF}\xA\xD\x9\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', $encodedMessage['body'])) {
+ $encodedMessage['body'] = base64_encode($encodedMessage['body']);
+ }
+
+ return $encodedMessage;
+ }
}
diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php
index 9de6fa8ca0919..322bf6f4df84b 100644
--- a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php
@@ -813,6 +813,73 @@ public function testItCanBeConstructedWithTLSOptionsAndNonTLSDsn()
);
}
+ public function testItCanRetryPublishWhenAMQPConnectionExceptionIsThrown()
+ {
+ $factory = new TestAmqpFactory(
+ $amqpConnection = $this->createMock(\AMQPConnection::class),
+ $amqpChannel = $this->createMock(\AMQPChannel::class),
+ $amqpQueue = $this->createMock(\AMQPQueue::class),
+ $amqpExchange = $this->createMock(\AMQPExchange::class)
+ );
+
+ $amqpExchange->expects($this->exactly(2))
+ ->method('publish')
+ ->willReturnOnConsecutiveCalls(
+ $this->throwException(new \AMQPConnectionException('a socket error occurred')),
+ null
+ );
+
+ $connection = Connection::fromDsn('amqp://localhost', [], $factory);
+ $connection->publish('body');
+ }
+
+ public function testItCanRetryPublishWithDelayWhenAMQPConnectionExceptionIsThrown()
+ {
+ $factory = new TestAmqpFactory(
+ $amqpConnection = $this->createMock(\AMQPConnection::class),
+ $amqpChannel = $this->createMock(\AMQPChannel::class),
+ $amqpQueue = $this->createMock(\AMQPQueue::class),
+ $amqpExchange = $this->createMock(\AMQPExchange::class)
+ );
+
+ $amqpExchange->expects($this->exactly(2))
+ ->method('publish')
+ ->willReturnOnConsecutiveCalls(
+ $this->throwException(new \AMQPConnectionException('a socket error occurred')),
+ null
+ );
+
+ $connection = Connection::fromDsn('amqp://localhost', [], $factory);
+ $connection->publish('body', [], 5000);
+ }
+
+ public function testItWillRetryMaxThreeTimesWhenAMQPConnectionExceptionIsThrown()
+ {
+ $factory = new TestAmqpFactory(
+ $amqpConnection = $this->createMock(\AMQPConnection::class),
+ $amqpChannel = $this->createMock(\AMQPChannel::class),
+ $amqpQueue = $this->createMock(\AMQPQueue::class),
+ $amqpExchange = $this->createMock(\AMQPExchange::class)
+ );
+
+ $exception = new \AMQPConnectionException('a socket error occurred');
+
+ $amqpExchange->expects($this->exactly(4))
+ ->method('publish')
+ ->willReturnOnConsecutiveCalls(
+ $this->throwException($exception),
+ $this->throwException($exception),
+ $this->throwException($exception),
+ $this->throwException($exception)
+ );
+
+ self::expectException(get_class($exception));
+ self::expectExceptionMessage($exception->getMessage());
+
+ $connection = Connection::fromDsn('amqp://localhost', [], $factory);
+ $connection->publish('body');
+ }
+
private function createDelayOrRetryConnection(\AMQPExchange $delayExchange, string $deadLetterExchangeName, string $delayQueueName): Connection
{
$amqpConnection = $this->createMock(\AMQPConnection::class);
diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
index 3ea7784d862fd..8689b8ee306cc 100644
--- a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
+++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
@@ -306,19 +306,21 @@ public function publish(string $body, array $headers = [], int $delayInMs = 0, ?
$this->setupExchangeAndQueues(); // also setup normal exchange for delayed messages so delay queue can DLX messages to it
}
- if (0 !== $delayInMs) {
- $this->publishWithDelay($body, $headers, $delayInMs, $amqpStamp);
+ $this->withConnectionExceptionRetry(function () use ($body, $headers, $delayInMs, $amqpStamp) {
+ if (0 !== $delayInMs) {
+ $this->publishWithDelay($body, $headers, $delayInMs, $amqpStamp);
- return;
- }
+ return;
+ }
- $this->publishOnExchange(
- $this->exchange(),
- $body,
- $this->getRoutingKeyForMessage($amqpStamp),
- $headers,
- $amqpStamp
- );
+ $this->publishOnExchange(
+ $this->exchange(),
+ $body,
+ $this->getRoutingKeyForMessage($amqpStamp),
+ $headers,
+ $amqpStamp
+ );
+ });
}
/**
@@ -570,13 +572,18 @@ public function exchange(): \AMQPExchange
private function clearWhenDisconnected(): void
{
if (!$this->channel()->isConnected()) {
- $this->amqpChannel = null;
- $this->amqpQueues = [];
- $this->amqpExchange = null;
- $this->amqpDelayExchange = null;
+ $this->clear();
}
}
+ private function clear(): void
+ {
+ $this->amqpChannel = null;
+ $this->amqpQueues = [];
+ $this->amqpExchange = null;
+ $this->amqpDelayExchange = null;
+ }
+
private function getDefaultPublishRoutingKey(): ?string
{
return $this->exchangeOptions['default_publish_routing_key'] ?? null;
@@ -593,6 +600,25 @@ private function getRoutingKeyForMessage(?AmqpStamp $amqpStamp): ?string
{
return (null !== $amqpStamp ? $amqpStamp->getRoutingKey() : null) ?? $this->getDefaultPublishRoutingKey();
}
+
+ private function withConnectionExceptionRetry(callable $callable): void
+ {
+ $maxRetries = 3;
+ $retries = 0;
+
+ retry:
+ try {
+ $callable();
+ } catch (\AMQPConnectionException $e) {
+ if (++$retries <= $maxRetries) {
+ $this->clear();
+
+ goto retry;
+ }
+
+ throw $e;
+ }
+ }
}
if (!class_exists(\Symfony\Component\Messenger\Transport\AmqpExt\Connection::class, false)) {
diff --git a/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php b/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php
index 3d306f2e8ff86..7a9913b969a64 100644
--- a/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php
+++ b/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php
@@ -143,15 +143,15 @@ public function testFromPathWithUrl()
}
$finder = new PhpExecutableFinder();
- $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8057']));
+ $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8856']));
$process->setWorkingDirectory(__DIR__.'/../Fixtures/web');
$process->start();
try {
do {
usleep(50000);
- } while (!@fopen('http://localhost:8057', 'r'));
- $p = DataPart::fromPath($file = 'http://localhost:8057/logo_symfony_header.png');
+ } while (!@fopen('http://localhost:8856', 'r'));
+ $p = DataPart::fromPath($file = 'http://localhost:8856/logo_symfony_header.png');
$content = file_get_contents($file);
$this->assertEquals($content, $p->getBody());
$maxLineLength = 76;
diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php
index 41666a7cf182e..735744e5e6da4 100644
--- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php
+++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php
@@ -96,16 +96,22 @@ protected function doSend(MessageInterface $message): SentMessage
$threadKey = $opts->getThreadKey() ?: $this->threadKey;
- $options = $opts->toArray();
$url = sprintf('https://%s/v1/spaces/%s/messages?key=%s&token=%s%s',
$this->getEndpoint(),
$this->space,
urlencode($this->accessKey),
urlencode($this->accessToken),
- $threadKey ? '&threadKey='.urlencode($threadKey) : ''
+ $threadKey ? '&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD' : ''
);
+
+ $body = array_filter($opts->toArray());
+
+ if ($threadKey) {
+ $body['thread']['threadKey'] = $threadKey;
+ }
+
$response = $this->client->request('POST', $url, [
- 'json' => array_filter($options),
+ 'json' => $body,
]);
try {
diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php
index c8df301ca96c6..b6cbf8176d55c 100644
--- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php
+++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php
@@ -116,11 +116,11 @@ public function testSendWithOptions()
->method('getContent')
->willReturn('{"name":"spaces/My-Space/messages/abcdefg.hijklmno"}');
- $expectedBody = json_encode(['text' => $message]);
+ $expectedBody = json_encode(['text' => $message, 'thread' => ['threadKey' => 'My-Thread']]);
$client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $expectedBody): ResponseInterface {
$this->assertSame('POST', $method);
- $this->assertSame('https://chat.googleapis.com/v1/spaces/My-Space/messages?key=theAccessKey&token=theAccessToken%3D&threadKey=My-Thread', $url);
+ $this->assertSame('https://chat.googleapis.com/v1/spaces/My-Space/messages?key=theAccessKey&token=theAccessToken%3D&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD', $url);
$this->assertSame($expectedBody, $options['body']);
return $response;
diff --git a/src/Symfony/Component/Notifier/ChatterInterface.php b/src/Symfony/Component/Notifier/ChatterInterface.php
index 915190e623aaa..6d89ca921e970 100644
--- a/src/Symfony/Component/Notifier/ChatterInterface.php
+++ b/src/Symfony/Component/Notifier/ChatterInterface.php
@@ -14,7 +14,7 @@
use Symfony\Component\Notifier\Transport\TransportInterface;
/**
- * Interface for classes able to send chat messages synchronous and/or asynchronous.
+ * Interface for classes able to send chat messages synchronously and/or asynchronously.
*
* @author Fabien Potencier
*/
diff --git a/src/Symfony/Component/Notifier/TexterInterface.php b/src/Symfony/Component/Notifier/TexterInterface.php
index e65547755cd70..a044bb6d5d835 100644
--- a/src/Symfony/Component/Notifier/TexterInterface.php
+++ b/src/Symfony/Component/Notifier/TexterInterface.php
@@ -14,7 +14,7 @@
use Symfony\Component\Notifier\Transport\TransportInterface;
/**
- * Interface for classes able to send SMS messages synchronous and/or asynchronous.
+ * Interface for classes able to send SMS messages synchronously and/or asynchronously.
*
* @author Fabien Potencier
*/
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
index f71664d5a3547..57869e40819bb 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
@@ -428,6 +428,11 @@ public function testUnknownPseudoType()
$this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, 'scalar')], $this->extractor->getTypes(PseudoTypeDummy::class, 'unknownPseudoType'));
}
+ public function testGenericInterface()
+ {
+ $this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface'));
+ }
+
protected static function isPhpDocumentorV5()
{
if (class_exists(InvalidTag::class)) {
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php
index fd11fcbeb8c63..8786785774878 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php
@@ -390,7 +390,7 @@ public static function unionTypesProvider(): array
['b', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
['c', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
['d', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])])]],
- ['e', [new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class, true, [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])], [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING, false, null, true, [], [new Type(Type::BUILTIN_TYPE_OBJECT, false, DefaultValue::class)])])]), new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
+ ['e', [new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class, false, [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])], [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_OBJECT, false, \Traversable::class, true, [], [new Type(Type::BUILTIN_TYPE_OBJECT, false, DefaultValue::class)])])]), new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
['f', null],
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
];
@@ -429,6 +429,11 @@ public static function intRangeTypeProvider(): array
['c', [new Type(Type::BUILTIN_TYPE_INT)]],
];
}
+
+ public function testGenericInterface()
+ {
+ $this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface'));
+ }
}
class PhpStanOmittedParamTagTypeDocBlock
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
index d3d57514a02c9..06e8bc53f87b5 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
@@ -73,6 +73,7 @@ public function testGetProperties()
'arrayOfMixed',
'listOfStrings',
'parentAnnotation',
+ 'genericInterface',
'foo',
'foo2',
'foo3',
@@ -137,6 +138,7 @@ public function testGetPropertiesWithCustomPrefixes()
'arrayOfMixed',
'listOfStrings',
'parentAnnotation',
+ 'genericInterface',
'foo',
'foo2',
'foo3',
@@ -190,6 +192,7 @@ public function testGetPropertiesWithNoPrefixes()
'arrayOfMixed',
'listOfStrings',
'parentAnnotation',
+ 'genericInterface',
'foo',
'foo2',
'foo3',
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
index 06c0783a3c8f8..cf0c791784695 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
@@ -165,6 +165,11 @@ class Dummy extends ParentDummy
*/
public $parentAnnotation;
+ /**
+ * @var \BackedEnum
+ */
+ public $genericInterface;
+
public static function getStatic()
{
}
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php
index 86ddb8a1650eb..7e2e1aa3ec8f7 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php
@@ -40,7 +40,7 @@ class DummyUnionType
public $d;
/**
- * @var (Dummy, (int | (string)[])> | ParentDummy | null)
+ * @var (Dummy, (int | (\Traversable)[])> | ParentDummy | null)
*/
public $e;
diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php
index 6020be0b80a3c..dc8a941b5e7fc 100644
--- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php
+++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php
@@ -102,9 +102,9 @@ public function getTypes(DocType $varType): array
/**
* Creates a {@see Type} from a PHPDoc type.
*/
- private function createType(DocType $type, bool $nullable, ?string $docType = null): ?Type
+ private function createType(DocType $type, bool $nullable): ?Type
{
- $docType = $docType ?? (string) $type;
+ $docType = (string) $type;
if ($type instanceof Collection) {
$fqsen = $type->getFqsen();
@@ -115,10 +115,17 @@ private function createType(DocType $type, bool $nullable, ?string $docType = nu
[$phpType, $class] = $this->getPhpTypeAndClass((string) $fqsen);
+ $collection = \is_a($class, \Traversable::class, true) || \is_a($class, \ArrayAccess::class, true);
+
+ // it's safer to fall back to other extractors if the generic type is too abstract
+ if (!$collection && !class_exists($class)) {
+ return null;
+ }
+
$keys = $this->getTypes($type->getKeyType());
$values = $this->getTypes($type->getValueType());
- return new Type($phpType, $nullable, $class, true, $keys, $values);
+ return new Type($phpType, $nullable, $class, $collection, $keys, $values);
}
// Cannot guess
diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
index 256122af759b7..6171530abadc7 100644
--- a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
+++ b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
@@ -121,6 +121,13 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
return [$mainType];
}
+ $collection = $mainType->isCollection() || \in_array($mainType->getClassName(), [\Traversable::class, \Iterator::class, \IteratorAggregate::class, \ArrayAccess::class, \Generator::class], true);
+
+ // it's safer to fall back to other extractors if the generic type is too abstract
+ if (!$collection && !class_exists($mainType->getClassName())) {
+ return [];
+ }
+
$collectionKeyTypes = $mainType->getCollectionKeyTypes();
$collectionKeyValues = [];
if (1 === \count($node->genericTypes)) {
@@ -136,7 +143,7 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
}
}
- return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), true, $collectionKeyTypes, $collectionKeyValues)];
+ return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), $collection, $collectionKeyTypes, $collectionKeyValues)];
}
if ($node instanceof ArrayShapeNode) {
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)];
diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf
index 6d7dc7fc23e33..93ff24f330735 100644
--- a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf
+++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf
@@ -64,7 +64,7 @@
Too many failed login attempts, please try again later.
- Massa intents d'inici de sessió fallits, torneu-ho a provar més tard.
+ Massa intents d'inici de sessió fallits, si us plau torneu-ho a provar més tard.Invalid or expired login link.
@@ -72,11 +72,11 @@
Too many failed login attempts, please try again in %minutes% minute.
- Massa intents d'inici de sessió fallits, torneu-ho a provar en %minutes% minut.
+ Massa intents d'inici de sessió fallits, si us plau torneu-ho a provar en %minutes% minut.Too many failed login attempts, please try again in %minutes% minutes.
- Massa intents fallits d'inici de sessió, torneu-ho a provar d'aquí a %minutes% minuts.
+ Massa intents d'inici de sessió fallits, si us plau torneu-ho a provar en %minutes% minuts.