diff --git a/.travis.yml b/.travis.yml
index 24155a5c0c36..cbf4eee7dd1f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,6 +31,9 @@ matrix:
- php: nightly
services: [memcached]
fast_finish: true
+ allow_failures:
+ - php: nightly
+ services: [memcached]
cache:
directories:
diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md
index be58f04b9550..20f38b3422f1 100644
--- a/CHANGELOG-4.4.md
+++ b/CHANGELOG-4.4.md
@@ -7,6 +7,68 @@ in 4.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/v4.4.0...v4.4.1
+* 4.4.9 (2020-05-31)
+
+ * bug #37008 [Security] Fixed AbstractToken::hasUserChanged() (wouterj)
+ * bug #36894 [Validator] never directly validate Existence (Required/Optional) constraints (xabbuh)
+ * bug #37007 [Console] Fix QuestionHelper::disableStty() (chalasr)
+ * bug #36865 [Form] validate subforms in all validation groups (xabbuh)
+ * bug #36907 Fixes sprintf(): Too few arguments in form transformer (pedrocasado)
+ * bug #36868 [Validator] Use Mime component to determine mime type for file validator (pierredup)
+ * bug #37000 Add meaningful message when using ProcessHelper and Process is not installed (l-vo)
+ * bug #36995 [TwigBridge] fix fallback html-to-txt body converter (nicolas-grekas)
+ * bug #36993 [ErrorHandler] fix setting $trace to null in FatalError (nicolas-grekas)
+ * bug #36987 Handle fetch mode deprecation of DBAL 2.11. (derrabus)
+ * bug #36974 [Security] Fixed handling of CSRF logout error (wouterj)
+ * bug #36947 [Mime] Allow email message to have "To", "Cc", or "Bcc" header to be valid (Ernest Hymel)
+ * bug #36914 Parse and render anonymous classes correctly on php 8 (derrabus)
+ * bug #36921 [OptionsResolver][Serializer] Remove calls to deprecated ReflectionParameter::getClass() (derrabus)
+ * bug #36920 [VarDumper] fix PHP 8 support (nicolas-grekas)
+ * bug #36917 [Cache] Accessing undefined constants raises an Error in php8 (derrabus)
+ * bug #36891 Address deprecation of ReflectionType::getClass() (derrabus)
+ * bug #36899 [VarDumper] ReflectionFunction::isDisabled() is deprecated (derrabus)
+ * bug #36905 [Validator] Catch expected ValueError (derrabus)
+ * bug #36915 [DomCrawler] Catch expected ValueError (derrabus)
+ * bug #36908 [Cache][HttpClient] Made method signatures compatible with their corresponding traits (derrabus)
+ * bug #36906 [DomCrawler] Catch expected ValueError (derrabus)
+ * bug #36904 [PropertyAccess] Parse php 8 TypeErrors correctly (derrabus)
+ * bug #36839 [BrowserKit] Raw body with custom Content-Type header (azhurb)
+ * bug #36896 [Config] Removed implicit cast of ReflectionProperty to string (derrabus)
+ * bug #35944 [Security/Core] Fix wrong roles comparison (thlbaut)
+ * bug #36882 [PhpUnitBridge] fix installing under PHP >= 8 (nicolas-grekas)
+ * bug #36833 [HttpKernel] Fix that the `Store` would not save responses with the X-Content-Digest header present (mpdude)
+ * bug #36867 [PhpUnitBridge] fix bad detection of unsilenced deprecations (nicolas-grekas)
+ * bug #36862 [Security] Unserialize $parentData, if needed, to avoid errors (rfaivre)
+ * bug #36855 [HttpKernel] Fix error logger when stderr is redirected to /dev/null (fabpot)
+ * bug #36838 [HttpKernel] Bring back the debug toolbar (derrabus)
+ * bug #36592 [BrowserKit] Allow Referer set by history to be overridden (Slamdunk)
+ * bug #36823 [HttpClient] fix PHP warning + accept status code >= 600 (nicolas-grekas)
+ * bug #36824 [Security/Core] fix compat of `NativePasswordEncoder` with pre-PHP74 values of `PASSWORD_*` consts (nicolas-grekas)
+ * bug #36811 [DependencyInjection] Fix register event listeners compiler pass (X-Coder264)
+ * bug #36789 Change priority of KernelEvents::RESPONSE subscriber (marcw)
+ * bug #36794 [Serializer] fix issue with PHP 8 (nicolas-grekas)
+ * bug #36786 [WebProfiler] Remove 'none' when appending CSP tokens (ndench)
+ * bug #36743 [Yaml] Fix escaped quotes in quoted multi-line string (ossinkine)
+ * bug #36777 [TwigBundle] FormExtension does not have a constructor anymore since sf 4.0 (Tobion)
+ * bug #36716 [Mime] handle passing custom mime types as string (mcneely)
+ * bug #36747 Queue name is a required parameter (theravel)
+ * bug #36751 [Mime] fix bad method call on `EmailAddressContains` (Kocal)
+ * bug #36696 [Console] don't check tty on stdin, it breaks with "data lost during stream conversion" (nicolas-grekas)
+ * bug #36569 [PhpUnitBridge] Mark parent class also covered in CoverageListener (lyrixx)
+ * bug #36690 [Yaml] prevent notice for invalid octal numbers on PHP 7.4 (xabbuh)
+ * bug #36590 [Console] Default hidden question to 1 attempt for non-tty session (ostrolucky)
+ * bug #36497 [Filesystem] Handle paths on different drives (crishoj)
+ * bug #36678 [WebProfiler] Do not add src-elem CSP directives if they do not exist (ndench)
+ * bug #36501 [DX] Show the ParseException message in all YAML file loaders (fancyweb)
+ * bug #36683 [Yaml] fix parse error when unindented collections contain a comment (wdiesveld)
+ * bug #36672 [Validator] Skip validation when email is an empty object (acrobat)
+ * bug #36673 [PhpUnitBridge] fix PHP 5.3 compat again (nicolas-grekas)
+ * bug #36505 [Translation] Fix for translation:update command updating ICU messages (artemoliynyk)
+ * bug #36627 [Validator] fix lazy property usage. (bendavies)
+ * bug #36601 [Serializer] do not transform empty \Traversable to Array (soyuka)
+ * bug #36606 [Cache] Fixed not supported Redis eviction policies (SerheyDolgushev)
+ * bug #36625 [PhpUnitBridge] fix compat with PHP 5.3 (nicolas-grekas)
+
* 4.4.8 (2020-04-28)
* bug #36536 [Cache] Allow invalidateTags calls to be traced by data collector (l-vo)
diff --git a/CHANGELOG-5.0.md b/CHANGELOG-5.0.md
index 1fb405767821..cbd366b3c739 100644
--- a/CHANGELOG-5.0.md
+++ b/CHANGELOG-5.0.md
@@ -7,6 +7,68 @@ in 5.0 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.0.0...v5.0.1
+* 5.0.9 (2020-05-31)
+
+ * bug #37008 [Security] Fixed AbstractToken::hasUserChanged() (wouterj)
+ * bug #36894 [Validator] never directly validate Existence (Required/Optional) constraints (xabbuh)
+ * bug #37007 [Console] Fix QuestionHelper::disableStty() (chalasr)
+ * bug #36865 [Form] validate subforms in all validation groups (xabbuh)
+ * bug #36907 Fixes sprintf(): Too few arguments in form transformer (pedrocasado)
+ * bug #36868 [Validator] Use Mime component to determine mime type for file validator (pierredup)
+ * bug #37000 Add meaningful message when using ProcessHelper and Process is not installed (l-vo)
+ * bug #36995 [TwigBridge] fix fallback html-to-txt body converter (nicolas-grekas)
+ * bug #36993 [ErrorHandler] fix setting $trace to null in FatalError (nicolas-grekas)
+ * bug #36987 Handle fetch mode deprecation of DBAL 2.11. (derrabus)
+ * bug #36974 [Security] Fixed handling of CSRF logout error (wouterj)
+ * bug #36947 [Mime] Allow email message to have "To", "Cc", or "Bcc" header to be valid (Ernest Hymel)
+ * bug #36914 Parse and render anonymous classes correctly on php 8 (derrabus)
+ * bug #36921 [OptionsResolver][Serializer] Remove calls to deprecated ReflectionParameter::getClass() (derrabus)
+ * bug #36920 [VarDumper] fix PHP 8 support (nicolas-grekas)
+ * bug #36917 [Cache] Accessing undefined constants raises an Error in php8 (derrabus)
+ * bug #36891 Address deprecation of ReflectionType::getClass() (derrabus)
+ * bug #36899 [VarDumper] ReflectionFunction::isDisabled() is deprecated (derrabus)
+ * bug #36905 [Validator] Catch expected ValueError (derrabus)
+ * bug #36915 [DomCrawler] Catch expected ValueError (derrabus)
+ * bug #36908 [Cache][HttpClient] Made method signatures compatible with their corresponding traits (derrabus)
+ * bug #36906 [DomCrawler] Catch expected ValueError (derrabus)
+ * bug #36904 [PropertyAccess] Parse php 8 TypeErrors correctly (derrabus)
+ * bug #36839 [BrowserKit] Raw body with custom Content-Type header (azhurb)
+ * bug #36896 [Config] Removed implicit cast of ReflectionProperty to string (derrabus)
+ * bug #35944 [Security/Core] Fix wrong roles comparison (thlbaut)
+ * bug #36882 [PhpUnitBridge] fix installing under PHP >= 8 (nicolas-grekas)
+ * bug #36833 [HttpKernel] Fix that the `Store` would not save responses with the X-Content-Digest header present (mpdude)
+ * bug #36867 [PhpUnitBridge] fix bad detection of unsilenced deprecations (nicolas-grekas)
+ * bug #36862 [Security] Unserialize $parentData, if needed, to avoid errors (rfaivre)
+ * bug #36855 [HttpKernel] Fix error logger when stderr is redirected to /dev/null (fabpot)
+ * bug #36838 [HttpKernel] Bring back the debug toolbar (derrabus)
+ * bug #36592 [BrowserKit] Allow Referer set by history to be overridden (Slamdunk)
+ * bug #36823 [HttpClient] fix PHP warning + accept status code >= 600 (nicolas-grekas)
+ * bug #36824 [Security/Core] fix compat of `NativePasswordEncoder` with pre-PHP74 values of `PASSWORD_*` consts (nicolas-grekas)
+ * bug #36811 [DependencyInjection] Fix register event listeners compiler pass (X-Coder264)
+ * bug #36789 Change priority of KernelEvents::RESPONSE subscriber (marcw)
+ * bug #36794 [Serializer] fix issue with PHP 8 (nicolas-grekas)
+ * bug #36786 [WebProfiler] Remove 'none' when appending CSP tokens (ndench)
+ * bug #36743 [Yaml] Fix escaped quotes in quoted multi-line string (ossinkine)
+ * bug #36777 [TwigBundle] FormExtension does not have a constructor anymore since sf 4.0 (Tobion)
+ * bug #36716 [Mime] handle passing custom mime types as string (mcneely)
+ * bug #36747 Queue name is a required parameter (theravel)
+ * bug #36751 [Mime] fix bad method call on `EmailAddressContains` (Kocal)
+ * bug #36696 [Console] don't check tty on stdin, it breaks with "data lost during stream conversion" (nicolas-grekas)
+ * bug #36569 [PhpUnitBridge] Mark parent class also covered in CoverageListener (lyrixx)
+ * bug #36690 [Yaml] prevent notice for invalid octal numbers on PHP 7.4 (xabbuh)
+ * bug #36590 [Console] Default hidden question to 1 attempt for non-tty session (ostrolucky)
+ * bug #36497 [Filesystem] Handle paths on different drives (crishoj)
+ * bug #36678 [WebProfiler] Do not add src-elem CSP directives if they do not exist (ndench)
+ * bug #36501 [DX] Show the ParseException message in all YAML file loaders (fancyweb)
+ * bug #36683 [Yaml] fix parse error when unindented collections contain a comment (wdiesveld)
+ * bug #36672 [Validator] Skip validation when email is an empty object (acrobat)
+ * bug #36673 [PhpUnitBridge] fix PHP 5.3 compat again (nicolas-grekas)
+ * bug #36505 [Translation] Fix for translation:update command updating ICU messages (artemoliynyk)
+ * bug #36627 [Validator] fix lazy property usage. (bendavies)
+ * bug #36601 [Serializer] do not transform empty \Traversable to Array (soyuka)
+ * bug #36606 [Cache] Fixed not supported Redis eviction policies (SerheyDolgushev)
+ * bug #36625 [PhpUnitBridge] fix compat with PHP 5.3 (nicolas-grekas)
+
* 5.0.8 (2020-04-28)
* bug #36536 [Cache] Allow invalidateTags calls to be traced by data collector (l-vo)
diff --git a/CHANGELOG-5.1.md b/CHANGELOG-5.1.md
index 7d1409a1ec72..4f34853bc8b9 100644
--- a/CHANGELOG-5.1.md
+++ b/CHANGELOG-5.1.md
@@ -7,6 +7,49 @@ in 5.1 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.1.0...v5.1.1
+* 5.1.1 (2020-06-12)
+
+ * bug #37227 [DependencyInjection][CheckTypeDeclarationsPass] Handle unresolved parameters pointing to environment variables (fancyweb)
+ * bug #37103 [Form] switch the context when validating nested forms (xabbuh)
+ * bug #37182 [HttpKernel] Fix regression where Store does not return response body correctly (mpdude)
+ * bug #37193 [DependencyInjection][CheckTypeDeclarationsPass] Always resolve parameters (fancyweb)
+ * bug #37044 [DependencyInjection] Apply ExpressionLanguageProviderPass to router.default (wizhippo)
+ * bug #37054 [String] Fix ellipsis of truncate when not using cut option (DuboisS)
+ * bug #37190 [HttpClient] disable AMP's inactivity timeout, we deal with it on our own already (nicolas-grekas)
+ * bug #37191 [HttpClient] fix offset computation for data chunks (nicolas-grekas)
+ * bug #37176 [Routing] Keeping routes priorities after add a name prefix to the collection (yceruto)
+ * bug #37140 [Lock] Fixed reading locks from replica set secondary nodes (kralos)
+ * bug #37177 [Ldap] fix refreshUser() ignoring extra_fields (arkste)
+ * bug #37181 [Mailer] Remove an internal annot (fabpot)
+ * bug #36913 [FrameworkBundle] fix type annotation on ControllerTrait::addFlash() (ThomasLandauer)
+ * bug #37153 [PhpUnitBridge] Fix ExpectDeprecationTrait::expectDeprecation() conflict (fancyweb)
+ * bug #37162 [Mailer] added the reply-to addresses to the API SES transport request. (ribeiropaulor)
+ * bug #37144 [DI] Add check around class_alias for generated proxy classes (enumag)
+ * bug #37167 [Mime] use fromString when creating a new Address (fabpot)
+ * bug #37169 [Cache] fix forward compatibility with Doctrine DBAL 3 (xabbuh)
+ * bug #37159 [Mailer] Fixed generator bug when creating multiple transports using Transport::fromDsn (atailouloute)
+ * bug #37154 [FrameworkBundle] Remove reference to APP_SECRET in MicroKernelTrait (nicolas-grekas)
+ * bug #37126 [SecurityBundle] Fix the session listener registration under the new authentication manager (johnvandeweghe)
+ * bug #37130 [Console] allow cursor to be used even when STDIN is not defined (xabbuh)
+ * bug #37053 [PropertyAccess] Fix getter call order BC (1ed)
+ * bug #37117 [Messenger/DoctrineBridge] set column length for mysql 5.6 compatibility (Nemo64)
+ * bug #37127 [Messenger/AmazonSqsBridge] Fixed left-over debug statement (sstok)
+ * bug #37048 [HttpClient] fix monitoring timeouts when other streams are active (nicolas-grekas)
+ * bug #37085 [Form] properly cascade validation to child forms (xabbuh)
+ * bug #37095 [PhpUnitBridge] Fix undefined index when output of "composer show" cannot be parsed (nicolas-grekas)
+ * bug #37092 [PhpUnitBridge] fix undefined var on version 3.4 (nicolas-grekas)
+ * bug #37022 [DependencyInjection] Improve missing package/version deprecation (acrobat)
+ * bug #37038 Fix invalid char in SQS Headers (jderusse)
+ * bug #37047 [SecurityBundle] Only register CSRF protection listener if CSRF is available (wouterj)
+ * bug #37065 [HttpClient] Throw JsonException instead of TransportException on empty response in Response::toArray() (jeroennoten)
+ * bug #37058 [FrameworkBundle] Extension Serializer issue (Korbeil)
+ * bug #37077 [WebProfilerBundle] Move ajax clear event listener initialization on loadToolbar (Bruno BOUTAREL)
+ * bug #37056 [DoctrineBridge] register event listeners depending on the installed packages (xabbuh)
+ * bug #37020 [ExpressionLanguage] reset the internal state when the parser is finished (xabbuh)
+ * bug #37049 [Serializer] take into account the context when preserving empty array objects (xabbuh)
+ * bug #37031 [Security] Fixed PUBLIC_ACCESS in authenticated sessions (wouterj)
+ * bug #37028 [FrameworkBundle] Fix enabled_locales behavior (tgalopin)
+
* 5.1.0 (2020-05-31)
* bug #37009 [Validator] use "allowedVariables" to configure the ExpressionLanguageSyntax constraint (xabbuh)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 449973d7578e..baf025cfe048 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -23,15 +23,15 @@ Symfony is the result of the work of many people who made the code better
- Johannes S (johannes)
- Kris Wallsmith (kriswallsmith)
- Yonel Ceruto (yonelceruto)
- - Hugo Hamon (hhamon)
- Wouter de Jong (wouterj)
+ - Hugo Hamon (hhamon)
- Thomas Calvet (fancyweb)
+ - Alexander M. Turek (derrabus)
- Abdellatif Ait boudad (aitboudad)
- Samuel ROZE (sroze)
- Romain Neutron (romain)
- Pascal Borreli (pborreli)
- Joseph Bielawski (stloyd)
- - Alexander M. Turek (derrabus)
- Karma Dordrak (drak)
- Lukas Kahwe Smith (lsmith)
- Jules Pietri (heah)
@@ -60,16 +60,16 @@ Symfony is the result of the work of many people who made the code better
- Alexander Mols (asm89)
- Konstantin Myakshin (koc)
- Grégoire Paris (greg0ire)
- - Bulat Shakirzyanov (avalanche123)
- Valentin Udaltsov (vudaltsov)
+ - Bulat Shakirzyanov (avalanche123)
- Kevin Bond (kbond)
- Saša Stamenković (umpirsky)
- Peter Rehm (rpet)
- - Henrik Bjørnskov (henrikbjorn)
- Gabriel Ostrolucký (gadelat)
+ - Henrik Bjørnskov (henrikbjorn)
+ - Gábor Egyed (1ed)
- Miha Vrhovnik
- David Maicher (dmaicher)
- - Gábor Egyed (1ed)
- Diego Saint Esteben (dii3g0)
- Jan Schädlich (jschaedl)
- Titouan Galopin (tgalopin)
@@ -132,6 +132,7 @@ Symfony is the result of the work of many people who made the code better
- Daniel Wehner (dawehner)
- Tugdual Saunier (tucksaun)
- excelwebzone
+ - Massimiliano Arione (garak)
- Gordon Franke (gimler)
- Joel Wurtz (brouznouf)
- Fabien Pennequin (fabienpennequin)
@@ -139,7 +140,6 @@ Symfony is the result of the work of many people who made the code better
- Przemysław Bogusz (przemyslaw-bogusz)
- Eric GELOEN (gelo)
- Lars Strojny (lstrojny)
- - Massimiliano Arione (garak)
- Jannik Zschiesche (apfelbox)
- Robert Schönthal (digitalkaoz)
- Gregor Harlan (gharlan)
@@ -161,6 +161,7 @@ Symfony is the result of the work of many people who made the code better
- Yanick Witschi (toflar)
- Arnaud Kleinpeter (nanocom)
- Guilherme Blanco (guilhermeblanco)
+ - Laurent VOULLEMIER (lvo)
- SpacePossum
- Pablo Godel (pgodel)
- Jérémie Augustin (jaugustin)
@@ -181,6 +182,7 @@ Symfony is the result of the work of many people who made the code better
- jeremyFreeAgent (jeremyfreeagent)
- Rouven Weßling (realityking)
- Jérôme Parmentier (lctrs)
+ - Ben Davies (bendavies)
- Andreas Schempp (aschempp)
- Clemens Tolboom
- Helmer Aaviksoo
@@ -195,7 +197,7 @@ Symfony is the result of the work of many people who made the code better
- Tyson Andre
- GDIBass
- Samuel NELA (snela)
- - Ben Davies (bendavies)
+ - Saif (╯°□°)╯ (azjezz)
- James Halsall (jaitsu)
- Matthieu Napoli (mnapoli)
- Florent Mata (fmata)
@@ -204,6 +206,7 @@ Symfony is the result of the work of many people who made the code better
- Dmitrii Chekaliuk (lazyhammer)
- Clément JOBEILI (dator)
- Marek Štípek (maryo)
+ - Filippo Tessarotto (slamdunk)
- Daniel Espendiller
- Possum
- Dorian Villet (gnutix)
@@ -211,11 +214,11 @@ Symfony is the result of the work of many people who made the code better
- Sergey Linnik (linniksa)
- Richard Miller (mr_r_miller)
- Albert Casademont (acasademont)
+ - Wouter J
- Mario A. Alvarez Garcia (nomack84)
- Dennis Benkert (denderello)
- DQNEO
- Andre Rømcke (andrerom)
- - Saif (╯°□°)╯ (azjezz)
- mcfedr (mcfedr)
- Gary PEGEOT (gary-p)
- Ruben Gonzalez (rubenrua)
@@ -232,7 +235,6 @@ Symfony is the result of the work of many people who made the code better
- Stadly
- Stepan Anchugov (kix)
- bronze1man
- - Filippo Tessarotto (slamdunk)
- sun (sun)
- Larry Garfield (crell)
- Nikolay Labinskiy (e-moe)
@@ -245,7 +247,6 @@ Symfony is the result of the work of many people who made the code better
- fivestar
- Dominique Bongiraud
- Jeremy Livingston (jeremylivingston)
- - Laurent VOULLEMIER (lvo)
- Michael Lee (zerustech)
- Matthieu Auger (matthieuauger)
- Mathias Arlaud (mtarld)
@@ -255,6 +256,7 @@ Symfony is the result of the work of many people who made the code better
- Dustin Whittle (dustinwhittle)
- jeff
- John Kary (johnkary)
+ - Tien Vo (tienvx)
- Justin Hileman (bobthecow)
- Blanchon Vincent (blanchonvincent)
- Michele Orselli (orso)
@@ -269,7 +271,6 @@ Symfony is the result of the work of many people who made the code better
- Tristan Darricau (nicofuma)
- Victor Bocharsky (bocharsky_bw)
- Tomas Norkūnas (norkunas)
- - Wouter J
- Smaine Milianni (ismail1432)
- Marcel Beerta (mazen)
- Christopher Hertel (chertel)
@@ -295,7 +296,6 @@ Symfony is the result of the work of many people who made the code better
- Marcos Sánchez
- Elnur Abdurrakhimov (elnur)
- Manuel Reinhard (sprain)
- - Tien Vo (tienvx)
- Danny Berger (dpb587)
- Antonio J. García Lagar (ajgarlag)
- Adam Prager (padam87)
@@ -309,6 +309,7 @@ Symfony is the result of the work of many people who made the code better
- Arjen Brouwer (arjenjb)
- Katsuhiro OGAWA
- Patrick McDougle (patrick-mcdougle)
+ - Marc Weistroff (futurecat)
- Alif Rachmawadi
- Anton Chernikov (anton_ch1989)
- Kristen Gilden (kgilden)
@@ -318,6 +319,7 @@ Symfony is the result of the work of many people who made the code better
- Jakub Kucharovic (jkucharovic)
- Loick Piera (pyrech)
- Uwe Jäger (uwej711)
+ - Martin Hujer (martinhujer)
- Eugene Leonovich (rybakit)
- Joseph Rouff (rouffj)
- Félix Labrecque (woodspire)
@@ -354,9 +356,9 @@ Symfony is the result of the work of many people who made the code better
- Wouter Van Hecke
- Peter Kruithof (pkruithof)
- Michael Holm (hollo)
+ - Antonio Pauletich (x-coder264)
- Arjen van der Meijden
- Mathieu Lechat
- - Marc Weistroff (futurecat)
- Damien Alexandre (damienalexandre)
- Simon Mönch (sm)
- Christian Schmidt
@@ -388,6 +390,7 @@ Symfony is the result of the work of many people who made the code better
- Aurelijus Valeiša (aurelijus)
- Jan Decavele (jandc)
- Gustavo Piltcher
+ - Jesse Rushlow (geeshoe)
- Stepan Tanasiychuk (stfalcon)
- Tiago Ribeiro (fixe)
- Hidde Boomsma (hboomsma)
@@ -416,7 +419,9 @@ Symfony is the result of the work of many people who made the code better
- Nicolas LEFEVRE (nicoweb)
- alquerci
- Oleg Andreyev
+ - Langlet Vincent (deviling)
- Mateusz Sip (mateusz_sip)
+ - Alessandro Lai (jean85)
- Francesco Levorato
- Vitaliy Zakharov (zakharovvi)
- Tobias Sjösten (tobiassjosten)
@@ -427,14 +432,15 @@ Symfony is the result of the work of many people who made the code better
- Tomasz Kowalczyk (thunderer)
- Artur Eshenbrener
- Timo Bakx (timobakx)
- - Antonio Pauletich (x-coder264)
- Thomas Perez (scullwm)
- Felix Labrecque
- Yaroslav Kiliba
- Terje Bråten
- Robbert Klarenbeek (robbertkl)
+ - soyuka
- Eric Masoero (eric-masoero)
- Denis Brumann (dbrumann)
+ - Gocha Ossinkine (ossinkine)
- JhonnyL
- Haralan Dobrev (hkdobrev)
- hossein zolfi (ocean)
@@ -449,10 +455,10 @@ Symfony is the result of the work of many people who made the code better
- Philipp Kräutli (pkraeutli)
- Grzegorz (Greg) Zdanowski (kiler129)
- Iker Ibarguren (ikerib)
+ - Dimitri Gritsajuk (ottaviano)
- Kirill chEbba Chebunin (chebba)
- Rokas Mikalkėnas (rokasm)
- Greg Thornton (xdissent)
- - Martin Hujer (martinhujer)
- Alex Bowers
- Philipp Cordes
- Costin Bereveanu (schniper)
@@ -473,6 +479,7 @@ Symfony is the result of the work of many people who made the code better
- Endre Fejes
- Tobias Naumann (tna)
- Daniel Beyer
+ - Timothée Barray (tyx)
- Shein Alexey
- Romain Gautier (mykiwi)
- Joe Lencioni
@@ -489,7 +496,6 @@ Symfony is the result of the work of many people who made the code better
- Xavier HAUSHERR
- Albert Jessurum (ajessu)
- Laszlo Korte
- - Jesse Rushlow (geeshoe)
- Miha Vrhovnik
- Alessandro Desantis
- hubert lecorche (hlecorche)
@@ -501,6 +507,7 @@ Symfony is the result of the work of many people who made the code better
- Christophe L. (christophelau)
- Sander Toonen (xatoo)
- Anthon Pang (robocoder)
+ - Marko Kaznovac (kaznovac)
- Sébastien Santoro (dereckson)
- Brian King
- Michel Salib (michelsalib)
@@ -524,7 +531,6 @@ Symfony is the result of the work of many people who made the code better
- Mihai Stancu
- Ivan Nikolaev (destillat)
- Gildas Quéméner (gquemener)
- - Alessandro Lai (jean85)
- Desjardins Jérôme (jewome62)
- Arturs Vonda
- Josip Kruslin
@@ -561,7 +567,6 @@ Symfony is the result of the work of many people who made the code better
- Marek Pietrzak
- Luc Vieillescazes (iamluc)
- franek (franek)
- - soyuka
- Raulnet
- Christian Wahler
- Giso Stallenberg (gisostallenberg)
@@ -571,7 +576,6 @@ Symfony is the result of the work of many people who made the code better
- HypeMC
- Soufian EZ-ZANTAR (soezz)
- Zander Baldwin
- - Gocha Ossinkine (ossinkine)
- Adam Harvey
- Anton Bakai
- Martin Auswöger
@@ -603,9 +607,9 @@ Symfony is the result of the work of many people who made the code better
- Philipp Rieber (bicpi)
- Manuel de Ruiter (manuel)
- Nathanael Noblet (gnat)
- - Dimitri Gritsajuk (ottaviano)
- nikos.sotiropoulos
- Eduardo Oliveira (entering)
+ - Oleksii Zhurbytskyi
- Ilya Antipenko (aivus)
- Ricardo Oliveira (ricardolotr)
- Roy Van Ginneken (rvanginneken)
@@ -645,9 +649,9 @@ Symfony is the result of the work of many people who made the code better
- Gábor Fási
- DUPUCH (bdupuch)
- Nate (frickenate)
- - Timothée Barray (tyx)
- jhonnyL
- Jacek Jędrzejewski (jacek.jedrzejewski)
+ - Stefan Kruppa
- sasezaki
- Bozhidar Hristov (warxcell)
- Dawid Pakuła (zulusx)
@@ -684,7 +688,6 @@ Symfony is the result of the work of many people who made the code better
- Pavel Campr (pcampr)
- Andrii Dembitskyi
- Johnny Robeson (johnny)
- - Marko Kaznovac (kaznovac)
- Guilliam Xavier
- Disquedur
- Michiel Boeckaert (milio)
@@ -711,6 +714,7 @@ Symfony is the result of the work of many people who made the code better
- vitaliytv
- Philippe Segatori
- Dalibor Karlović (dkarlovi)
+ - Andrey Sevastianov
- Sebastian Blum
- Alexis Lefebvre
- aubx
@@ -728,6 +732,7 @@ Symfony is the result of the work of many people who made the code better
- Sinan Eldem
- BoShurik
- Alexandre Dupuy (satchette)
+ - Michel Hunziker
- Malte Blättermann
- Simeon Kolev (simeon_kolev9)
- Joost van Driel (j92)
@@ -741,7 +746,6 @@ Symfony is the result of the work of many people who made the code better
- Stefan Gehrig (sgehrig)
- Hany el-Kerdany
- Wang Jingyu
- - Langlet Vincent (deviling)
- Åsmund Garfors
- Gunnstein Lye (glye)
- Maxime Douailin
@@ -847,8 +851,10 @@ Symfony is the result of the work of many people who made the code better
- Michael Lutz
- Koen Reiniers (koenre)
- jochenvdv
+ - Michel Roca (mroca)
- Reedy
- Arturas Smorgun (asarturas)
+ - Michał (bambucha15)
- Alexander Volochnev (exelenz)
- Michael Piecko
- Toni Peric (tperic)
@@ -879,12 +885,12 @@ Symfony is the result of the work of many people who made the code better
- Axel Guckelsberger (guite)
- Jose Gonzalez
- Jonathan (jls-esokia)
- - Oleksii Zhurbytskyi
- Dariusz Ruminski
- Joshua Nye
- Claudio Zizza
- Dave Marshall (davedevelopment)
- Jakub Kulhan (jakubkulhan)
+ - Nathan Dench (ndenc2)
- Shaharia Azam
- avorobiev
- stoccc
@@ -948,9 +954,11 @@ Symfony is the result of the work of many people who made the code better
- GDIBass
- Antoine Lamirault
- Adrien Lucas (adrienlucas)
+ - Jeroen Thora (bolle)
- Zhuravlev Alexander (scif)
- Stefano Degenkamp (steef)
- James Michael DuPont
+ - Carlos Buenosvinos (carlosbuenosvinos)
- Tom Klingenberg
- Christopher Hall (mythmakr)
- Patrick Dawkins (pjcdawkins)
@@ -982,6 +990,7 @@ Symfony is the result of the work of many people who made the code better
- Julie Hourcade (juliehde)
- Dmitry Parnas (parnas)
- Paul LE CORRE
+ - Loïc Beurlet
- Daniel Gorgan
- Tony Malzhacker
- Mathieu MARCHOIS
@@ -1006,7 +1015,6 @@ Symfony is the result of the work of many people who made the code better
- Jelle Kapitein
- Benoît Bourgeois
- mantulo
- - Stefan Kruppa
- corphi
- JoppeDC
- grizlik
@@ -1049,6 +1057,7 @@ Symfony is the result of the work of many people who made the code better
- Arno Geurts
- Adán Lobato (adanlobato)
- Ian Jenkins (jenkoian)
+ - Hugo Alliaume (kocal)
- Marcos Gómez Vilches (markitosgv)
- Matthew Davis (mdavis1982)
- Markus S. (staabm)
@@ -1062,6 +1071,7 @@ Symfony is the result of the work of many people who made the code better
- Daniel Cestari
- Matt Janssen
- David Lima
+ - Dmitriy Derepko
- Stéphane Delprat
- Brian Freytag (brianfreytag)
- Samuele Lilli (doncallisto)
@@ -1148,6 +1158,7 @@ Symfony is the result of the work of many people who made the code better
- Anton Babenko (antonbabenko)
- Irmantas Šiupšinskas (irmantas)
- Danilo Silva
+ - Giuseppe Campanelli
- Arnaud PETITPAS (apetitpa)
- Ken Stanley
- Zachary Tong (polyfractal)
@@ -1164,6 +1175,7 @@ Symfony is the result of the work of many people who made the code better
- Tero Alén (tero)
- Stanislav Kocanda
- DerManoMann
+ - MatTheCat
- Guillaume Royer
- Artem (digi)
- boite
@@ -1184,7 +1196,6 @@ Symfony is the result of the work of many people who made the code better
- Danijel Obradović
- Pablo Borowicz
- Mathieu Santostefano
- - Michel Hunziker
- Arjan Keeman
- Máximo Cuadros (mcuadros)
- Lukas Mencl
@@ -1277,7 +1288,6 @@ Symfony is the result of the work of many people who made the code better
- Jakub Sacha
- Olaf Klischat
- orlovv
- - Andrey Sevastianov
- Claude Dioudonnat
- Jonathan Hedstrom
- Peter Smeets (darkspartan)
@@ -1297,12 +1307,10 @@ Symfony is the result of the work of many people who made the code better
- James Hudson
- Stephen Clouse
- e-ivanov
- - Michał (bambucha15)
- Benjamin Dos Santos
- Einenlum
- Jérémy Jarrié (gagnar)
- Jochen Bayer (jocl)
- - Michel Roca (mroca)
- Patrick Carlo-Hickman
- Bruno MATEU
- Jeremy Bush
@@ -1416,6 +1424,7 @@ Symfony is the result of the work of many people who made the code better
- Florian Hermann (fhermann)
- Mo Di (modi)
- Pablo Schläpfer
+ - Christian Rishøj
- Patrick Berenschot
- SuRiKmAn
- Gert de Pagter
@@ -1423,6 +1432,7 @@ Symfony is the result of the work of many people who made the code better
- David Négrier (moufmouf)
- Quique Porta (quiqueporta)
- mohammadreza honarkhah
+ - Artem Oliynyk (artemoliynyk)
- Andrea Quintino (dirk39)
- Tomasz Szymczyk (karion)
- Alex Vasilchenko
@@ -1453,6 +1463,7 @@ Symfony is the result of the work of many people who made the code better
- Andrei Igna
- Adam Prickett
- azine
+ - Anton Kroshilin
- Dawid Sajdak
- Ludek Stepan
- Aaron Stephens (astephens)
@@ -1605,6 +1616,7 @@ Symfony is the result of the work of many people who made the code better
- Robert Queck
- Peter Bouwdewijn
- mlively
+ - Wouter Diesveld
- Amine Matmati
- caalholm
- Nouhail AL FIDI (alfidi)
@@ -1612,6 +1624,7 @@ Symfony is the result of the work of many people who made the code better
- Felipy Tavares Amorim (felipyamorim)
- Guillaume Loulier (guikingone)
- Klaus Silveira (klaussilveira)
+ - Pedro Casado (pdr33n)
- Pierre Grimaud (pgrimaud)
- Thomas Chmielowiec (chmielot)
- Jānis Lukss
@@ -1724,6 +1737,7 @@ Symfony is the result of the work of many people who made the code better
- Stanislav Gamayunov (happyproff)
- Iwan van Staveren (istaveren)
- Alexander McCullagh (mccullagh)
+ - Paul L McNeely (mcneely)
- Povilas S. (povilas)
- Laurent Negre (raulnet)
- Evrard Boulou
@@ -1809,6 +1823,7 @@ Symfony is the result of the work of many people who made the code better
- Mathieu Dewet (mdewet)
- Nicolas Tallefourtané (nicolab)
- Botond Dani (picur)
+ - Rémi Faivre (rfv)
- Romaric Drigon (romaricdrigon)
- Thierry Marianne (thierrymarianne)
- Nick Stemerdink
@@ -1816,6 +1831,7 @@ Symfony is the result of the work of many people who made the code better
- jjanvier
- Julius Beckmann
- loru88
+ - Thibaut Salanon
- Romain Dorgueil
- Christopher Parotat
- Dennis Haarbrink
@@ -1860,6 +1876,7 @@ Symfony is the result of the work of many people who made the code better
- Chris
- Farid Jalilov
- Florent Olivaud
+ - Eric Hertwig
- JakeFr
- Simon Sargeant
- efeen
@@ -1920,6 +1937,7 @@ Symfony is the result of the work of many people who made the code better
- Michael van Tricht
- ReScO
- Tim Strehle
+ - Sébastien COURJEAN
- Sam Ward
- Michael Voříšek
- Walther Lalk
@@ -1972,6 +1990,7 @@ Symfony is the result of the work of many people who made the code better
- Martijn Boers (plebian)
- Pedro Magalhães (pmmaga)
- Rares Vlaseanu (raresvla)
+ - Sergii Dolgushev (serhey)
- tante kinast (tante)
- Stephen Lewis (tehanomalousone)
- Ahmed Hannachi (tiecoders)
@@ -1981,6 +2000,7 @@ Symfony is the result of the work of many people who made the code better
- Darryl Hein (xmmedia)
- Sadicov Vladimir (xtech)
- Kevin EMO (zarcox)
+ - sdkawata
- Andrzej
- Alexander Zogheb
- Rémi Blaise
@@ -2016,6 +2036,7 @@ Symfony is the result of the work of many people who made the code better
- Ashura
- Götz Gottwald
- Veres Lajos
+ - Ernest Hymel
- Nick Chiu
- grifx
- Robert Campbell
@@ -2045,6 +2066,7 @@ Symfony is the result of the work of many people who made the code better
- Rowan Manning
- Per Modin
- David Windell
+ - Christian Scheb
- Gabriel Birke
- skafandri
- Derek Bonner
@@ -2088,7 +2110,6 @@ Symfony is the result of the work of many people who made the code better
- baron (bastien)
- Rosio (ben-rosio)
- Simon Paarlberg (blamh)
- - Jeroen Thora (bolle)
- Brieuc THOMAS (brieucthomas)
- Masao Maeda (brtriver)
- Damien Harper (damien.harper)
@@ -2148,6 +2169,7 @@ Symfony is the result of the work of many people who made the code better
- Michael Orlitzky
- Nicolas A. Bérard-Nault
- Quentin Favrie
+ - Matthias Derer
- Saem Ghani
- Stefan Oderbolz
- Curtis
@@ -2173,7 +2195,6 @@ Symfony is the result of the work of many people who made the code better
- Daniele Cesarini (ijanki)
- Ismail Asci (ismailasci)
- Jeffrey Moelands (jeffreymoelands)
- - Hugo Alliaume (kocal)
- Simon CONSTANS (kosssi)
- Dennis Langen (nijusan)
- Paulius Jarmalavičius (pjarmalavicius)
@@ -2218,6 +2239,7 @@ Symfony is the result of the work of many people who made the code better
- Antonio Angelino
- Jens Schulze
- Matt Fields
+ - Olatunbosun Egberinde
- Niklas Keller
- Andras Debreczeni
- Vladimir Sazhin
@@ -2352,11 +2374,11 @@ Symfony is the result of the work of many people who made the code better
- Karolis Daužickas
- Nicolas
- Sergio Santoro
- - Dmitriy Derepko
- tirnanog06
- phc
- Дмитрий Пацура
- Signor Pedro
+ - Matthias Larisch
- ilyes kooli
- Ilia Lazarev
- Michaël VEROUX
@@ -2388,8 +2410,10 @@ Symfony is the result of the work of many people who made the code better
- Christian Gripp (core23)
- Christoph Schaefer (cvschaefer)
- Damon Jones (damon__jones)
+ - Cătălin Dan (dancatalin)
- Łukasz Giza (destroyer)
- Daniel Londero (dlondero)
+ - Dmitrii Tarasov (dtarasov)
- Sebastian Landwehr (dword123)
- Adel ELHAIBA (eadel)
- Damián Nohales (eagleoneraptor)
diff --git a/src/Symfony/Bridge/Doctrine/SchemaListener/MessengerTransportDoctrineSchemaSubscriber.php b/src/Symfony/Bridge/Doctrine/SchemaListener/MessengerTransportDoctrineSchemaSubscriber.php
index 3311e6316825..5b3798eb3918 100644
--- a/src/Symfony/Bridge/Doctrine/SchemaListener/MessengerTransportDoctrineSchemaSubscriber.php
+++ b/src/Symfony/Bridge/Doctrine/SchemaListener/MessengerTransportDoctrineSchemaSubscriber.php
@@ -89,9 +89,16 @@ public function onSchemaCreateTable(SchemaCreateTableEventArgs $event): void
public function getSubscribedEvents(): array
{
- return [
- ToolEvents::postGenerateSchema,
- Events::onSchemaCreateTable,
- ];
+ $subscribedEvents = [];
+
+ if (class_exists(ToolEvents::class)) {
+ $subscribedEvents[] = ToolEvents::postGenerateSchema;
+ }
+
+ if (class_exists(Events::class)) {
+ $subscribedEvents[] = Events::onSchemaCreateTable;
+ }
+
+ return $subscribedEvents;
}
}
diff --git a/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php b/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php
index 41330e7971b5..527b055b2807 100644
--- a/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php
+++ b/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php
@@ -43,6 +43,10 @@ public function postGenerateSchema(GenerateSchemaEventArgs $event): void
public function getSubscribedEvents(): array
{
+ if (!class_exists(ToolEvents::class)) {
+ return [];
+ }
+
return [
ToolEvents::postGenerateSchema,
];
diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php
index eda901d0541e..9ac53b87f661 100644
--- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php
+++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php
@@ -12,6 +12,7 @@
namespace Symfony\Bridge\Doctrine\Security\RememberMe;
use Doctrine\DBAL\Connection;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
@@ -63,7 +64,7 @@ public function loadTokenBySeries(string $series)
$paramValues = ['series' => $series];
$paramTypes = ['series' => \PDO::PARAM_STR];
$stmt = $this->conn->executeQuery($sql, $paramValues, $paramTypes);
- $row = method_exists($stmt, 'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch(\PDO::FETCH_ASSOC);
+ $row = $stmt instanceof Result ? $stmt->fetchAssociative() : $stmt->fetch(\PDO::FETCH_ASSOC);
if ($row) {
return new PersistentToken($row['class'], $row['username'], $series, $row['value'], new \DateTime($row['last_used']));
diff --git a/src/Symfony/Bridge/PhpUnit/ExpectDeprecationTrait.php b/src/Symfony/Bridge/PhpUnit/ExpectDeprecationTrait.php
index 0db391d12aba..fd7f2c80b84f 100644
--- a/src/Symfony/Bridge/PhpUnit/ExpectDeprecationTrait.php
+++ b/src/Symfony/Bridge/PhpUnit/ExpectDeprecationTrait.php
@@ -11,21 +11,20 @@
namespace Symfony\Bridge\PhpUnit;
-use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait;
+use Symfony\Bridge\PhpUnit\Legacy\ExpectDeprecationTraitBeforeV8_4;
+use Symfony\Bridge\PhpUnit\Legacy\ExpectDeprecationTraitForV8_4;
-trait ExpectDeprecationTrait
-{
+if (version_compare(\PHPUnit\Runner\Version::id(), '8.4.0', '<')) {
+ trait ExpectDeprecationTrait
+ {
+ use ExpectDeprecationTraitBeforeV8_4;
+ }
+} else {
/**
- * @param string $message
- *
- * @return void
+ * @method void expectDeprecation(string $message)
*/
- protected function expectDeprecation($message)
+ trait ExpectDeprecationTrait
{
- if (!SymfonyTestsListenerTrait::$previousErrorHandler) {
- SymfonyTestsListenerTrait::$previousErrorHandler = set_error_handler([SymfonyTestsListenerTrait::class, 'handleError']);
- }
-
- SymfonyTestsListenerTrait::$expectedDeprecations[] = $message;
+ use ExpectDeprecationTraitForV8_4;
}
}
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/ExpectDeprecationTraitBeforeV8_4.php b/src/Symfony/Bridge/PhpUnit/Legacy/ExpectDeprecationTraitBeforeV8_4.php
new file mode 100644
index 000000000000..47c8226e6e55
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/ExpectDeprecationTraitBeforeV8_4.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Legacy;
+
+/**
+ * @internal, use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait instead.
+ */
+trait ExpectDeprecationTraitBeforeV8_4
+{
+ /**
+ * @param string $message
+ *
+ * @return void
+ */
+ protected function expectDeprecation($message)
+ {
+ if (!SymfonyTestsListenerTrait::$previousErrorHandler) {
+ SymfonyTestsListenerTrait::$previousErrorHandler = set_error_handler([SymfonyTestsListenerTrait::class, 'handleError']);
+ }
+
+ SymfonyTestsListenerTrait::$expectedDeprecations[] = $message;
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/ExpectDeprecationTraitForV8_4.php b/src/Symfony/Bridge/PhpUnit/Legacy/ExpectDeprecationTraitForV8_4.php
new file mode 100644
index 000000000000..ceaefdb0b3a2
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/ExpectDeprecationTraitForV8_4.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Legacy;
+
+/**
+ * @internal use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait instead.
+ */
+trait ExpectDeprecationTraitForV8_4
+{
+ /**
+ * @param string $message
+ */
+ public function expectDeprecation(): void
+ {
+ if (1 > func_num_args() || !\is_string($message = func_get_arg(0))) {
+ throw new \InvalidArgumentException(sprintf('The "%s()" method requires the string $message argument.', __FUNCTION__));
+ }
+
+ if (!SymfonyTestsListenerTrait::$previousErrorHandler) {
+ SymfonyTestsListenerTrait::$previousErrorHandler = set_error_handler([SymfonyTestsListenerTrait::class, 'handleError']);
+ }
+
+ SymfonyTestsListenerTrait::$expectedDeprecations[] = $message;
+ }
+
+ /**
+ * @internal use expectDeprecation() instead.
+ */
+ public function expectDeprecationMessage(string $message): void
+ {
+ throw new \BadMethodCallException(sprintf('The "%s()" method is not supported by Symfony\'s PHPUnit Bridge ExpectDeprecationTrait, pass the message to expectDeprecation() instead.', __FUNCTION__));
+ }
+
+ /**
+ * @internal use expectDeprecation() instead.
+ */
+ public function expectDeprecationMessageMatches(string $regularExpression): void
+ {
+ throw new \BadMethodCallException(sprintf('The "%s()" method is not supported by Symfony\'s PHPUnit Bridge ExpectDeprecationTrait.', __FUNCTION__));
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
index 70c2fcdbddcf..ca41113b373c 100644
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
@@ -182,6 +182,11 @@
}
}
+ $info += [
+ 'versions' => [],
+ 'requires' => ['php' => '*'],
+ ];
+
if (1 === \count($info['versions'])) {
$passthruOrFail("$COMPOSER create-project --ignore-platform-reqs --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi -s dev phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\"");
} else {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
index 343c2bbaf552..23ae45754456 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
@@ -197,7 +197,7 @@ protected function file($file, string $fileName = null, string $disposition = Re
*
* @throws \LogicException
*/
- protected function addFlash(string $type, string $message): void
+ protected function addFlash(string $type, $message): void
{
if (!$this->container->has('session')) {
throw new \LogicException('You can not use the addFlash method if sessions are disabled. Enable them in "config/packages/framework.yaml".');
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php
index 47195b48300f..3e2f2768edc1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php
@@ -28,8 +28,8 @@ class AddExpressionLanguageProvidersPass implements CompilerPassInterface
public function process(ContainerBuilder $container)
{
// routing
- if ($container->has('router')) {
- $definition = $container->findDefinition('router');
+ if ($container->has('router.default')) {
+ $definition = $container->findDefinition('router.default');
foreach ($container->findTaggedServiceIds('routing.expression_language_provider', true) as $id => $attributes) {
$definition->addMethodCall('addExpressionLanguageProvider', [new Reference($id)]);
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 666797f2039c..4517521c7643 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -1094,7 +1094,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
$defaultOptions['cache_dir'] = $config['cache_dir'];
$translator->setArgument(4, $defaultOptions);
- $translator->setArgument(6, $config['enabled_locales']);
+ $translator->setArgument(5, $config['enabled_locales']);
$container->setParameter('translator.logging', $config['logging']);
$container->setParameter('translator.default_path', $config['default_path']);
@@ -1379,7 +1379,7 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde
private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
- if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {
+ if (!class_exists(PropertyAccessor::class)) {
return;
}
@@ -1463,7 +1463,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
$chainLoader = $container->getDefinition('serializer.mapping.chain_loader');
- if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {
+ if (!class_exists(PropertyAccessor::class)) {
$container->removeAlias('serializer.property_accessor');
$container->removeDefinition('serializer.normalizer.object');
}
@@ -1472,7 +1472,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
$container->removeDefinition('serializer.encoder.yaml');
}
- if (!class_exists(UnwrappingDenormalizer::class)) {
+ if (!class_exists(UnwrappingDenormalizer::class) || !class_exists(PropertyAccessor::class)) {
$container->removeDefinition('serializer.denormalizer.unwrapping');
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php
index 540c97672c14..abb220b4aa2b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php
@@ -83,7 +83,6 @@ public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(function (ContainerBuilder $container) use ($loader) {
$container->loadFromExtension('framework', [
- 'secret' => '%env(APP_SECRET)%',
'router' => [
'resource' => 'kernel::loadRoutes',
'type' => 'service',
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index 99ffbabb82cd..71eb751a5d41 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -567,7 +567,7 @@
-
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml
index ef5ed701adea..bc7dd2028a39 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml
@@ -6,7 +6,6 @@
%kernel.cache_dir%/serialization.php
-
@@ -103,7 +102,6 @@
- null
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml
index 070908f3db35..7c10470d5196 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml
@@ -5,7 +5,6 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
- %kernel.cache_dir%/validation.php
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php
index 95ed74f8b9fc..1207a4820d10 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php
@@ -22,16 +22,16 @@ class AddExpressionLanguageProvidersPassTest extends TestCase
public function testProcessForRouter()
{
$container = new ContainerBuilder();
- $container->addCompilerPass(new AddExpressionLanguageProvidersPass(false));
+ $container->addCompilerPass(new AddExpressionLanguageProvidersPass());
$definition = new Definition('\stdClass');
$definition->addTag('routing.expression_language_provider');
$container->setDefinition('some_routing_provider', $definition->setPublic(true));
- $container->register('router', '\stdClass')->setPublic(true);
+ $container->register('router.default', '\stdClass')->setPublic(true);
$container->compile();
- $router = $container->getDefinition('router');
+ $router = $container->getDefinition('router.default');
$calls = $router->getMethodCalls();
$this->assertCount(1, $calls);
$this->assertEquals('addExpressionLanguageProvider', $calls[0][0]);
@@ -41,14 +41,14 @@ public function testProcessForRouter()
public function testProcessForRouterAlias()
{
$container = new ContainerBuilder();
- $container->addCompilerPass(new AddExpressionLanguageProvidersPass(false));
+ $container->addCompilerPass(new AddExpressionLanguageProvidersPass());
$definition = new Definition('\stdClass');
$definition->addTag('routing.expression_language_provider');
$container->setDefinition('some_routing_provider', $definition->setPublic(true));
$container->register('my_router', '\stdClass')->setPublic(true);
- $container->setAlias('router', 'my_router');
+ $container->setAlias('router.default', 'my_router');
$container->compile();
$router = $container->getDefinition('my_router');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index f17597589683..c4729771bbca 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -1049,7 +1049,6 @@ public function testSerializerEnabled()
$this->assertCount(2, $argument);
$this->assertEquals('Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', $argument[0]->getClass());
- $this->assertNull($container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1));
$this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.name_converter.metadata_aware')->getArgument(1));
$this->assertEquals(new Reference('property_info', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $container->getDefinition('serializer.normalizer.object')->getArgument(3));
$this->assertArrayHasKey('circular_reference_handler', $container->getDefinition('serializer.normalizer.object')->getArgument(6));
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfFeaturesPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfFeaturesPass.php
new file mode 100644
index 000000000000..5af1c49568ba
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfFeaturesPass.php
@@ -0,0 +1,68 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Security\Http\EventListener\CsrfProtectionListener;
+use Symfony\Component\Security\Http\EventListener\CsrfTokenClearingLogoutListener;
+
+/**
+ * @author Christian Flothmann
+ * @author Wouter de Jong
+ *
+ * @internal
+ */
+class RegisterCsrfFeaturesPass implements CompilerPassInterface
+{
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->has('security.csrf.token_storage')) {
+ return;
+ }
+
+ $this->registerCsrfProtectionListener($container);
+ $this->registerLogoutHandler($container);
+ }
+
+ private function registerCsrfProtectionListener(ContainerBuilder $container)
+ {
+ if (!$container->has('security.authenticator.manager')) {
+ return;
+ }
+
+ $container->register('security.listener.csrf_protection', CsrfProtectionListener::class)
+ ->addArgument(new Reference('security.csrf.token_storage'))
+ ->addTag('kernel.event_subscriber')
+ ->setPublic(false);
+ }
+
+ protected function registerLogoutHandler(ContainerBuilder $container)
+ {
+ if (!$container->has('security.logout_listener')) {
+ return;
+ }
+
+ $csrfTokenStorage = $container->findDefinition('security.csrf.token_storage');
+ $csrfTokenStorageClass = $container->getParameterBag()->resolveValue($csrfTokenStorage->getClass());
+
+ if (!is_subclass_of($csrfTokenStorageClass, 'Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface')) {
+ return;
+ }
+
+ $container->register('security.logout.listener.csrf_token_clearing', CsrfTokenClearingLogoutListener::class)
+ ->addArgument(new Reference('security.csrf.token_storage'))
+ ->addTag('kernel.event_subscriber')
+ ->setPublic(false);
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php
index 2d6960e1fe45..8cabb9d73d36 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php
@@ -11,32 +11,21 @@
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler;
-use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\Security\Http\EventListener\CsrfTokenClearingLogoutListener;
+
+trigger_deprecation('symfony/security-bundle', '5.1', 'The "%s" class is deprecated.', RegisterCsrfTokenClearingLogoutHandlerPass::class);
/**
- * @author Christian Flothmann
+ * @deprecated since symfony/security-bundle 5.1
*/
-class RegisterCsrfTokenClearingLogoutHandlerPass implements CompilerPassInterface
+class RegisterCsrfTokenClearingLogoutHandlerPass extends RegisterCsrfFeaturesPass
{
public function process(ContainerBuilder $container)
{
- if (!$container->has('security.logout_listener') || !$container->has('security.csrf.token_storage')) {
- return;
- }
-
- $csrfTokenStorage = $container->findDefinition('security.csrf.token_storage');
- $csrfTokenStorageClass = $container->getParameterBag()->resolveValue($csrfTokenStorage->getClass());
-
- if (!is_subclass_of($csrfTokenStorageClass, 'Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface')) {
+ if (!$container->has('security.csrf.token_storage')) {
return;
}
- $container->register('security.logout.listener.csrf_token_clearing', CsrfTokenClearingLogoutListener::class)
- ->addArgument(new Reference('security.csrf.token_storage'))
- ->addTag('kernel.event_subscriber')
- ->setPublic(false);
+ $this->registerLogoutHandler($container);
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index afa04d7cad7d..35cc7b1b4d91 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -168,14 +168,6 @@ public function load(array $configs, ContainerBuilder $container)
$container->getDefinition('security.authentication.guard_handler')
->replaceArgument(2, $this->statelessFirewallKeys);
- if ($this->authenticatorManagerEnabled) {
- foreach ($this->statelessFirewallKeys as $statelessFirewallId) {
- $container
- ->setDefinition('security.listener.session.'.$statelessFirewallId, new ChildDefinition('security.listener.session'))
- ->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$statelessFirewallId]);
- }
- }
-
if ($config['encoders']) {
$this->createEncoders($config['encoders'], $container);
}
@@ -373,6 +365,12 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
$contextKey = $firewall['context'] ?? $id;
$listeners[] = new Reference($contextListenerId = $this->createContextListener($container, $contextKey));
$sessionStrategyId = 'security.authentication.session_strategy';
+
+ if ($this->authenticatorManagerEnabled) {
+ $container
+ ->setDefinition('security.listener.session.'.$id, new ChildDefinition('security.listener.session'))
+ ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
+ }
} else {
$this->statelessFirewallKeys[] = $id;
$sessionStrategyId = 'security.authentication.session_strategy_noop';
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.xml
index 26e47613c102..65bc75594169 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.xml
@@ -63,12 +63,6 @@
class="Symfony\Component\Security\Http\EventListener\SessionStrategyListener"
abstract="true">
- stateless firewall keys
-
-
-
-
- addCompilerPass(new AddExpressionLanguageProvidersPass());
$container->addCompilerPass(new AddSecurityVotersPass());
$container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_BEFORE_REMOVING);
- $container->addCompilerPass(new RegisterCsrfTokenClearingLogoutHandlerPass());
+ $container->addCompilerPass(new RegisterCsrfFeaturesPass());
$container->addCompilerPass(new RegisterTokenUsageTrackingPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 200);
$container->addCompilerPass(new RegisterLdapLocatorPass());
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
index b7063f42a05b..b2595dc4346c 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
@@ -559,6 +559,48 @@ public function provideConfigureCustomAuthenticatorData()
];
}
+ public function testCompilesWithoutSessionListenerWithStatelessFirewallWithAuthenticationManager()
+ {
+ $container = $this->getRawContainer();
+
+ $firewallId = 'stateless_firewall';
+ $container->loadFromExtension('security', [
+ 'enable_authenticator_manager' => true,
+ 'firewalls' => [
+ $firewallId => [
+ 'pattern' => '/.*',
+ 'stateless' => true,
+ 'http_basic' => null,
+ ],
+ ],
+ ]);
+
+ $container->compile();
+
+ $this->assertFalse($container->has('security.listener.session.'.$firewallId));
+ }
+
+ public function testCompilesWithSessionListenerWithStatefulllFirewallWithAuthenticationManager()
+ {
+ $container = $this->getRawContainer();
+
+ $firewallId = 'statefull_firewall';
+ $container->loadFromExtension('security', [
+ 'enable_authenticator_manager' => true,
+ 'firewalls' => [
+ $firewallId => [
+ 'pattern' => '/.*',
+ 'stateless' => false,
+ 'http_basic' => null,
+ ],
+ ],
+ ]);
+
+ $container->compile();
+
+ $this->assertTrue($container->has('security.listener.session.'.$firewallId));
+ }
+
protected function getRawContainer()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
index c65e278021a0..58f3867c6f2f 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
@@ -131,13 +131,6 @@
removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading');
removeClass(ajaxToolbarPanel, 'sf-toolbar-status-red');
}
-
- addEventListener(document.querySelector('.sf-toolbar-ajax-clear'), 'click', function() {
- requestStack = [];
- renderAjaxRequests();
- successStreak = 4;
- document.querySelector('.sf-toolbar-ajax-request-list').innerHTML = '';
- });
};
var startAjaxRequest = function(index) {
@@ -506,6 +499,12 @@
setPreference('toolbar/displayState', 'block');
});
renderAjaxRequests();
+ addEventListener(document.querySelector('.sf-toolbar-ajax-clear'), 'click', function() {
+ requestStack = [];
+ renderAjaxRequests();
+ successStreak = 4;
+ document.querySelector('.sf-toolbar-ajax-request-list').innerHTML = '';
+ });
addEventListener(document.querySelector('.sf-toolbar-block-ajax'), 'mouseenter', function (event) {
var elem = document.querySelector('.sf-toolbar-block-ajax .sf-toolbar-info');
elem.scrollTop = elem.scrollHeight;
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php
index df3054bdea06..1d3bcf33cdcf 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Functional/WebProfilerBundleKernel.php
@@ -2,6 +2,7 @@
namespace Symfony\Bundle\WebProfilerBundle\Tests\Functional;
+use Psr\Log\NullLogger;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Bundle\TwigBundle\TwigBundle;
@@ -62,6 +63,11 @@ public function getLogDir()
return sys_get_temp_dir().'/log-'.spl_object_hash($this);
}
+ protected function build(ContainerBuilder $container)
+ {
+ $container->register('logger', NullLogger::class);
+ }
+
public function homepageController()
{
return new Response('Homepage Controller.');
diff --git a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
index a042ad8a2c1d..97ff79ec2873 100644
--- a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
@@ -11,8 +11,10 @@
namespace Symfony\Component\Cache\Adapter;
+use Doctrine\DBAL\Abstraction\Result;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception\TableNotFoundException;
@@ -217,15 +219,16 @@ protected function doFetch(array $ids)
foreach ($ids as $id) {
$stmt->bindValue(++$i, $id);
}
- $stmt->execute();
+ $result = $stmt->execute();
- if (method_exists($stmt, 'iterateNumeric')) {
- $stmt = $stmt->iterateNumeric();
+ if ($result instanceof Result) {
+ $result = $result->iterateNumeric();
} else {
$stmt->setFetchMode(\PDO::FETCH_NUM);
+ $result = $stmt;
}
- foreach ($stmt as $row) {
+ foreach ($result as $row) {
if (null === $row[1]) {
$expired[] = $row[0];
} else {
@@ -255,9 +258,9 @@ protected function doHave(string $id)
$stmt->bindValue(':id', $id);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
- $stmt->execute();
+ $result = $stmt->execute();
- return (bool) (method_exists($stmt, 'fetchOne') ? $stmt->fetchOne() : $stmt->fetchColumn());
+ return (bool) ($result instanceof DriverResult ? $result->fetchOne() : $stmt->fetchColumn());
}
/**
@@ -387,19 +390,19 @@ protected function doSave(array $values, int $lifetime)
foreach ($values as $id => $data) {
try {
- $stmt->execute();
+ $result = $stmt->execute();
} catch (TableNotFoundException $e) {
if (!$conn->isTransactionActive() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
$this->createTable();
}
- $stmt->execute();
+ $result = $stmt->execute();
} catch (\PDOException $e) {
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
$this->createTable();
}
- $stmt->execute();
+ $result = $stmt->execute();
}
- if (null === $driver && !$stmt->rowCount()) {
+ if (null === $driver && !($result instanceof DriverResult ? $result : $stmt)->rowCount()) {
try {
$insertStmt->execute();
} catch (DBALException $e) {
diff --git a/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php b/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php
index 05a88e086c3c..53d0ccc5db44 100644
--- a/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php
+++ b/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\Cache\Tests\Traits;
+use Doctrine\DBAL\Driver\Result;
+
trait PdoPruneableTrait
{
protected function isPruned($cache, string $name): bool
@@ -27,8 +29,8 @@ protected function isPruned($cache, string $name): bool
/** @var \Doctrine\DBAL\Statement|\PDOStatement $select */
$select = $getPdoConn->invoke($cache)->prepare('SELECT 1 FROM cache_items WHERE item_id LIKE :id');
$select->bindValue(':id', sprintf('%%%s', $name));
- $select->execute();
+ $result = $select->execute();
- return 1 !== (int) (method_exists($select, 'fetchOne') ? $select->fetchOne() : $select->fetch(\PDO::FETCH_COLUMN));
+ return 1 !== (int) ($result instanceof Result ? $result->fetchOne() : $select->fetch(\PDO::FETCH_COLUMN));
}
}
diff --git a/src/Symfony/Component/Console/Cursor.php b/src/Symfony/Component/Console/Cursor.php
index 9f8be9649c52..2da899b5564a 100644
--- a/src/Symfony/Component/Console/Cursor.php
+++ b/src/Symfony/Component/Console/Cursor.php
@@ -21,10 +21,10 @@ final class Cursor
private $output;
private $input;
- public function __construct(OutputInterface $output, $input = STDIN)
+ public function __construct(OutputInterface $output, $input = null)
{
$this->output = $output;
- $this->input = $input;
+ $this->input = $input ?? (\defined('STDIN') ? STDIN : fopen('php://input', 'r+'));
}
public function moveUp(int $lines = 1): self
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php
index a5d06f1f8da6..8229beb106ef 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php
@@ -198,9 +198,10 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
}
} elseif (\is_string($value)) {
if ('%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) {
- // Only array parameters are not inlined when dumped.
- $value = [];
- } elseif ($envPlaceholderUniquePrefix && false !== strpos($value, 'env_')) {
+ $value = $this->container->getParameter(substr($value, 1, -1));
+ }
+
+ if ($envPlaceholderUniquePrefix && \is_string($value) && false !== strpos($value, 'env_')) {
// If the value is an env placeholder that is either mixed with a string or with another env placeholder, then its resolved value will always be a string, so we don't need to resolve it.
// We don't need to change the value because it is already a string.
if ('' === preg_replace('/'.$envPlaceholderUniquePrefix.'_\w+_[a-f0-9]{32}/U', '', $value, -1, $c) && 1 === $c) {
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index c343de30357e..28e475c4600e 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -567,7 +567,7 @@ private function generateProxyClasses(): array
$proxyClass = explode(' ', $this->inlineRequires ? substr($proxyCode, \strlen($code)) : $proxyCode, 3)[1];
if ($this->asFiles || $this->namespace) {
- $proxyCode .= "\n\\class_alias(__NAMESPACE__.'\\\\$proxyClass', '$proxyClass', false);\n";
+ $proxyCode .= "\nif (!\\class_exists('$proxyClass', false)) {\n \\class_alias(__NAMESPACE__.'\\\\$proxyClass', '$proxyClass', false);\n}\n";
}
$proxyClasses[$proxyClass.'.php'] = $proxyCode;
@@ -1086,7 +1086,7 @@ private function addNewInstance(Definition $definition, string $return = '', str
// If the class is a string we can optimize away
if (0 === strpos($class, "'") && false === strpos($class, '$')) {
if ("''" === $class) {
- throw new RuntimeException(sprintf('Cannot dump definition: %s service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id ? 'The "'.$id.'"' : 'inline'));
+ throw new RuntimeException(sprintf('Cannot dump definition: "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id ? 'The "'.$id.'"' : 'inline'));
}
return $return.sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index 3cb0cba8040e..7bce62a5f7dc 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -205,11 +205,11 @@ private function parseDefinition(\DOMElement $service, string $file, Definition
$version = $deprecated[0]->getAttribute('version') ?: '';
if (!$deprecated[0]->hasAttribute('package')) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.', $file);
}
if (!$deprecated[0]->hasAttribute('version')) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" in "%s" is deprecated.', $file);
}
$alias->setDeprecated($package, $version, $message);
@@ -265,11 +265,11 @@ private function parseDefinition(\DOMElement $service, string $file, Definition
$version = $deprecated[0]->getAttribute('version') ?: '';
if ('' === $package) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.', $file);
}
if ('' === $version) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" in "%s" is deprecated.', $file);
}
$definition->setDeprecated($package, $version, $message);
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index a5eedb9db300..417e568ed0af 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -409,11 +409,11 @@ private function parseDefinition(string $id, $service, string $file, array $defa
$deprecation = \is_array($value) ? $value : ['message' => $value];
if (!isset($deprecation['package'])) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option in "%s" is deprecated.', $file);
}
if (!isset($deprecation['version'])) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file);
}
$alias->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message']);
@@ -478,11 +478,11 @@ private function parseDefinition(string $id, $service, string $file, array $defa
$deprecation = \is_array($service['deprecated']) ? $service['deprecated'] : ['message' => $service['deprecated']];
if (!isset($deprecation['package'])) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option in "%s" is deprecated.', $file);
}
if (!isset($deprecation['version'])) {
- trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option is deprecated.');
+ trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file);
}
$definition->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message']);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php
index 56589ae0d6f8..0aef6e89e3b5 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php
@@ -612,20 +612,6 @@ public function testProcessThrowsOnIterableTypeWhenScalarPassed()
$this->assertInstanceOf(\stdClass::class, $container->get('bar')->foo);
}
- public function testProcessResolveArrayParameters()
- {
- $container = new ContainerBuilder();
- $container->setParameter('ccc', ['foobar']);
-
- $container
- ->register('foobar', BarMethodCall::class)
- ->addMethodCall('setArray', ['%ccc%']);
-
- (new CheckTypeDeclarationsPass(true))->process($container);
-
- $this->addToAssertionCount(1);
- }
-
public function testProcessResolveExpressions()
{
$container = new ContainerBuilder();
@@ -791,4 +777,30 @@ public function testExpressionLanguageWithSyntheticService()
$this->addToAssertionCount(1);
}
+
+ public function testProcessResolveParameters()
+ {
+ putenv('ARRAY={"foo":"bar"}');
+
+ $container = new ContainerBuilder(new EnvPlaceholderParameterBag([
+ 'env_array_param' => '%env(json:ARRAY)%',
+ ]));
+ $container->setParameter('array_param', ['foobar']);
+ $container->setParameter('string_param', 'ccc');
+
+ $definition = $container->register('foobar', BarMethodCall::class);
+ $definition
+ ->addMethodCall('setArray', ['%array_param%'])
+ ->addMethodCall('setString', ['%string_param%']);
+
+ (new ResolveParameterPlaceHoldersPass())->process($container);
+
+ $definition->addMethodCall('setArray', ['%env_array_param%']);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->addToAssertionCount(1);
+
+ putenv('ARRAY=');
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarMethodCall.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarMethodCall.php
index 69f1a693a4c5..65437a63ec74 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarMethodCall.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarMethodCall.php
@@ -44,4 +44,8 @@ public function setCallable(callable $callable): void
public function setClosure(\Closure $closure): void
{
}
+
+ public function setString(string $string)
+ {
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
index f1dd4db45184..92c7299ac305 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
@@ -160,7 +160,9 @@ class FooClass_%s extends \Bar\FooClass implements \ProxyManager\Proxy\VirtualPr
%A
}
-\class_alias(__NAMESPACE__.'\\FooClass_%s', 'FooClass_%s', false);
+if (!\class_exists('FooClass_%s', false)) {
+ \class_alias(__NAMESPACE__.'\\FooClass_%s', 'FooClass_%s', false);
+}
[ProjectServiceContainer.preload.php] => expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "package" of the node "deprecated" is deprecated.');
+ $this->expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.');
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
@@ -442,7 +442,7 @@ public function testDeprecatedAliases()
*/
public function testDeprecatedAliaseWithoutPackageAndVersion()
{
- $this->expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "package" of the node "deprecated" is deprecated.');
+ $this->expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.');
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
index bd46044e4f56..ab9021ba265d 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
@@ -238,8 +238,8 @@ public function testDeprecatedAliases()
*/
public function testDeprecatedAliasesWithoutPackageAndVersion()
{
- $this->expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "package" of the "deprecated" option is deprecated.');
- $this->expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "version" of the "deprecated" option is deprecated.');
+ $this->expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "package" of the "deprecated" option in "%s" is deprecated.');
+ $this->expectDeprecation('Since symfony/dependency-injection 5.1: Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
diff --git a/src/Symfony/Component/ExpressionLanguage/Parser.php b/src/Symfony/Component/ExpressionLanguage/Parser.php
index 34658b97c0c0..fb9c321c4b9f 100644
--- a/src/Symfony/Component/ExpressionLanguage/Parser.php
+++ b/src/Symfony/Component/ExpressionLanguage/Parser.php
@@ -124,6 +124,9 @@ private function doParse(TokenStream $stream, ?array $names = []): Node\Node
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', $stream->current->type, $stream->current->value), $stream->current->cursor, $stream->getExpression());
}
+ $this->stream = null;
+ $this->names = null;
+
return $node;
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index bd7f553cdedc..b21c442a8354 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -72,7 +72,6 @@ public function validate($form, Constraint $formConstraint)
if ($groups instanceof GroupSequence) {
// Validate the data, the form AND nested fields in sequence
$violationsCount = $this->context->getViolations()->count();
- $fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s';
foreach ($groups->groups as $group) {
if ($validateDataGraph) {
@@ -91,7 +90,8 @@ public function validate($form, Constraint $formConstraint)
// in different steps without breaking early enough
$this->resolvedGroups[$field] = (array) $group;
$fieldFormConstraint = new Form();
- $validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $fieldFormConstraint);
+ $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath());
+ $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint);
}
}
@@ -100,8 +100,6 @@ public function validate($form, Constraint $formConstraint)
}
}
} else {
- $fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s';
-
if ($validateDataGraph) {
$validator->atPath('data')->validate($data, null, $groups);
}
@@ -138,7 +136,8 @@ public function validate($form, Constraint $formConstraint)
if ($field->isSubmitted()) {
$this->resolvedGroups[$field] = $groups;
$fieldFormConstraint = new Form();
- $validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $fieldFormConstraint);
+ $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath());
+ $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint);
}
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php
new file mode 100644
index 000000000000..4a91f16af066
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php
@@ -0,0 +1,362 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Validator\Constraints;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormFactoryBuilder;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Validator\Constraints\Collection;
+use Symfony\Component\Validator\Constraints\Expression;
+use Symfony\Component\Validator\Constraints\GroupSequence;
+use Symfony\Component\Validator\Constraints\Length;
+use Symfony\Component\Validator\Constraints\NotBlank;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
+use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
+use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader;
+use Symfony\Component\Validator\Validation;
+
+class FormValidatorFunctionalTest extends TestCase
+{
+ private $validator;
+ private $formFactory;
+
+ protected function setUp(): void
+ {
+ $this->validator = Validation::createValidatorBuilder()
+ ->setMetadataFactory(new LazyLoadingMetadataFactory(new StaticMethodLoader()))
+ ->getValidator();
+ $this->formFactory = (new FormFactoryBuilder())
+ ->addExtension(new ValidatorExtension($this->validator))
+ ->getFormFactory();
+ }
+
+ public function testDataConstraintsInvalidateFormEvenIfFieldIsNotSubmitted()
+ {
+ $form = $this->formFactory->create(FooType::class);
+ $form->submit(['baz' => 'foobar'], false);
+
+ $this->assertTrue($form->isSubmitted());
+ $this->assertFalse($form->isValid());
+ $this->assertFalse($form->get('bar')->isSubmitted());
+ $this->assertCount(1, $form->get('bar')->getErrors());
+ }
+
+ public function testFieldConstraintsDoNotInvalidateFormIfFieldIsNotSubmitted()
+ {
+ $form = $this->formFactory->create(FooType::class);
+ $form->submit(['bar' => 'foobar'], false);
+
+ $this->assertTrue($form->isSubmitted());
+ $this->assertTrue($form->isValid());
+ }
+
+ public function testFieldConstraintsInvalidateFormIfFieldIsSubmitted()
+ {
+ $form = $this->formFactory->create(FooType::class);
+ $form->submit(['bar' => 'foobar', 'baz' => ''], false);
+
+ $this->assertTrue($form->isSubmitted());
+ $this->assertFalse($form->isValid());
+ $this->assertTrue($form->get('bar')->isSubmitted());
+ $this->assertTrue($form->get('bar')->isValid());
+ $this->assertTrue($form->get('baz')->isSubmitted());
+ $this->assertFalse($form->get('baz')->isValid());
+ }
+
+ public function testNonCompositeConstraintValidatedOnce()
+ {
+ $form = $this->formFactory->create(TextType::class, null, [
+ 'constraints' => [new NotBlank(['groups' => ['foo', 'bar']])],
+ 'validation_groups' => ['foo', 'bar'],
+ ]);
+ $form->submit('');
+
+ $violations = $this->validator->validate($form);
+
+ $this->assertCount(1, $violations);
+ $this->assertSame('This value should not be blank.', $violations[0]->getMessage());
+ $this->assertSame('data', $violations[0]->getPropertyPath());
+ }
+
+ public function testCompositeConstraintValidatedInEachGroup()
+ {
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'constraints' => [
+ new Collection([
+ 'field1' => new NotBlank([
+ 'groups' => ['field1'],
+ ]),
+ 'field2' => new NotBlank([
+ 'groups' => ['field2'],
+ ]),
+ ]),
+ ],
+ 'validation_groups' => ['field1', 'field2'],
+ ]);
+ $form->add('field1');
+ $form->add('field2');
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $violations = $this->validator->validate($form);
+
+ $this->assertCount(2, $violations);
+ $this->assertSame('This value should not be blank.', $violations[0]->getMessage());
+ $this->assertSame('data[field1]', $violations[0]->getPropertyPath());
+ $this->assertSame('This value should not be blank.', $violations[1]->getMessage());
+ $this->assertSame('data[field2]', $violations[1]->getPropertyPath());
+ }
+
+ public function testCompositeConstraintValidatedInSequence()
+ {
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'constraints' => [
+ new Collection([
+ 'field1' => new NotBlank([
+ 'groups' => ['field1'],
+ ]),
+ 'field2' => new NotBlank([
+ 'groups' => ['field2'],
+ ]),
+ ]),
+ ],
+ 'validation_groups' => new GroupSequence(['field1', 'field2']),
+ ]);
+ $form->add('field1');
+ $form->add('field2');
+
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $violations = $this->validator->validate($form);
+
+ $this->assertCount(1, $violations);
+ $this->assertSame('This value should not be blank.', $violations[0]->getMessage());
+ $this->assertSame('data[field1]', $violations[0]->getPropertyPath());
+ }
+
+ public function testFieldsValidateInSequence()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'validation_groups' => new GroupSequence(['group1', 'group2']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
+ ])
+ ->add('bar', TextType::class, [
+ 'constraints' => [new NotBlank(['groups' => ['group2']])],
+ ])
+ ;
+
+ $form->submit(['foo' => 'invalid', 'bar' => null]);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ }
+
+ public function testFieldsValidateInSequenceWithNestedGroupsArray()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'validation_groups' => new GroupSequence([['group1', 'group2'], 'group3']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
+ ])
+ ->add('bar', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group2']] + $allowEmptyString)],
+ ])
+ ->add('baz', TextType::class, [
+ 'constraints' => [new NotBlank(['groups' => ['group3']])],
+ ])
+ ;
+
+ $form->submit(['foo' => 'invalid', 'bar' => 'invalid', 'baz' => null]);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(2, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ $this->assertInstanceOf(Length::class, $errors[1]->getCause()->getConstraint());
+ }
+
+ public function testConstraintsInDifferentGroupsOnSingleField()
+ {
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'validation_groups' => new GroupSequence(['group1', 'group2']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [
+ new NotBlank([
+ 'groups' => ['group1'],
+ ]),
+ new Length([
+ 'groups' => ['group2'],
+ 'max' => 3,
+ ]),
+ ],
+ ]);
+ $form->submit([
+ 'foo' => 'test@example.com',
+ ]);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertFalse($form->isValid());
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ }
+
+ public function testCascadeValidationToChildFormsUsingPropertyPaths()
+ {
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'validation_groups' => ['group1', 'group2'],
+ ])
+ ->add('field1', null, [
+ 'constraints' => [new NotBlank(['groups' => 'group1'])],
+ 'property_path' => '[foo]',
+ ])
+ ->add('field2', null, [
+ 'constraints' => [new NotBlank(['groups' => 'group2'])],
+ 'property_path' => '[bar]',
+ ])
+ ;
+
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $violations = $this->validator->validate($form);
+
+ $this->assertCount(2, $violations);
+ $this->assertSame('This value should not be blank.', $violations[0]->getMessage());
+ $this->assertSame('children[field1].data', $violations[0]->getPropertyPath());
+ $this->assertSame('This value should not be blank.', $violations[1]->getMessage());
+ $this->assertSame('children[field2].data', $violations[1]->getPropertyPath());
+ }
+
+ public function testCascadeValidationToChildFormsUsingPropertyPathsValidatedInSequence()
+ {
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'validation_groups' => new GroupSequence(['group1', 'group2']),
+ ])
+ ->add('field1', null, [
+ 'constraints' => [new NotBlank(['groups' => 'group1'])],
+ 'property_path' => '[foo]',
+ ])
+ ->add('field2', null, [
+ 'constraints' => [new NotBlank(['groups' => 'group2'])],
+ 'property_path' => '[bar]',
+ ])
+ ;
+
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $violations = $this->validator->validate($form);
+
+ $this->assertCount(1, $violations);
+ $this->assertSame('This value should not be blank.', $violations[0]->getMessage());
+ $this->assertSame('children[field1].data', $violations[0]->getPropertyPath());
+ }
+
+ public function testContextIsPopulatedWithFormBeingValidated()
+ {
+ $form = $this->formFactory->create(FormType::class)
+ ->add('field1', null, [
+ 'constraints' => [new Expression([
+ 'expression' => '!this.getParent().get("field2").getData()',
+ ])],
+ ])
+ ->add('field2')
+ ;
+
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $violations = $this->validator->validate($form);
+
+ $this->assertCount(0, $violations);
+ }
+
+ public function testContextIsPopulatedWithFormBeingValidatedUsingGroupSequence()
+ {
+ $form = $this->formFactory->create(FormType::class, null, [
+ 'validation_groups' => new GroupSequence(['group1']),
+ ])
+ ->add('field1', null, [
+ 'constraints' => [new Expression([
+ 'expression' => '!this.getParent().get("field2").getData()',
+ 'groups' => ['group1'],
+ ])],
+ ])
+ ->add('field2')
+ ;
+
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $violations = $this->validator->validate($form);
+
+ $this->assertCount(0, $violations);
+ }
+}
+
+class Foo
+{
+ public $bar;
+ public $baz;
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata)
+ {
+ $metadata->addPropertyConstraint('bar', new NotBlank());
+ }
+}
+
+class FooType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('bar')
+ ->add('baz', null, [
+ 'constraints' => [new NotBlank()],
+ ])
+ ;
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefault('data_class', Foo::class);
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
index b6e0cfa16499..2c0a728610e1 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
@@ -18,13 +18,13 @@
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator;
+use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormFactoryBuilder;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\SubmitButtonBuilder;
use Symfony\Component\Translation\IdentityTranslator;
-use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
@@ -52,7 +52,9 @@ class FormValidatorTest extends ConstraintValidatorTestCase
protected function setUp(): void
{
$this->dispatcher = new EventDispatcher();
- $this->factory = (new FormFactoryBuilder())->getFormFactory();
+ $this->factory = (new FormFactoryBuilder())
+ ->addExtension(new ValidatorExtension(Validation::createValidator()))
+ ->getFormFactory();
parent::setUp();
@@ -746,96 +748,6 @@ public function testCauseForNotAllowedExtraFieldsIsTheFormConstraint()
$this->assertSame($constraint, $context->getViolations()->get(0)->getConstraint());
}
- public function testNonCompositeConstraintValidatedOnce()
- {
- $form = $this
- ->getBuilder('form', null, [
- 'constraints' => [new NotBlank(['groups' => ['foo', 'bar']])],
- 'validation_groups' => ['foo', 'bar'],
- ])
- ->setCompound(false)
- ->getForm();
- $form->submit('');
-
- $context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
- $this->validator->initialize($context);
- $this->validator->validate($form, new Form());
-
- $this->assertCount(1, $context->getViolations());
- $this->assertSame('This value should not be blank.', $context->getViolations()[0]->getMessage());
- $this->assertSame('data', $context->getViolations()[0]->getPropertyPath());
- }
-
- public function testCompositeConstraintValidatedInEachGroup()
- {
- $form = $this->getBuilder('form', null, [
- 'constraints' => [
- new Collection([
- 'field1' => new NotBlank([
- 'groups' => ['field1'],
- ]),
- 'field2' => new NotBlank([
- 'groups' => ['field2'],
- ]),
- ]),
- ],
- 'validation_groups' => ['field1', 'field2'],
- ])
- ->setData([])
- ->setCompound(true)
- ->setDataMapper(new PropertyPathMapper())
- ->getForm();
- $form->add($this->getForm('field1'));
- $form->add($this->getForm('field2'));
- $form->submit([
- 'field1' => '',
- 'field2' => '',
- ]);
-
- $context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
- $this->validator->initialize($context);
- $this->validator->validate($form, new Form());
-
- $this->assertCount(2, $context->getViolations());
- $this->assertSame('This value should not be blank.', $context->getViolations()[0]->getMessage());
- $this->assertSame('data[field1]', $context->getViolations()[0]->getPropertyPath());
- $this->assertSame('This value should not be blank.', $context->getViolations()[1]->getMessage());
- $this->assertSame('data[field2]', $context->getViolations()[1]->getPropertyPath());
- }
-
- public function testCompositeConstraintValidatedInSequence()
- {
- $form = $this->getCompoundForm([], [
- 'constraints' => [
- new Collection([
- 'field1' => new NotBlank([
- 'groups' => ['field1'],
- ]),
- 'field2' => new NotBlank([
- 'groups' => ['field2'],
- ]),
- ]),
- ],
- 'validation_groups' => new GroupSequence(['field1', 'field2']),
- ])
- ->add($this->getForm('field1'))
- ->add($this->getForm('field2'))
- ;
-
- $form->submit([
- 'field1' => '',
- 'field2' => '',
- ]);
-
- $context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
- $this->validator->initialize($context);
- $this->validator->validate($form, new Form());
-
- $this->assertCount(1, $context->getViolations());
- $this->assertSame('This value should not be blank.', $context->getViolations()[0]->getMessage());
- $this->assertSame('data[field1]', $context->getViolations()[0]->getPropertyPath());
- }
-
protected function createValidator()
{
return new FormValidator();
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
index 4324541cb7ff..4a12acf4126b 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
@@ -12,23 +12,12 @@
namespace Symfony\Component\Form\Tests\Extension\Validator;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\Extension\Core\Type\FormType;
-use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Validator\Constraints\Form as FormConstraint;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser;
use Symfony\Component\Form\Form;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\Form\FormFactoryBuilder;
-use Symfony\Component\OptionsResolver\OptionsResolver;
-use Symfony\Component\Validator\Constraints\GroupSequence;
-use Symfony\Component\Validator\Constraints\Length;
-use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
-use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
-use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory;
use Symfony\Component\Validator\Validation;
@@ -57,155 +46,4 @@ public function test2Dot5ValidationApi()
$this->assertSame(TraversalStrategy::NONE, $metadata->traversalStrategy);
$this->assertCount(0, $metadata->getPropertyMetadata('children'));
}
-
- public function testDataConstraintsInvalidateFormEvenIfFieldIsNotSubmitted()
- {
- $form = $this->createForm(FooType::class);
- $form->submit(['baz' => 'foobar'], false);
-
- $this->assertTrue($form->isSubmitted());
- $this->assertFalse($form->isValid());
- $this->assertFalse($form->get('bar')->isSubmitted());
- $this->assertCount(1, $form->get('bar')->getErrors());
- }
-
- public function testFieldConstraintsDoNotInvalidateFormIfFieldIsNotSubmitted()
- {
- $form = $this->createForm(FooType::class);
- $form->submit(['bar' => 'foobar'], false);
-
- $this->assertTrue($form->isSubmitted());
- $this->assertTrue($form->isValid());
- }
-
- public function testFieldConstraintsInvalidateFormIfFieldIsSubmitted()
- {
- $form = $this->createForm(FooType::class);
- $form->submit(['bar' => 'foobar', 'baz' => ''], false);
-
- $this->assertTrue($form->isSubmitted());
- $this->assertFalse($form->isValid());
- $this->assertTrue($form->get('bar')->isSubmitted());
- $this->assertTrue($form->get('bar')->isValid());
- $this->assertTrue($form->get('baz')->isSubmitted());
- $this->assertFalse($form->get('baz')->isValid());
- }
-
- public function testFieldsValidateInSequence()
- {
- $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
-
- $form = $this->createForm(FormType::class, null, [
- 'validation_groups' => new GroupSequence(['group1', 'group2']),
- ])
- ->add('foo', TextType::class, [
- 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
- ])
- ->add('bar', TextType::class, [
- 'constraints' => [new NotBlank(['groups' => ['group2']])],
- ])
- ;
-
- $form->submit(['foo' => 'invalid', 'bar' => null]);
-
- $errors = $form->getErrors(true);
-
- $this->assertCount(1, $errors);
- $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
- }
-
- public function testFieldsValidateInSequenceWithNestedGroupsArray()
- {
- $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
-
- $form = $this->createForm(FormType::class, null, [
- 'validation_groups' => new GroupSequence([['group1', 'group2'], 'group3']),
- ])
- ->add('foo', TextType::class, [
- 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
- ])
- ->add('bar', TextType::class, [
- 'constraints' => [new Length(['min' => 10, 'groups' => ['group2']] + $allowEmptyString)],
- ])
- ->add('baz', TextType::class, [
- 'constraints' => [new NotBlank(['groups' => ['group3']])],
- ])
- ;
-
- $form->submit(['foo' => 'invalid', 'bar' => 'invalid', 'baz' => null]);
-
- $errors = $form->getErrors(true);
-
- $this->assertCount(2, $errors);
- $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
- $this->assertInstanceOf(Length::class, $errors[1]->getCause()->getConstraint());
- }
-
- public function testConstraintsInDifferentGroupsOnSingleField()
- {
- $form = $this->createForm(FormType::class, null, [
- 'validation_groups' => new GroupSequence(['group1', 'group2']),
- ])
- ->add('foo', TextType::class, [
- 'constraints' => [
- new NotBlank([
- 'groups' => ['group1'],
- ]),
- new Length([
- 'groups' => ['group2'],
- 'max' => 3,
- ]),
- ],
- ]);
- $form->submit([
- 'foo' => 'test@example.com',
- ]);
-
- $errors = $form->getErrors(true);
-
- $this->assertFalse($form->isValid());
- $this->assertCount(1, $errors);
- $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
- }
-
- private function createForm($type, $data = null, array $options = [])
- {
- $validator = Validation::createValidatorBuilder()
- ->setMetadataFactory(new LazyLoadingMetadataFactory(new StaticMethodLoader()))
- ->getValidator();
- $formFactoryBuilder = new FormFactoryBuilder();
- $formFactoryBuilder->addExtension(new ValidatorExtension($validator));
- $formFactory = $formFactoryBuilder->getFormFactory();
-
- return $formFactory->create($type, $data, $options);
- }
-}
-
-class Foo
-{
- public $bar;
- public $baz;
-
- public static function loadValidatorMetadata(ClassMetadata $metadata)
- {
- $metadata->addPropertyConstraint('bar', new NotBlank());
- }
-}
-
-class FooType extends AbstractType
-{
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- $builder
- ->add('bar')
- ->add('baz', null, [
- 'constraints' => [new NotBlank()],
- ])
- ;
- }
-
- public function configureOptions(OptionsResolver $resolver)
- {
- $resolver->setDefault('data_class', Foo::class);
- }
}
diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json
index cc398d2f7668..8976d79837b2 100644
--- a/src/Symfony/Component/Form/composer.json
+++ b/src/Symfony/Component/Form/composer.json
@@ -31,6 +31,7 @@
"doctrine/collections": "~1.0",
"symfony/validator": "^4.4|^5.0",
"symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/expression-language": "^4.4|^5.0",
"symfony/config": "^4.4|^5.0",
"symfony/console": "^4.4|^5.0",
"symfony/http-foundation": "^4.4|^5.0",
diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php
index 1520d22a2727..0877b8883bef 100644
--- a/src/Symfony/Component/HttpClient/AmpHttpClient.php
+++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php
@@ -120,6 +120,9 @@ public function request(string $method, string $url, array $options = []): Respo
$request->setTcpConnectTimeout(1000 * $options['timeout']);
$request->setTlsHandshakeTimeout(1000 * $options['timeout']);
$request->setTransferTimeout(1000 * $options['max_duration']);
+ if (method_exists($request, 'setInactivityTimeout')) {
+ $request->setInactivityTimeout(0);
+ }
if ('' !== $request->getUri()->getUserInfo() && !$request->hasHeader('authorization')) {
$auth = explode(':', $request->getUri()->getUserInfo(), 2);
diff --git a/src/Symfony/Component/HttpClient/Internal/NativeClientState.php b/src/Symfony/Component/HttpClient/Internal/NativeClientState.php
index 6578929dc546..2a47dbcca0ec 100644
--- a/src/Symfony/Component/HttpClient/Internal/NativeClientState.php
+++ b/src/Symfony/Component/HttpClient/Internal/NativeClientState.php
@@ -28,8 +28,6 @@ final class NativeClientState extends ClientState
public $responseCount = 0;
/** @var string[] */
public $dnsCache = [];
- /** @var resource[] */
- public $handles = [];
/** @var bool */
public $sleep = false;
diff --git a/src/Symfony/Component/HttpClient/Response/AmpResponse.php b/src/Symfony/Component/HttpClient/Response/AmpResponse.php
index a406585b9d9a..7745461c9475 100644
--- a/src/Symfony/Component/HttpClient/Response/AmpResponse.php
+++ b/src/Symfony/Component/HttpClient/Response/AmpResponse.php
@@ -173,21 +173,18 @@ private static function perform(ClientState $multi, array &$responses = null): v
*/
private static function select(ClientState $multi, float $timeout): int
{
- $start = microtime(true);
- $remaining = $timeout;
-
- while (true) {
- self::$delay = Loop::delay(1000 * $remaining, [Loop::class, 'stop']);
- Loop::run();
-
- if (null === self::$delay) {
- return 1;
+ $timeout += microtime(true);
+ self::$delay = Loop::defer(static function () use ($timeout) {
+ if (0 < $timeout -= microtime(true)) {
+ self::$delay = Loop::delay(ceil(1000 * $timeout), [Loop::class, 'stop']);
+ } else {
+ Loop::stop();
}
+ });
- if (0 >= $remaining = $timeout - microtime(true) + $start) {
- return 0;
- }
- }
+ Loop::run();
+
+ return null === self::$delay ? 1 : 0;
}
private static function generateResponse(Request $request, AmpClientState $multi, string $id, array &$info, array &$headers, CancellationTokenSource $canceller, array &$options, \Closure $onProgress, &$handle, ?LoggerInterface $logger)
diff --git a/src/Symfony/Component/HttpClient/Response/NativeResponse.php b/src/Symfony/Component/HttpClient/Response/NativeResponse.php
index 639957415f0d..2ba1b010e41f 100644
--- a/src/Symfony/Component/HttpClient/Response/NativeResponse.php
+++ b/src/Symfony/Component/HttpClient/Response/NativeResponse.php
@@ -220,11 +220,6 @@ private static function schedule(self $response, array &$runningResponses): void
*/
private static function perform(ClientState $multi, array &$responses = null): void
{
- // List of native handles for stream_select()
- if (null !== $responses) {
- $multi->handles = [];
- }
-
foreach ($multi->openHandles as $i => [$h, $buffer, $onProgress]) {
$hasActivity = false;
$remaining = &$multi->openHandles[$i][3];
@@ -291,8 +286,6 @@ private static function perform(ClientState $multi, array &$responses = null): v
$multi->handlesActivity[$i][] = $e;
unset($multi->openHandles[$i]);
$multi->sleep = false;
- } elseif (null !== $responses) {
- $multi->handles[] = $h;
}
}
@@ -307,7 +300,7 @@ private static function perform(ClientState $multi, array &$responses = null): v
}
}
- if (\count($multi->handles) >= $multi->maxHostConnections) {
+ if (\count($multi->openHandles) >= $multi->maxHostConnections) {
return;
}
@@ -318,10 +311,6 @@ private static function perform(ClientState $multi, array &$responses = null): v
$multi->sleep = false;
self::perform($multi);
- if (null !== $response->handle) {
- $multi->handles[] = $response->handle;
- }
-
break;
}
}
@@ -335,7 +324,8 @@ private static function perform(ClientState $multi, array &$responses = null): v
private static function select(ClientState $multi, float $timeout): int
{
$_ = [];
+ $handles = array_column($multi->openHandles, 0);
- return (!$multi->sleep = !$multi->sleep) ? -1 : stream_select($multi->handles, $_, $_, (int) $timeout, (int) (1E6 * ($timeout - (int) $timeout)));
+ return (!$multi->sleep = !$multi->sleep) ? -1 : stream_select($handles, $_, $_, (int) $timeout, (int) (1E6 * ($timeout - (int) $timeout)));
}
}
diff --git a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
index 44f2b559795c..37a134cea01e 100644
--- a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
+++ b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
@@ -137,7 +137,7 @@ public function getContent(bool $throw = true): string
public function toArray(bool $throw = true): array
{
if ('' === $content = $this->getContent($throw)) {
- throw new TransportException('Response body is empty.');
+ throw new JsonException('Response body is empty.');
}
if (null !== $this->jsonData) {
@@ -316,7 +316,7 @@ public static function stream(iterable $responses, float $timeout = null): \Gene
}
$lastActivity = microtime(true);
- $isTimeout = false;
+ $enlapsedTimeout = 0;
while (true) {
$hasActivity = false;
@@ -338,7 +338,7 @@ public static function stream(iterable $responses, float $timeout = null): \Gene
} elseif (!isset($multi->openHandles[$j])) {
unset($responses[$j]);
continue;
- } elseif ($isTimeout) {
+ } elseif ($enlapsedTimeout >= $timeoutMax) {
$multi->handlesActivity[$j] = [new ErrorChunk($response->offset, sprintf('Idle timeout reached for "%s".', $response->getInfo('url')))];
} else {
continue;
@@ -346,7 +346,7 @@ public static function stream(iterable $responses, float $timeout = null): \Gene
while ($multi->handlesActivity[$j] ?? false) {
$hasActivity = true;
- $isTimeout = false;
+ $enlapsedTimeout = 0;
if (\is_string($chunk = array_shift($multi->handlesActivity[$j]))) {
if (null !== $response->inflate && false === $chunk = @inflate_add($response->inflate, $chunk)) {
@@ -359,8 +359,9 @@ public static function stream(iterable $responses, float $timeout = null): \Gene
continue;
}
- $response->offset += \strlen($chunk);
+ $chunkLen = \strlen($chunk);
$chunk = new DataChunk($response->offset, $chunk);
+ $response->offset += $chunkLen;
} elseif (null === $chunk) {
$e = $multi->handlesActivity[$j][0];
unset($responses[$j], $multi->handlesActivity[$j]);
@@ -379,7 +380,7 @@ public static function stream(iterable $responses, float $timeout = null): \Gene
}
} elseif ($chunk instanceof ErrorChunk) {
unset($responses[$j]);
- $isTimeout = true;
+ $enlapsedTimeout = $timeoutMax;
} elseif ($chunk instanceof FirstChunk) {
if ($response->logger) {
$info = $response->getInfo();
@@ -447,10 +448,11 @@ public static function stream(iterable $responses, float $timeout = null): \Gene
continue;
}
- switch (self::select($multi, $timeoutMin)) {
- case -1: usleep(min(500, 1E6 * $timeoutMin)); break;
- case 0: $isTimeout = microtime(true) - $lastActivity > $timeoutMax; break;
+ if (-1 === self::select($multi, min($timeoutMin, $timeoutMax - $enlapsedTimeout))) {
+ usleep(min(500, 1E6 * $timeoutMin));
}
+
+ $enlapsedTimeout = microtime(true) - $lastActivity;
}
}
}
diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
index d238034f451e..269464d400e3 100644
--- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
@@ -33,4 +33,13 @@ protected function getHttpClient(string $testCase): HttpClientInterface
return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]);
}
+
+ public function testTimeoutIsNotAFatalError()
+ {
+ if ('\\' === \DIRECTORY_SEPARATOR) {
+ $this->markTestSkipped('Too transient on Windows');
+ }
+
+ parent::testTimeoutIsNotAFatalError();
+ }
}
diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
index 907857fa4d11..f8a84ea4bcfe 100644
--- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
+++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php
@@ -83,9 +83,9 @@ public function testToStream404()
$this->assertSame($response, stream_get_meta_data($stream)['wrapper_data']->getResponse());
$this->assertSame(404, $response->getStatusCode());
- $this->expectException(ClientException::class);
$response = $client->request('GET', 'http://localhost:8057/404');
- $stream = $response->toStream();
+ $this->expectException(ClientException::class);
+ $response->toStream();
}
public function testNonBlockingStream()
@@ -93,6 +93,7 @@ public function testNonBlockingStream()
$client = $this->getHttpClient(__FUNCTION__);
$response = $client->request('GET', 'http://localhost:8057/timeout-body');
$stream = $response->toStream();
+ usleep(10000);
$this->assertTrue(stream_set_blocking($stream, false));
$this->assertSame('<1>', fread($stream, 8192));
diff --git a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
index 7c9a1aa3c6da..56b7232bc4d8 100644
--- a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
@@ -190,6 +190,10 @@ protected function getHttpClient(string $testCase): HttpClientInterface
$this->markTestSkipped("MockHttpClient doesn't unzip");
break;
+ case 'testTimeoutWithActiveConcurrentStream':
+ $this->markTestSkipped('Real transport required');
+ break;
+
case 'testDestruct':
$this->markTestSkipped("MockHttpClient doesn't timeout on destruct");
break;
diff --git a/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php b/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php
index 77b0dfa7c8c0..b1cb16c1d90e 100644
--- a/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/Response/MockResponseTest.php
@@ -35,6 +35,12 @@ public function testToArrayError($content, $responseHeaders, $message)
public function toArrayErrors()
{
+ yield [
+ 'content' => '',
+ 'responseHeaders' => [],
+ 'message' => 'Response body is empty.',
+ ];
+
yield [
'content' => '{}',
'responseHeaders' => ['content-type' => 'plain/text'],
diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php
index 96c310fe95dc..22a03f261f6a 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php
@@ -150,8 +150,8 @@ public function lookup(Request $request)
}
$headers = $match[1];
- if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) {
- return $this->restoreResponse($headers, $body);
+ if (file_exists($path = $this->getPath($headers['x-content-digest'][0]))) {
+ return $this->restoreResponse($headers, $path);
}
// TODO the metaStore referenced an entity that doesn't exist in
@@ -175,15 +175,28 @@ public function write(Request $request, Response $response)
$key = $this->getCacheKey($request);
$storedEnv = $this->persistRequest($request);
- $digest = $this->generateContentDigest($response);
- $response->headers->set('X-Content-Digest', $digest);
+ if ($response->headers->has('X-Body-File')) {
+ // Assume the response came from disk, but at least perform some safeguard checks
+ if (!$response->headers->has('X-Content-Digest')) {
+ throw new \RuntimeException('A restored response must have the X-Content-Digest header.');
+ }
- if (!$this->save($digest, $response->getContent(), false)) {
- throw new \RuntimeException('Unable to store the entity.');
- }
+ $digest = $response->headers->get('X-Content-Digest');
+ if ($this->getPath($digest) !== $response->headers->get('X-Body-File')) {
+ throw new \RuntimeException('X-Body-File and X-Content-Digest do not match.');
+ }
+ // Everything seems ok, omit writing content to disk
+ } else {
+ $digest = $this->generateContentDigest($response);
+ $response->headers->set('X-Content-Digest', $digest);
- if (!$response->headers->has('Transfer-Encoding')) {
- $response->headers->set('Content-Length', \strlen($response->getContent()));
+ if (!$this->save($digest, $response->getContent(), false)) {
+ throw new \RuntimeException('Unable to store the entity.');
+ }
+
+ if (!$response->headers->has('Transfer-Encoding')) {
+ $response->headers->set('Content-Length', \strlen($response->getContent()));
+ }
}
// read existing cache entries, remove non-varying, and add this one to the list
@@ -446,15 +459,15 @@ private function persistResponse(Response $response): array
/**
* Restores a Response from the HTTP headers and body.
*/
- private function restoreResponse(array $headers, string $body = null): Response
+ private function restoreResponse(array $headers, string $path = null): Response
{
$status = $headers['X-Status'][0];
unset($headers['X-Status']);
- if (null !== $body) {
- $headers['X-Body-File'] = [$body];
+ if (null !== $path) {
+ $headers['X-Body-File'] = [$path];
}
- return new Response($body, $status, $headers);
+ return new Response($path, $status, $headers);
}
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 0df231dca933..ceb069ee2c4f 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private static $freshCache = [];
- const VERSION = '5.1.0';
- const VERSION_ID = 50100;
+ const VERSION = '5.1.1';
+ const VERSION_ID = 50101;
const MAJOR_VERSION = 5;
const MINOR_VERSION = 1;
- const RELEASE_VERSION = 0;
+ const RELEASE_VERSION = 1;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '01/2021';
diff --git a/src/Symfony/Component/HttpKernel/Resources/welcome.html.php b/src/Symfony/Component/HttpKernel/Resources/welcome.html.php
index e1c0ff11926b..b8337dc737e4 100644
--- a/src/Symfony/Component/HttpKernel/Resources/welcome.html.php
+++ b/src/Symfony/Component/HttpKernel/Resources/welcome.html.php
@@ -65,7 +65,7 @@
- You're seeing this page because you haven't configured any homepage URL.
+ You're seeing this page because you haven't configured any homepage URL and debug mode is enabled.
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php
index b17cc0a44f9e..1f5f472802e7 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php
@@ -118,6 +118,39 @@ public function testWritesResponseEvenIfXContentDigestIsPresent()
$this->assertNotNull($response);
}
+ public function testWritingARestoredResponseDoesNotCorruptCache()
+ {
+ /*
+ * This covers the regression reported in https://github.com/symfony/symfony/issues/37174.
+ *
+ * A restored response does *not* load the body, but only keep the file path in a special X-Body-File
+ * header. For reasons (?), the file path was also used as the restored response body.
+ * It would be up to others (HttpCache...?) to honor this header and actually load the response content
+ * from there.
+ *
+ * When a restored response was stored again, the Store itself would ignore the header. In the first
+ * step, this would compute a new Content Digest based on the file path in the restored response body;
+ * this is covered by "Checkpoint 1" below. But, since the X-Body-File header was left untouched (Checkpoint 2), downstream
+ * code (HttpCache...) would not immediately notice.
+ *
+ * Only upon performing the lookup for a second time, we'd get a Response where the (wrong) Content Digest
+ * is also reflected in the X-Body-File header, this time also producing wrong content when the downstream
+ * evaluates it.
+ */
+ $this->store->write($this->request, $this->response);
+ $digest = $this->response->headers->get('X-Content-Digest');
+ $path = $this->getStorePath($digest);
+
+ $response = $this->store->lookup($this->request);
+ $this->store->write($this->request, $response);
+ $this->assertEquals($digest, $response->headers->get('X-Content-Digest')); // Checkpoint 1
+ $this->assertEquals($path, $response->headers->get('X-Body-File')); // Checkpoint 2
+
+ $response = $this->store->lookup($this->request);
+ $this->assertEquals($digest, $response->headers->get('X-Content-Digest'));
+ $this->assertEquals($path, $response->headers->get('X-Body-File'));
+ }
+
public function testFindsAStoredEntryWithLookup()
{
$this->storeSimpleEntry();
diff --git a/src/Symfony/Component/Ldap/Security/LdapUserProvider.php b/src/Symfony/Component/Ldap/Security/LdapUserProvider.php
index 28bd1d4c6892..c593a1376e5d 100644
--- a/src/Symfony/Component/Ldap/Security/LdapUserProvider.php
+++ b/src/Symfony/Component/Ldap/Security/LdapUserProvider.php
@@ -108,7 +108,7 @@ public function refreshUser(UserInterface $user)
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user)));
}
- return new LdapUser($user->getEntry(), $user->getUsername(), $user->getPassword(), $user->getRoles());
+ return new LdapUser($user->getEntry(), $user->getUsername(), $user->getPassword(), $user->getRoles(), $user->getExtraFields());
}
/**
diff --git a/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php b/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php
index 8d0a7a351758..a2e888077cde 100644
--- a/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php
+++ b/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php
@@ -330,4 +330,14 @@ public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute()
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']);
$this->assertInstanceOf(LdapUser::class, $provider->loadUserByUsername('foo'));
}
+
+ public function testRefreshUserShouldReturnUserWithSameProperties()
+ {
+ $ldap = $this->createMock(LdapInterface::class);
+ $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']);
+
+ $user = new LdapUser(new Entry('foo'), 'foo', 'bar', ['ROLE_DUMMY'], ['email' => 'foo@symfony.com']);
+
+ $this->assertEquals($user, $provider->refreshUser($user));
+ }
}
diff --git a/src/Symfony/Component/Lock/Store/MongoDbStore.php b/src/Symfony/Component/Lock/Store/MongoDbStore.php
index 296c68be1072..34c740a6685c 100644
--- a/src/Symfony/Component/Lock/Store/MongoDbStore.php
+++ b/src/Symfony/Component/Lock/Store/MongoDbStore.php
@@ -14,8 +14,8 @@
use MongoDB\BSON\UTCDateTime;
use MongoDB\Client;
use MongoDB\Collection;
-use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\WriteException;
+use MongoDB\Driver\ReadPreference;
use MongoDB\Exception\DriverRuntimeException;
use MongoDB\Exception\InvalidArgumentException as MongoInvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
@@ -54,8 +54,6 @@ class MongoDbStore implements BlockingStoreInterface
private $options;
private $initialTtl;
- private $databaseVersion;
-
use ExpiringStoreTrait;
/**
@@ -87,8 +85,8 @@ class MongoDbStore implements BlockingStoreInterface
* to 0.0 and optionally leverage
* self::createTtlIndex(int $expireAfterSeconds = 0).
*
- * writeConcern, readConcern and readPreference are not specified by
- * MongoDbStore meaning the collection's settings will take effect.
+ * writeConcern and readConcern are not specified by MongoDbStore meaning the connection's settings will take effect.
+ * readPreference is primary for all queries.
* @see https://docs.mongodb.com/manual/applications/replication/
*/
public function __construct($mongo, array $options = [], float $initialTtl = 300.0)
@@ -262,6 +260,8 @@ public function exists(Key $key): bool
'expires_at' => [
'$gt' => $this->createMongoDateTime(microtime(true)),
],
+ ], [
+ 'readPreference' => new ReadPreference(\defined(ReadPreference::PRIMARY) ? ReadPreference::PRIMARY : ReadPreference::RP_PRIMARY),
]);
}
@@ -315,25 +315,6 @@ private function isDuplicateKeyException(WriteException $e): bool
return 11000 === $code;
}
- private function getDatabaseVersion(): string
- {
- if (null !== $this->databaseVersion) {
- return $this->databaseVersion;
- }
-
- $command = new Command([
- 'buildinfo' => 1,
- ]);
- $cursor = $this->getCollection()->getManager()->executeReadCommand(
- $this->getCollection()->getDatabaseName(),
- $command
- );
- $buildInfo = $cursor->toArray()[0];
- $this->databaseVersion = $buildInfo->version;
-
- return $this->databaseVersion;
- }
-
private function getCollection(): Collection
{
if (null !== $this->collection) {
diff --git a/src/Symfony/Component/Lock/Store/PdoStore.php b/src/Symfony/Component/Lock/Store/PdoStore.php
index b792d4d9ca97..fb44803681ad 100644
--- a/src/Symfony/Component/Lock/Store/PdoStore.php
+++ b/src/Symfony/Component/Lock/Store/PdoStore.php
@@ -13,6 +13,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\Lock\Exception\InvalidArgumentException;
@@ -158,10 +159,10 @@ public function putOffExpiration(Key $key, float $ttl)
$stmt->bindValue(':id', $this->getHashedKey($key));
$stmt->bindValue(':token1', $uniqueToken);
$stmt->bindValue(':token2', $uniqueToken);
- $stmt->execute();
+ $result = $stmt->execute();
// If this method is called twice in the same second, the row wouldn't be updated. We have to call exists to know if we are the owner
- if (!$stmt->rowCount() && !$this->exists($key)) {
+ if (!($result instanceof Result ? $result : $stmt)->rowCount() && !$this->exists($key)) {
throw new LockConflictedException();
}
@@ -191,9 +192,9 @@ public function exists(Key $key)
$stmt->bindValue(':id', $this->getHashedKey($key));
$stmt->bindValue(':token', $this->getUniqueToken($key));
- $stmt->execute();
+ $result = $stmt->execute();
- return (bool) (method_exists($stmt, 'fetchOne') ? $stmt->fetchOne() : $stmt->fetchColumn());
+ return (bool) ($result instanceof Result ? $result->fetchOne() : $stmt->fetchColumn());
}
/**
diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
index 0a7fde06ce6d..45ccd65cdf13 100644
--- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
@@ -115,6 +115,9 @@ private function getPayload(Email $email, Envelope $envelope): array
if ($email->getHtmlBody()) {
$payload['Message.Body.Html.Data'] = $email->getHtmlBody();
}
+ if ($email->getReplyTo()) {
+ $payload['ReplyToAddresses.member'] = $this->stringifyAddresses($email->getReplyTo());
+ }
return $payload;
}
diff --git a/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php b/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php
index 0472c36b6209..b06ac839c64f 100644
--- a/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php
+++ b/src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php
@@ -22,9 +22,6 @@ class SendEmailMessage
private $message;
private $envelope;
- /**
- * @internal
- */
public function __construct(RawMessage $message, Envelope $envelope = null)
{
$this->message = $message;
diff --git a/src/Symfony/Component/Mailer/Tests/TransportTest.php b/src/Symfony/Component/Mailer/Tests/TransportTest.php
index 95eb5b6ebf03..dfd8d1926e61 100644
--- a/src/Symfony/Component/Mailer/Tests/TransportTest.php
+++ b/src/Symfony/Component/Mailer/Tests/TransportTest.php
@@ -60,6 +60,22 @@ public function fromStringProvider(): iterable
];
}
+ /**
+ * @dataProvider fromDsnProvider
+ */
+ public function testFromDsn(string $dsn, TransportInterface $transport): void
+ {
+ $this->assertEquals($transport, Transport::fromDsn($dsn));
+ }
+
+ public function fromDsnProvider(): iterable
+ {
+ yield 'multiple transports' => [
+ 'failover(smtp://a smtp://b)',
+ new FailoverTransport([new Transport\Smtp\EsmtpTransport('a'), new Transport\Smtp\EsmtpTransport('b')]),
+ ];
+ }
+
/**
* @dataProvider fromWrongStringProvider
*/
diff --git a/src/Symfony/Component/Mailer/Transport.php b/src/Symfony/Component/Mailer/Transport.php
index b30ba6fbdc6e..5d5c7e0b6eb8 100644
--- a/src/Symfony/Component/Mailer/Transport.php
+++ b/src/Symfony/Component/Mailer/Transport.php
@@ -51,7 +51,7 @@ class Transport
public static function fromDsn(string $dsn, EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null): TransportInterface
{
- $factory = new self(self::getDefaultFactories($dispatcher, $client, $logger));
+ $factory = new self(iterator_to_array(self::getDefaultFactories($dispatcher, $client, $logger)));
return $factory->fromString($dsn);
}
diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php
index 3c47be561475..c840822abea4 100644
--- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsIntegrationTest.php
@@ -45,7 +45,7 @@ private function execute(string $dsn): void
$connection->setup();
$this->clearSqs($dsn);
- $connection->send('{"message": "Hi"}', ['type' => DummyMessage::class]);
+ $connection->send('{"message": "Hi"}', ['type' => DummyMessage::class, DummyMessage::class => 'special']);
$this->assertSame(1, $connection->getMessageCount());
$wait = 0;
@@ -54,7 +54,7 @@ private function execute(string $dsn): void
}
$this->assertEquals('{"message": "Hi"}', $encoded['body']);
- $this->assertEquals(['type' => DummyMessage::class], $encoded['headers']);
+ $this->assertEquals(['type' => DummyMessage::class, DummyMessage::class => 'special'], $encoded['headers']);
}
private function clearSqs(string $dsn): void
diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php
index 5d5bfb36c65c..1ae2b463210f 100644
--- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php
+++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/Connection.php
@@ -30,6 +30,7 @@
class Connection
{
private const AWS_SQS_FIFO_SUFFIX = '.fifo';
+ private const MESSAGE_ATTRIBUTE_NAME = 'X-Symfony-Messenger';
private const DEFAULT_OPTIONS = [
'buffer_size' => 9,
@@ -200,7 +201,12 @@ private function fetchMessage(): bool
foreach ($this->currentResponse->getMessages() as $message) {
$headers = [];
- foreach ($message->getMessageAttributes() as $name => $attribute) {
+ $attributes = $message->getMessageAttributes();
+ if (isset($attributes[self::MESSAGE_ATTRIBUTE_NAME]) && 'String' === $attributes[self::MESSAGE_ATTRIBUTE_NAME]->getDataType()) {
+ $headers = json_decode($attributes[self::MESSAGE_ATTRIBUTE_NAME]->getStringValue(), true);
+ unset($attributes[self::MESSAGE_ATTRIBUTE_NAME]);
+ }
+ foreach ($attributes as $name => $attribute) {
if ('String' !== $attribute->getDataType()) {
continue;
}
@@ -284,13 +290,27 @@ public function send(string $body, array $headers, int $delay = 0, ?string $mess
'MessageAttributes' => [],
];
+ $specialHeaders = [];
foreach ($headers as $name => $value) {
+ if ('.' === $name[0] || self::MESSAGE_ATTRIBUTE_NAME === $name || \strlen($name) > 256 || '.' === substr($name, -1) || 'AWS.' === substr($name, 0, \strlen('AWS.')) || 'Amazon.' === substr($name, 0, \strlen('Amazon.')) || preg_match('/([^a-zA-Z0-9_\.-]+|\.\.)/', $name)) {
+ $specialHeaders[$name] = $value;
+
+ continue;
+ }
+
$parameters['MessageAttributes'][$name] = new MessageAttributeValue([
'DataType' => 'String',
'StringValue' => $value,
]);
}
+ if (!empty($specialHeaders)) {
+ $parameters['MessageAttributes'][self::MESSAGE_ATTRIBUTE_NAME] = new MessageAttributeValue([
+ 'DataType' => 'String',
+ 'StringValue' => json_encode($specialHeaders),
+ ]);
+ }
+
if (self::isFifoQueue($this->configuration['queue_name'])) {
$parameters['MessageGroupId'] = null !== $messageGroupId ? $messageGroupId : __METHOD__;
$parameters['MessageDeduplicationId'] = null !== $messageDeduplicationId ? $messageDeduplicationId : sha1(json_encode(['body' => $body, 'headers' => $headers]));
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php
index ef94e60f4431..88a68defeeec 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/ConnectionTest.php
@@ -11,15 +11,15 @@
namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport;
+use Doctrine\DBAL\Abstraction\Result;
use Doctrine\DBAL\DBALException;
-use Doctrine\DBAL\Driver\ResultStatement;
-use Doctrine\DBAL\ForwardCompatibility\Driver\ResultStatement as ForwardCompatibleResultStatement;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaConfig;
use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer;
+use Doctrine\DBAL\Statement;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Bridge\Doctrine\Tests\Fixtures\DummyMessage;
use Symfony\Component\Messenger\Bridge\Doctrine\Transport\Connection;
@@ -31,7 +31,7 @@ public function testGetAMessageWillChangeItsStatus()
$queryBuilder = $this->getQueryBuilderMock();
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
- $stmt = $this->getStatementMock([
+ $stmt = $this->getResultMock([
'id' => 1,
'body' => '{"message":"Hi"}',
'headers' => json_encode(['type' => DummyMessage::class]),
@@ -65,7 +65,7 @@ public function testGetWithNoPendingMessageWillReturnNull()
$queryBuilder = $this->getQueryBuilderMock();
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
- $stmt = $this->getStatementMock(false);
+ $stmt = $this->getResultMock(false);
$queryBuilder
->method('getParameters')
@@ -144,16 +144,12 @@ private function getQueryBuilderMock()
return $queryBuilder;
}
- private function getStatementMock($expectedResult): ResultStatement
+ private function getResultMock($expectedResult)
{
- $mockedInterface = interface_exists(ForwardCompatibleResultStatement::class)
- ? ForwardCompatibleResultStatement::class
- : ResultStatement::class;
-
- $stmt = $this->createMock($mockedInterface);
+ $stmt = $this->createMock(interface_exists(Result::class) ? Result::class : Statement::class);
$stmt->expects($this->once())
- ->method(method_exists($mockedInterface, 'fetchAssociative') ? 'fetchAssociative' : 'fetch')
+ ->method(interface_exists(Result::class) ? 'fetchAssociative' : 'fetch')
->willReturn($expectedResult);
return $stmt;
@@ -270,7 +266,7 @@ public function testFind()
$driverConnection = $this->getDBALConnectionMock();
$schemaSynchronizer = $this->getSchemaSynchronizerMock();
$id = 1;
- $stmt = $this->getStatementMock([
+ $stmt = $this->getResultMock([
'id' => $id,
'body' => '{"message":"Hi"}',
'headers' => json_encode(['type' => DummyMessage::class]),
@@ -315,12 +311,9 @@ public function testFindAll()
'headers' => json_encode(['type' => DummyMessage::class]),
];
- $mockedInterface = interface_exists(ForwardCompatibleResultStatement::class)
- ? ForwardCompatibleResultStatement::class
- : ResultStatement::class;
- $stmt = $this->createMock($mockedInterface);
+ $stmt = $this->createMock(interface_exists(Result::class) ? Result::class : Statement::class);
$stmt->expects($this->once())
- ->method(method_exists($mockedInterface, 'fetchAllAssociative') ? 'fetchAllAssociative' : 'fetchAll')
+ ->method(interface_exists(Result::class) ? 'fetchAllAssociative' : 'fetchAll')
->willReturn([$message1, $message2]);
$driverConnection
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php
index a16800464fc1..f5cd170cfbb2 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Version;
use PHPUnit\Framework\TestCase;
@@ -71,7 +72,7 @@ public function testSendWithDelay()
->setParameter(':body', '{"message": "Hi i am delayed"}')
->execute();
- $available_at = new \DateTime(method_exists($stmt, 'fetchOne') ? $stmt->fetchOne() : $stmt->fetchColumn());
+ $available_at = new \DateTime($stmt instanceof Result ? $stmt->fetchOne() : $stmt->fetchColumn());
$now = new \DateTime();
$now->modify('+60 seconds');
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php
index 90017123e168..39398519bbee 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php
@@ -13,7 +13,7 @@
use Doctrine\DBAL\Connection as DBALConnection;
use Doctrine\DBAL\DBALException;
-use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Schema\Schema;
@@ -175,7 +175,7 @@ public function get(): ?array
$query->getParameters(),
$query->getParameterTypes()
);
- $doctrineEnvelope = method_exists($stmt, 'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch();
+ $doctrineEnvelope = $stmt instanceof Result ? $stmt->fetchAssociative() : $stmt->fetch();
if (false === $doctrineEnvelope) {
$this->driverConnection->commit();
@@ -267,7 +267,7 @@ public function getMessageCount(): int
$stmt = $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters(), $queryBuilder->getParameterTypes());
- return method_exists($stmt, 'fetchOne') ? $stmt->fetchOne() : $stmt->fetchColumn();
+ return $stmt instanceof Result ? $stmt->fetchOne() : $stmt->fetchColumn();
}
public function findAll(int $limit = null): array
@@ -278,7 +278,7 @@ public function findAll(int $limit = null): array
}
$stmt = $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters(), $queryBuilder->getParameterTypes());
- $data = method_exists($stmt, 'fetchAllAssociative') ? $stmt->fetchAllAssociative() : $stmt->fetchAll();
+ $data = $stmt instanceof Result ? $stmt->fetchAllAssociative() : $stmt->fetchAll();
return array_map(function ($doctrineEnvelope) {
return $this->decodeEnvelopeHeaders($doctrineEnvelope);
@@ -291,7 +291,7 @@ public function find($id): ?array
->where('m.id = ?');
$stmt = $this->executeQuery($queryBuilder->getSQL(), [$id]);
- $data = method_exists($stmt, 'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch();
+ $data = $stmt instanceof Result ? $stmt->fetchAssociative() : $stmt->fetch();
return false === $data ? null : $this->decodeEnvelopeHeaders($data);
}
@@ -350,7 +350,7 @@ private function createQueryBuilder(): QueryBuilder
->from($this->configuration['table_name'], 'm');
}
- private function executeQuery(string $sql, array $parameters = [], array $types = []): ResultStatement
+ private function executeQuery(string $sql, array $parameters = [], array $types = [])
{
try {
$stmt = $this->driverConnection->executeQuery($sql, $parameters, $types);
@@ -390,6 +390,7 @@ private function addTableToSchema(Schema $schema): void
$table->addColumn('headers', self::$useDeprecatedConstants ? Type::TEXT : Types::TEXT)
->setNotnull(true);
$table->addColumn('queue_name', self::$useDeprecatedConstants ? Type::STRING : Types::STRING)
+ ->setLength(190) // mysql 5.6 only supports 191 characters on an indexed column in utf8mb4 mode
->setNotnull(true);
$table->addColumn('created_at', self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE)
->setNotnull(true);
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json b/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json
index 35ddc7f09a52..9d8d8a8fef53 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json
@@ -22,7 +22,6 @@
},
"require-dev": {
"doctrine/dbal": "^2.6|^3.0",
- "doctrine/orm": "^2.6.3",
"doctrine/persistence": "^1.3",
"symfony/property-access": "^4.4|^5.0",
"symfony/serializer": "^4.4|^5.0"
diff --git a/src/Symfony/Component/Mime/Address.php b/src/Symfony/Component/Mime/Address.php
index 6d663e93b75f..9847712b1074 100644
--- a/src/Symfony/Component/Mime/Address.php
+++ b/src/Symfony/Component/Mime/Address.php
@@ -89,7 +89,7 @@ public static function create($address): self
return $address;
}
if (\is_string($address)) {
- return new self($address);
+ return self::fromString($address);
}
throw new InvalidArgumentException(sprintf('An address can be an instance of Address or a string ("%s") given).', get_debug_type($address)));
diff --git a/src/Symfony/Component/Mime/Crypto/SMimeSigner.php b/src/Symfony/Component/Mime/Crypto/SMimeSigner.php
index 243aaf10da06..1b555375ce90 100644
--- a/src/Symfony/Component/Mime/Crypto/SMimeSigner.php
+++ b/src/Symfony/Component/Mime/Crypto/SMimeSigner.php
@@ -24,11 +24,6 @@ final class SMimeSigner extends SMime
private $signOptions;
private $extraCerts;
- /**
- * @var string|null
- */
- private $privateKeyPassphrase;
-
/**
* @param string $certificate The path of the file containing the signing certificate (in PEM format)
* @param string $privateKey The path of the file containing the private key (in PEM format)
@@ -52,7 +47,6 @@ public function __construct(string $certificate, string $privateKey, string $pri
$this->signOptions = $signOptions ?? PKCS7_DETACHED;
$this->extraCerts = $extraCerts ? realpath($extraCerts) : null;
- $this->privateKeyPassphrase = $privateKeyPassphrase;
}
public function sign(Message $message): Message
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
index a667997a9b7e..baf377427d38 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
@@ -69,6 +69,11 @@ public function getPublicAccessor()
return $this->publicAccessor;
}
+ public function isPublicAccessor($param)
+ {
+ throw new \LogicException('This method should never have been called.');
+ }
+
public function getPublicAccessorWithDefaultValue()
{
return $this->publicAccessorWithDefaultValue;
diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json
index 4cde7bacf9a1..dd7bfb1ac7f5 100644
--- a/src/Symfony/Component/PropertyAccess/composer.json
+++ b/src/Symfony/Component/PropertyAccess/composer.json
@@ -19,7 +19,7 @@
"php": ">=7.2.5",
"symfony/inflector": "^4.4|^5.0",
"symfony/polyfill-php80": "^1.15",
- "symfony/property-info": "^5.1"
+ "symfony/property-info": "^5.1.1"
},
"require-dev": {
"symfony/cache": "^4.4|^5.0"
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index 8de931d9c577..1cf15345028c 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -40,7 +40,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
/**
* @internal
*/
- public static $defaultAccessorPrefixes = ['is', 'can', 'get', 'has'];
+ public static $defaultAccessorPrefixes = ['get', 'is', 'has', 'can'];
/**
* @internal
diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
index bee7a4a5694c..cd137eea78be 100644
--- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
@@ -171,7 +171,7 @@ protected function parseImport(RouteCollection $collection, array $config, strin
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
$methods = isset($config['methods']) ? $config['methods'] : null;
$trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
- $namePrefix = $config['name_prefix'] ?? '';
+ $namePrefix = $config['name_prefix'] ?? null;
$exclude = $config['exclude'] ?? null;
if (isset($config['controller'])) {
diff --git a/src/Symfony/Component/Routing/RouteCollection.php b/src/Symfony/Component/Routing/RouteCollection.php
index a3284771bb57..b661bb13a61d 100644
--- a/src/Symfony/Component/Routing/RouteCollection.php
+++ b/src/Symfony/Component/Routing/RouteCollection.php
@@ -179,8 +179,8 @@ public function addNamePrefix(string $prefix)
foreach ($this->routes as $name => $route) {
$prefixedRoutes[$prefix.$name] = $route;
- if (null !== $name = $route->getDefault('_canonical_route')) {
- $route->setDefault('_canonical_route', $prefix.$name);
+ if (null !== $canonicalName = $route->getDefault('_canonical_route')) {
+ $route->setDefault('_canonical_route', $prefix.$canonicalName);
}
if (isset($this->priorities[$name])) {
$prefixedPriorities[$prefix.$name] = $this->priorities[$name];
diff --git a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
index 05d3d0162ad1..90a66dbc679e 100644
--- a/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
+++ b/src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
@@ -363,4 +363,21 @@ public function testAddWithPriority()
$this->assertSame($expected, $collection2->all());
}
+
+ public function testAddWithPriorityAndPrefix()
+ {
+ $collection3 = new RouteCollection();
+ $collection3->add('foo3', $foo3 = new Route('/foo'), 0);
+ $collection3->add('bar3', $bar3 = new Route('/bar'), 1);
+ $collection3->add('baz3', $baz3 = new Route('/baz'));
+ $collection3->addNamePrefix('prefix_');
+
+ $expected = [
+ 'prefix_bar3' => $bar3,
+ 'prefix_foo3' => $foo3,
+ 'prefix_baz3' => $baz3,
+ ];
+
+ $this->assertSame($expected, $collection3->all());
+ }
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/AccessListener.php b/src/Symfony/Component/Security/Http/Firewall/AccessListener.php
index 8da2a994bf48..b218e1086c62 100644
--- a/src/Symfony/Component/Security/Http/Firewall/AccessListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/AccessListener.php
@@ -95,11 +95,13 @@ public function authenticate(RequestEvent $event)
return;
}
- if ([self::PUBLIC_ACCESS] === $attributes) {
- return;
+ if ([self::PUBLIC_ACCESS] !== $attributes) {
+ throw $this->createAccessDeniedException($request, $attributes);
}
+ }
- throw $this->createAccessDeniedException($request, $attributes);
+ if ([self::PUBLIC_ACCESS] === $attributes) {
+ return;
}
if (!$token->isAuthenticated()) {
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php
index 9748e6522c6a..154addc7c409 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php
@@ -18,8 +18,10 @@
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Http\Event\LazyResponseEvent;
use Symfony\Component\Security\Http\Firewall\AccessListener;
@@ -279,6 +281,33 @@ public function testHandleWhenPublicAccessIsAllowedAndExceptionOnTokenIsFalse()
$this->expectNotToPerformAssertions();
}
+ public function testHandleWhenPublicAccessWhileAuthenticated()
+ {
+ $token = new UsernamePasswordToken(new User('Wouter', null, ['ROLE_USER']), null, 'main', ['ROLE_USER']);
+ $tokenStorage = new TokenStorage();
+ $tokenStorage->setToken($token);
+ $request = new Request();
+
+ $accessMap = $this->createMock(AccessMapInterface::class);
+ $accessMap->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->willReturn([[AccessListener::PUBLIC_ACCESS], null])
+ ;
+
+ $listener = new AccessListener(
+ $tokenStorage,
+ $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')->getMock(),
+ $accessMap,
+ $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
+ false
+ );
+
+ $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST));
+
+ $this->expectNotToPerformAssertions();
+ }
+
public function testHandleMWithultipleAttributesShouldBeHandledAsAnd()
{
$request = new Request();
diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php
index fa4f944cd1fe..b8c33a2fe56c 100644
--- a/src/Symfony/Component/Serializer/Serializer.php
+++ b/src/Symfony/Component/Serializer/Serializer.php
@@ -21,6 +21,7 @@
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
+use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
@@ -158,7 +159,7 @@ public function normalize($data, string $format = null, array $context = [])
}
if (\is_array($data) || $data instanceof \Traversable) {
- if ($data instanceof \Countable && 0 === $data->count()) {
+ if (($context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] ?? false) === true && $data instanceof \Countable && 0 === $data->count()) {
return $data;
}
diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php
index cbc40b361cbc..3158f728fbfb 100644
--- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php
@@ -492,6 +492,27 @@ public function testNotNormalizableValueExceptionMessageForAResource()
(new Serializer())->normalize(tmpfile());
}
+ public function testNormalizeTransformEmptyArrayObjectToArray()
+ {
+ $serializer = new Serializer(
+ [
+ new PropertyNormalizer(),
+ new ObjectNormalizer(),
+ new ArrayDenormalizer(),
+ ],
+ [
+ 'json' => new JsonEncoder(),
+ ]
+ );
+
+ $object = [];
+ $object['foo'] = new \ArrayObject();
+ $object['bar'] = new \ArrayObject(['notempty']);
+ $object['baz'] = new \ArrayObject(['nested' => new \ArrayObject()]);
+
+ $this->assertSame('{"foo":[],"bar":["notempty"],"baz":{"nested":[]}}', $serializer->serialize($object, 'json'));
+ }
+
public function testNormalizePreserveEmptyArrayObject()
{
$serializer = new Serializer(
diff --git a/src/Symfony/Component/String/AbstractString.php b/src/Symfony/Component/String/AbstractString.php
index c9ba147b319c..f2754a56d0b6 100644
--- a/src/Symfony/Component/String/AbstractString.php
+++ b/src/Symfony/Component/String/AbstractString.php
@@ -643,7 +643,11 @@ public function truncate(int $length, string $ellipsis = '', bool $cut = true):
}
if (!$cut) {
- $length = $ellipsisLength + ($this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1) ?? $stringLength);
+ if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) {
+ return clone $this;
+ }
+
+ $length += $ellipsisLength;
}
$str = $this->slice(0, $length - $ellipsisLength);
diff --git a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php
index b333c74a252b..44f14c18af3a 100644
--- a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php
+++ b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php
@@ -1450,6 +1450,7 @@ public static function provideTruncate()
['foobar...', 'foobar foo', 6, '...', false],
['foobar...', 'foobar foo', 7, '...', false],
['foobar foo...', 'foobar foo a', 10, '...', false],
+ ['foobar foo aar', 'foobar foo aar', 12, '...', false],
];
}
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php
index 7db835b333f5..677660f6c31a 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php
@@ -11,39 +11,21 @@
namespace Symfony\Component\Validator\Tests\Constraints;
-use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
-use Symfony\Component\ExpressionLanguage\SyntaxError;
use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntax;
use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntaxValidator;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
class ExpressionLanguageSyntaxValidatorTest extends ConstraintValidatorTestCase
{
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|ExpressionLanguage
- */
- protected $expressionLanguage;
-
protected function createValidator()
{
- return new ExpressionLanguageSyntaxValidator($this->expressionLanguage);
- }
-
- protected function setUp(): void
- {
- $this->expressionLanguage = $this->createExpressionLanguage();
-
- parent::setUp();
+ return new ExpressionLanguageSyntaxValidator(new ExpressionLanguage());
}
public function testExpressionValid(): void
{
- $this->expressionLanguage->expects($this->once())
- ->method('lint')
- ->with($this->value, []);
-
- $this->validator->validate($this->value, new ExpressionLanguageSyntax([
+ $this->validator->validate('1 + 1', new ExpressionLanguageSyntax([
'message' => 'myMessage',
'allowedVariables' => [],
]));
@@ -53,12 +35,18 @@ public function testExpressionValid(): void
public function testExpressionWithoutNames(): void
{
- $this->expressionLanguage->expects($this->once())
- ->method('lint')
- ->with($this->value, null);
+ $this->validator->validate('1 + 1', new ExpressionLanguageSyntax([
+ 'message' => 'myMessage',
+ ]));
- $this->validator->validate($this->value, new ExpressionLanguageSyntax([
+ $this->assertNoViolation();
+ }
+
+ public function testExpressionWithAllowedVariableName(): void
+ {
+ $this->validator->validate('a + 1', new ExpressionLanguageSyntax([
'message' => 'myMessage',
+ 'allowedVariables' => ['a'],
]));
$this->assertNoViolation();
@@ -66,24 +54,15 @@ public function testExpressionWithoutNames(): void
public function testExpressionIsNotValid(): void
{
- $this->expressionLanguage->expects($this->once())
- ->method('lint')
- ->with($this->value, [])
- ->willThrowException(new SyntaxError('Test exception', 42));
-
- $this->validator->validate($this->value, new ExpressionLanguageSyntax([
+ $this->validator->validate('a + 1', new ExpressionLanguageSyntax([
'message' => 'myMessage',
'allowedVariables' => [],
]));
$this->buildViolation('myMessage')
- ->setParameter('{{ syntax_error }}', '"Test exception around position 42."')
+ ->setParameter('{{ syntax_error }}', '"Variable "a" is not valid around position 1 for expression `a + 1`."')
+ ->setInvalidValue('a + 1')
->setCode(ExpressionLanguageSyntax::EXPRESSION_LANGUAGE_SYNTAX_ERROR)
->assertRaised();
}
-
- protected function createExpressionLanguage(): MockObject
- {
- return $this->getMockBuilder('\Symfony\Component\ExpressionLanguage\ExpressionLanguage')->getMock();
- }
}
diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
index 55dfc1276366..fcb1d146944a 100644
--- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
+++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php
@@ -92,7 +92,7 @@ public function testExport(string $testName, $value, bool $staticValueExpected =
} elseif (\PHP_VERSION_ID < 70400) {
$fixtureFile = __DIR__.'/Fixtures/'.$testName.'-legacy.php';
} else {
- $this->markAsSkipped('PHP >= 7.4.6 required.');
+ $this->markTestSkipped('PHP >= 7.4.6 required.');
}
$this->assertStringEqualsFile($fixtureFile, $dump);
diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
index a60d8057e0b7..2c23815bad64 100644
--- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
+++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
@@ -811,6 +811,30 @@ public function testUncheckedTimeoutThrows()
}
}
+ public function testTimeoutWithActiveConcurrentStream()
+ {
+ $p1 = TestHttpServer::start(8067);
+ $p2 = TestHttpServer::start(8077);
+
+ $client = $this->getHttpClient(__FUNCTION__);
+ $streamingResponse = $client->request('GET', 'http://localhost:8067/max-duration');
+ $blockingResponse = $client->request('GET', 'http://localhost:8077/timeout-body', [
+ 'timeout' => 0.25,
+ ]);
+
+ $this->assertSame(200, $streamingResponse->getStatusCode());
+ $this->assertSame(200, $blockingResponse->getStatusCode());
+
+ $this->expectException(TransportExceptionInterface::class);
+
+ try {
+ $blockingResponse->getContent();
+ } finally {
+ $p1->stop();
+ $p2->stop();
+ }
+ }
+
public function testDestruct()
{
$client = $this->getHttpClient(__FUNCTION__);
diff --git a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
index 5dae27c912a0..1af9130f964f 100644
--- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
+++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
@@ -16,23 +16,28 @@
class TestHttpServer
{
- private static $process;
+ private static $process = [];
- public static function start()
+ public static function start(int $port = 8057)
{
- if (self::$process) {
- self::$process->stop();
+ if (isset(self::$process[$port])) {
+ self::$process[$port]->stop();
+ } else {
+ register_shutdown_function(static function () use ($port) {
+ self::$process[$port]->stop();
+ });
}
$finder = new PhpExecutableFinder();
- $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:8057']));
+ $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:'.$port]));
$process->setWorkingDirectory(__DIR__.'/Fixtures/web');
$process->start();
+ self::$process[$port] = $process;
do {
usleep(50000);
- } while (!@fopen('http://127.0.0.1:8057/', 'r'));
+ } while (!@fopen('http://127.0.0.1:'.$port, 'r'));
- self::$process = $process;
+ return $process;
}
}