diff --git a/.appveyor.yml b/.appveyor.yml index c786f6f9d786e..648c9a9849e6e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -46,6 +46,8 @@ install: - php composer.phar self-update - copy /Y .github\composer-config.json %APPDATA%\Composer\config.json - php composer.phar global require --no-progress --no-scripts --no-plugins symfony/flex dev-master + - git config --global user.email "" + - git config --global user.name "Symfony" - php .github/build-packages.php "HEAD^" src\Symfony\Bridge\PhpUnit src\Symfony\Contracts - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - php composer.phar update --no-progress --no-suggest --ansi diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e459d1e55f616..9e23ca5c1ea74 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,7 +4,7 @@ /src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @dunglas # Form /src/Symfony/Bridge/Twig/Extension/FormExtension.php @xabbuh -/src/Symfony/Bridge/Twig/Form/* @xabbuh +/src/Symfony/Bridge/Twig/Form/ @xabbuh /src/Symfony/Bridge/Twig/Node/FormThemeNode.php @xabbuh /src/Symfony/Bridge/Twig/Node/RenderBlockNode.php @xabbuh /src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php @xabbuh @@ -13,34 +13,34 @@ /src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @xabbuh /src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php @xabbuh /src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php @xabbuh -/src/Symfony/Bundle/FrameworkBundle/Resources/views/* @xabbuh +/src/Symfony/Bundle/FrameworkBundle/Resources/views/ @xabbuh /src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php @xabbuh /src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php @xabbuh /src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php @xabbuh /src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @xabbuh -/src/Symfony/Component/Form/* @xabbuh +/src/Symfony/Component/Form/ @xabbuh # HttpKernel /src/Symfony/Component/HttpKernel/Log/Logger.php @dunglas # LDAP -/src/Symfony/Component/Ldap/* @csarrazi +/src/Symfony/Component/Ldap/ @csarrazi # Lock -/src/Symfony/Component/Lock/* @jderusse +/src/Symfony/Component/Lock/ @jderusse # Messenger -/src/Symfony/Bridge/Doctrine/Messenger/* @sroze -/src/Symfony/Component/Messenger/* @sroze +/src/Symfony/Bridge/Doctrine/Messenger/ @sroze +/src/Symfony/Component/Messenger/ @sroze # PropertyInfo -/src/Symfony/Component/PropertyInfo/* @dunglas -/src/Symfony/Bridge/Doctrine/PropertyInfo/* @dunglas +/src/Symfony/Component/PropertyInfo/ @dunglas +/src/Symfony/Bridge/Doctrine/PropertyInfo/ @dunglas # Serializer -/src/Symfony/Component/Serializer/* @dunglas +/src/Symfony/Component/Serializer/ @dunglas # WebLink -/src/Symfony/Component/WebLink/* @dunglas +/src/Symfony/Component/WebLink/ @dunglas # Workflow /src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php @lyrixx /src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php @lyrixx /src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @lyrixx /src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php @lyrixx /src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php @lyrixx -/src/Symfony/Component/Workflow/* @lyrixx +/src/Symfony/Component/Workflow/ @lyrixx # Yaml -/src/Symfony/Component/Yaml/* @xabbuh +/src/Symfony/Component/Yaml/ @xabbuh diff --git a/.github/build-packages.php b/.github/build-packages.php index d859e3ec808a1..81a309911135c 100644 --- a/.github/build-packages.php +++ b/.github/build-packages.php @@ -47,7 +47,7 @@ if (isset($preferredInstall[$package->name]) && 'source' === $preferredInstall[$package->name]) { passthru("cd $dir && tar -cf package.tar --exclude='package.tar' *"); } else { - passthru("cd $dir && git init && git add . && git commit --author \"Symfony <>\" -m - && git archive -o package.tar HEAD && rm .git/ -Rf"); + passthru("cd $dir && git init && git add . && git commit -q -m - && git archive -o package.tar HEAD && rm .git/ -Rf"); } if (!isset($package->extra->{'branch-alias'}->{'dev-master'})) { diff --git a/.travis.yml b/.travis.yml index aedc228105a81..5b09c1fb3463f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -202,6 +202,9 @@ install: - | # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components + git config --global user.email "" + git config --global user.name "Symfony" + if [[ ! $deps ]]; then php .github/build-packages.php HEAD^ src/Symfony/Bridge/PhpUnit src/Symfony/Contracts else diff --git a/CHANGELOG-4.3.md b/CHANGELOG-4.3.md index 0d1862231449a..5707e18c465a7 100644 --- a/CHANGELOG-4.3.md +++ b/CHANGELOG-4.3.md @@ -7,6 +7,49 @@ in 4.3 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.3.0...v4.3.1 +* 4.3.6 (2019-11-01) + + * bug #34198 [HttpClient] Fix perf issue when doing thousands of requests with curl (nicolas-grekas) + * bug #33998 [Config] Disable default alphabet sorting in glob function due of unstable sort (hurricane-voronin) + * bug #34144 [Serializer] Improve messages for unexpected resources values (fancyweb) + * bug #34186 [HttpClient] always return the empty string when the response cannot have a body (nicolas-grekas) + * bug #34167 [HttpFoundation] Allow to not pass a parameter to Request::isMethodSafe() (dunglas) + * bug #33828 [DoctrineBridge] Auto-validation must work if no regex are passed (dunglas) + * bug #34080 [SecurityBundle] correct types for default arguments for firewall configs (shieldo) + * bug #34152 [Workflow] Made the configuration more robust for the 'property' key (lyrixx) + * bug #34154 [HttpClient] fix handling of 3xx with no Location header - ignore Content-Length when no body is expected (nicolas-grekas) + * bug #34140 [Security/Core] make NativePasswordEncoder use sodium to validate passwords when possible (nicolas-grekas) + * bug #33999 [Form] Make sure to collect child forms created on *_SET_DATA events (yceruto) + * bug #34090 [WebProfilerBundle] Improve display in Email panel for dark theme (antograssiot) + * bug #34116 [HttpClient] ignore the body of responses to HEAD requests (nicolas-grekas) + * bug #32456 [Messenger] use database platform to convert correctly the DateTime (roukmoute) + * bug #34107 [Messenger] prevent infinite redelivery loops and blocked queues (Tobion) + * bug #32341 [Messenger] Show exceptions after multiple retries (TimoBakx) + * bug #34082 Revert "[Messenger] Fix exception message of failed message is dropped (Tobion) + * bug #34021 [TwigBridge] do not render errors for checkboxes twice (xabbuh) + * bug #34017 [Messenger] Fix ignored options in redis transport (chalasr) + * bug #34041 [HttpKernel] fix wrong removal of the just generated container dir (nicolas-grekas) + * bug #34024 [Routing] fix route loading with wildcard, but dir or file is empty (gseidel) + * bug #34023 [Dotenv] allow LF in single-quoted strings (nicolas-grekas) + * bug #33818 [Yaml] Throw exception for tagged invalid inline elements (gharlan) + * bug #33994 [Mailer] Fix Mandrill Transport API payload for named addresses (Michaël Perrin) + * bug #33985 [HttpClient] workaround curl_multi_select() issue (nicolas-grekas) + * bug #33948 [PropertyInfo] Respect property name case when guessing from public method name (antograssiot) + * bug #33962 [Cache] fixed TagAwareAdapter returning invalid cache (v-m-i) + * bug #33958 [DI] Add extra type check to php dumper (gquemener) + * bug #33965 [HttpFoundation] Add plus character `+` to legal mime subtype (ilzrv) + * bug #32943 [Dotenv] search variable values in ENV first then env file (soufianZantar) + * bug #33943 [VarDumper] fix resetting the "bold" state in CliDumper (nicolas-grekas) + * bug #33936 [HttpClient] Missing argument in method_exists (detinkin) + * bug #33937 [Cache] ignore unserialization failures in AbstractTagAwareAdapter::doDelete() (nicolas-grekas) + * bug #33935 [HttpClient] send `Accept: */*` by default, fix removing it when needed (nicolas-grekas) + * bug #33922 [Cache] remove implicit dependency on symfony/filesystem (nicolas-grekas) + * bug #33927 Allow to set SameSite config to 'none' (ihmels) + * bug #33930 [Cache] clean tags folder on invalidation (nicolas-grekas) + * bug #33919 [VarDumper] fix array key error for class SymfonyCaster (zcodes) + * bug #33885 [Form][DateTimeImmutableToDateTimeTransformer] Preserve microseconds and use \DateTime::createFromImmutable() when available (fancyweb) + * bug #33900 [HttpKernel] Fix to populate $dotenvVars in data collector when not using putenv() (mynameisbogdan) + * 4.3.5 (2019-10-07) * bug #33742 [Crawler] document $default as string|null (nicolas-grekas) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 963f9e960293c..2cf905be32e88 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -6,36 +6,36 @@ Symfony is the result of the work of many people who made the code better - Fabien Potencier (fabpot) - Nicolas Grekas (nicolas-grekas) - - Bernhard Schussek (bschussek) - Christian Flothmann (xabbuh) + - Bernhard Schussek (bschussek) - Tobias Schultze (tobion) - Christophe Coevoet (stof) - Robin Chalas (chalas_r) - Jordi Boggiano (seldaek) - - Victor Berchet (victor) - Kévin Dunglas (dunglas) + - Victor Berchet (victor) - Maxime Steinhausser (ogizanagi) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) - - Jakub Zalas (jakubzalas) - Roland Franssen (ro0) + - Jakub Zalas (jakubzalas) - Johannes S (johannes) - - Kris Wallsmith (kriswallsmith) - Grégoire Pineau (lyrixx) + - Kris Wallsmith (kriswallsmith) - Hugo Hamon (hhamon) + - Yonel Ceruto (yonelceruto) - Abdellatif Ait boudad (aitboudad) - Samuel ROZE (sroze) - - Yonel Ceruto (yonelceruto) - Romain Neutron (romain) - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) + - Alexander M. Turek (derrabus) - Martin Hasoň (hason) - Hamza Amrouche (simperfit) - Jeremy Mikola (jmikola) - - Alexander M. Turek (derrabus) - Jean-François Simon (jfsimon) - Jules Pietri (heah) - Benjamin Eberlei (beberlei) @@ -44,21 +44,21 @@ Symfony is the result of the work of many people who made the code better - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) - Sarah Khalil (saro0h) + - Thomas Calvet (fancyweb) - Tobias Nyholm (tobias) - Jonathan Wage (jwage) - Lynn van der Berg (kjarli) - Diego Saint Esteben (dosten) - - Thomas Calvet (fancyweb) - Alexandre Salomé (alexandresalome) - William Durand (couac) - ornicar + - Pierre du Plessis (pierredup) - Dany Maillard (maidmaid) - Francis Besset (francisbesset) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - - Pierre du Plessis (pierredup) - - Matthias Pigulla (mpdude) - Konstantin Myakshin (koc) + - Matthias Pigulla (mpdude) - Bulat Shakirzyanov (avalanche123) - Grégoire Paris (greg0ire) - Saša Stamenković (umpirsky) @@ -69,13 +69,13 @@ Symfony is the result of the work of many people who made the code better - Miha Vrhovnik - Diego Saint Esteben (dii3g0) - Gábor Egyed (1ed) - - Konstantin Kudryashov (everzet) - Titouan Galopin (tgalopin) + - Konstantin Kudryashov (everzet) + - David Maicher (dmaicher) - Bilal Amarni (bamarni) - Mathieu Piot (mpiot) - - David Maicher (dmaicher) - - Florin Patan (florinpatan) - Gabriel Ostrolucký (gadelat) + - Florin Patan (florinpatan) - Vladimir Reznichenko (kalessil) - Jáchym Toušek (enumag) - Michel Weimerskirch (mweimerskirch) @@ -83,6 +83,7 @@ Symfony is the result of the work of many people who made the code better - Issei Murasawa (issei_m) - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) + - Jan Schädlich (jschaedl) - Christian Raue - Arnout Boks (aboks) - Deni @@ -90,7 +91,6 @@ Symfony is the result of the work of many people who made the code better - Dariusz Górecki (canni) - Douglas Greenshields (shieldo) - David Buchmann (dbu) - - Jan Schädlich (jschaedl) - Dariusz Ruminski - Lee McDermott - Brandon Turner @@ -114,30 +114,30 @@ Symfony is the result of the work of many people who made the code better - Baptiste Clavié (talus) - marc.weistroff - Tomáš Votruba (tomas_votruba) + - Jérôme Vasseur (jvasseur) - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) - - Jérôme Vasseur (jvasseur) + - Sebastiaan Stok (sstok) + - Adrien Brault (adrienbrault) - Peter Kokot (maastermedia) - Jacob Dreesen (jdreesen) - Florian Voutzinos (florianv) - - Sebastiaan Stok (sstok) - Colin Frei - Javier Spagnoletti (phansys) - - Adrien Brault (adrienbrault) - Joshua Thijssen - Daniel Wehner (dawehner) - excelwebzone - Gordon Franke (gimler) + - Teoh Han Hui (teohhanhui) - Oskar Stark (oskarstark) + - Alex Pott - Fabien Pennequin (fabienpennequin) - Théo FIDRY (theofidry) - - Teoh Han Hui (teohhanhui) - Eric GELOEN (gelo) - Joel Wurtz (brouznouf) - Lars Strojny (lstrojny) - Tugdual Saunier (tucksaun) - - Alex Pott - Jannik Zschiesche (apfelbox) - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) @@ -152,6 +152,7 @@ Symfony is the result of the work of many people who made the code better - Daniel Gomes (danielcsgomes) - Hidenori Goto (hidenorigoto) - Andréia Bohner (andreia) + - Julien Falque (julienfalque) - Arnaud Kleinpeter (nanocom) - Guilherme Blanco (guilhermeblanco) - SpacePossum @@ -160,7 +161,6 @@ Symfony is the result of the work of many people who made the code better - François-Xavier de Guillebon (de-gui_f) - Oleg Voronkovich - Philipp Wahala (hifi) - - Julien Falque (julienfalque) - Rafael Dohms (rdohms) - jwdeitch - Mikael Pajunen @@ -172,6 +172,7 @@ Symfony is the result of the work of many people who made the code better - Richard Shank (iampersistent) - Thomas Rabaix (rande) - Vincent Touzet (vincenttouzet) + - Gregor Harlan (gharlan) - jeremyFreeAgent (jeremyfreeagent) - Rouven Weßling (realityking) - Alexander Schranz (alexander-schranz) @@ -193,6 +194,7 @@ Symfony is the result of the work of many people who made the code better - James Halsall (jaitsu) - Matthieu Napoli (mnapoli) - Florent Mata (fmata) + - Arman Hosseini - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) @@ -206,11 +208,11 @@ Symfony is the result of the work of many people who made the code better - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) - DQNEO - - Gregor Harlan (gharlan) + - mcfedr (mcfedr) - Gary PEGEOT (gary-p) - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) - - Arman Hosseini + - Andreas Braun - Mathieu Lemoine (lemoinem) - Christian Schmidt - Andreas Hucks (meandmymonkey) @@ -241,7 +243,6 @@ Symfony is the result of the work of many people who made the code better - jeff - John Kary (johnkary) - Andreas Schempp (aschempp) - - Andreas Braun - Justin Hileman (bobthecow) - Blanchon Vincent (blanchonvincent) - Michele Orselli (orso) @@ -265,7 +266,6 @@ Symfony is the result of the work of many people who made the code better - julien pauli (jpauli) - Lorenz Schori - Sébastien Lavoie (lavoiesl) - - mcfedr (mcfedr) - Dariusz - Michael Babker (mbabker) - Francois Zaninotto @@ -314,12 +314,14 @@ Symfony is the result of the work of many people who made the code better - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - jdhoek + - Maxime Helias (maxhelias) - David Prévot - Bob den Otter (bopp) - Thomas Schulz (king2500) - Frank de Jonge (frenkynet) - Nikita Konstantinov - Wodor Wodorski + - dFayet - Thomas Lallement (raziel057) - Colin O'Dell (colinodell) - Giorgio Premi @@ -343,6 +345,7 @@ Symfony is the result of the work of many people who made the code better - Christian Schmidt - Patrick Landolt (scube) - MatTheCat + - David Badura (davidbadura) - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) @@ -361,6 +364,7 @@ Symfony is the result of the work of many people who made the code better - Jerzy Zawadzki (jzawadzki) - Wouter J - Ismael Ambrosi (iambrosi) + - Ruud Kamphuis (ruudk) - Emmanuel BORGES (eborges78) - Aurelijus Valeiša (aurelijus) - Jan Decavele (jandc) @@ -374,7 +378,6 @@ Symfony is the result of the work of many people who made the code better - Francesc Rosàs (frosas) - Romain Pierre (romain-pierre) - Julien Galenski (ruian) - - Maxime Helias - Bongiraud Dominique - janschoenherr - Emanuele Gaspari (inmarelibero) @@ -382,7 +385,6 @@ Symfony is the result of the work of many people who made the code better - Berny Cantos (xphere81) - Thierry Thuon (lepiaf) - Ricard Clau (ricardclau) - - dFayet - Mark Challoner (markchalloner) - Gennady Telegin (gtelegin) - Erin Millard @@ -410,10 +412,10 @@ Symfony is the result of the work of many people who made the code better - Robbert Klarenbeek (robbertkl) - Eric Masoero (eric-masoero) - JhonnyL - - David Badura (davidbadura) - hossein zolfi (ocean) - Clément Gautier (clementgautier) - Thomas Bisignani (toma) + - Dāvis Zālītis (k0d3r1s) - Sanpi - Eduardo Gulias (egulias) - giulio de donato (liuggio) @@ -436,6 +438,7 @@ Symfony is the result of the work of many people who made the code better - Tamas Szijarto - Michele Locati - Pavel Volokitin (pvolok) + - Valentine Boineau (valentineboineau) - Arthur de Moulins (4rthem) - Matthias Althaus (althaus) - Nicolas Dewez (nicolas_dewez) @@ -447,13 +450,13 @@ Symfony is the result of the work of many people who made the code better - Joe Lencioni - Daniel Tschinder - vladimir.reznichenko - - Ruud Kamphuis (ruudk) - Kai - Lee Rowlands - Krzysztof Piasecki (krzysztek) - Maximilian Reichel (phramz) - Loick Piera (pyrech) - Alain Hippolyte (aloneh) + - Grenier Kévin (mcsky_biig) - Karoly Negyesi (chx) - Ivan Kurnosov - Xavier HAUSHERR @@ -481,9 +484,11 @@ Symfony is the result of the work of many people who made the code better - Jeanmonod David (jeanmonod) - Christopher Davis (chrisguitarguy) - Webnet team (webnet) + - Farhad Safarov - Jan Schumann - Niklas Fiekas - Markus Bachmann (baachi) + - Kévin THERAGE (kevin_therage) - lancergr - Mihai Stancu - Ivan Nikolaev (destillat) @@ -499,6 +504,7 @@ Symfony is the result of the work of many people who made the code better - EdgarPE - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) + - Sylvain Fabre (sylfabre) - Martijn Cuppens - Vlad Gregurco (vgregurco) - Maciej Malarz (malarzm) @@ -517,6 +523,7 @@ Symfony is the result of the work of many people who made the code better - Tobias Weichart - Gonzalo Vilaseca (gonzalovilaseca) - Marcin Sikoń (marphi) + - Tien Vo (tienvx) - Denis Brumann (dbrumann) - Dominik Zogg (dominik.zogg) - Marek Pietrzak @@ -533,6 +540,7 @@ Symfony is the result of the work of many people who made the code better - Anton Bakai - Martin Auswöger - Rhodri Pugh (rodnaph) + - battye - Sam Fleming (sam_fleming) - Alex Bakhturin - Patrick Reimers (preimers) @@ -606,7 +614,6 @@ Symfony is the result of the work of many people who made the code better - Nate (frickenate) - Timothée Barray (tyx) - jhonnyL - - Grenier Kévin (mcsky_biig) - sasezaki - Dawid Pakuła (zulusx) - Florian Rey (nervo) @@ -621,6 +628,7 @@ Symfony is the result of the work of many people who made the code better - Kevin Saliou (kbsali) - Shawn Iwinski - Gawain Lynch (gawain) + - mmokhi - NothingWeAre - Ryan - Alexander Deruwe (aderuwe) @@ -643,9 +651,11 @@ Symfony is the result of the work of many people who made the code better - Disquedur - Michiel Boeckaert (milio) - Geoffrey Tran (geoff) + - Kyle - Jan Behrens - Mantas Var (mvar) - Chris Tanaskoski + - Terje Bråten - Sebastian Krebs - Piotr Stankowski - Baptiste Leduc (bleduc) @@ -669,6 +679,7 @@ Symfony is the result of the work of many people who made the code better - Kyle Evans (kevans91) - Charles-Henri Bruyand - Max Rath (drak3) + - Oleg Andreyev - Stéphane Escandell (sescandell) - Konstantin S. M. Möllers (ksmmoellers) - James Johnston @@ -676,7 +687,6 @@ Symfony is the result of the work of many people who made the code better - Alexandre Dupuy (satchette) - Malte Blättermann - Desjardins Jérôme (jewome62) - - Kévin THERAGE (kevin_therage) - Simeon Kolev (simeon_kolev9) - Joost van Driel (j92) - Jonas Elfering @@ -693,7 +703,6 @@ Symfony is the result of the work of many people who made the code better - Gunnstein Lye (glye) - Maxime Douailin - Jean Pasdeloup (pasdeloup) - - Sylvain Fabre (sylfabre) - Benjamin Cremer (bcremer) - Javier López (loalf) - Reinier Kip @@ -712,6 +721,7 @@ Symfony is the result of the work of many people who made the code better - DerManoMann - Rostyslav Kinash - Dennis Fridrich (dfridrich) + - Mardari Dorel (dorumd) - Daisuke Ohata - Vincent Simonin - Alex Bogomazov (alebo) @@ -730,6 +740,7 @@ Symfony is the result of the work of many people who made the code better - Miquel Rodríguez Telep (mrtorrent) - Sergey Kolodyazhnyy (skolodyazhnyy) - umpirski + - M. Vondano - Quentin de Longraye (quentinus95) - Chris Heng (gigablah) - Shaun Simmons (simshaun) @@ -750,7 +761,7 @@ Symfony is the result of the work of many people who made the code better - Kristijan Kanalas - Stephan Vock - Benjamin Zikarsky (bzikarsky) - - battye + - Ruben Jacobs (rubenj) - Simon Schick (simonsimcity) - redstar504 - Tristan Roussel @@ -799,6 +810,7 @@ Symfony is the result of the work of many people who made the code better - Indra Gunawan (guind) - Peter Ward - Davide Borsatto (davide.borsatto) + - Markus Fasselt (digilist) - Julien DIDIER (juliendidier) - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) @@ -935,7 +947,6 @@ Symfony is the result of the work of many people who made the code better - Benoît Bourgeois - mantulo - Stefan Kruppa - - mmokhi - corphi - JoppeDC - grizlik @@ -1005,10 +1016,8 @@ Symfony is the result of the work of many people who made the code better - Pablo Lozano (arkadis) - Erik Saunier (snickers) - Rootie - - Kyle - Daniel Alejandro Castro Arellano (lexcast) - sensio - - Terje Bråten - Thomas Jarrand - Antoine Bluchet (soyuka) - Sebastien Morel (plopix) @@ -1031,7 +1040,6 @@ Symfony is the result of the work of many people who made the code better - The Whole Life to Learn - Mikkel Paulson - ergiegonzaga - - Farhad Safarov - Liverbool (liverbool) - Sam Malone - Phan Thanh Ha (haphan) @@ -1039,7 +1047,9 @@ Symfony is the result of the work of many people who made the code better - neghmurken - xaav - Mahmoud Mostafa (mahmoud) + - Julien Turby - Ahmed Abdou + - Daniel Iwaniec - Pieter - Michael Tibben - Billie Thompson @@ -1115,6 +1125,7 @@ Symfony is the result of the work of many people who made the code better - Chris Tiearney - Oliver Hoff - Ole Rößner (basster) + - rtek - Faton (notaf) - Tom Houdmont - Per Sandström (per) @@ -1133,6 +1144,7 @@ Symfony is the result of the work of many people who made the code better - ilyes kooli - gr1ev0us - mlazovla + - Alejandro Diaz Torres - Max Beutel - Antanas Arvasevicius - Pierre Dudoret @@ -1152,6 +1164,7 @@ Symfony is the result of the work of many people who made the code better - Ken Marfilla (marfillaster) - benatespina (benatespina) - Denis Kop + - HypeMC - Jean-Guilhem Rouel (jean-gui) - jfcixmedia - Dominic Tubach @@ -1167,6 +1180,7 @@ Symfony is the result of the work of many people who made the code better - Soner Sayakci - hugofonseca (fonsecas72) - Marc Duboc (icemad) + - Matthias Krauser (mkrauser) - Martynas Narbutas - Toon Verwerft (veewee) - Bailey Parker @@ -1216,6 +1230,7 @@ Symfony is the result of the work of many people who made the code better - Mathias STRASSER (roukmoute) - Thomason, James - Gordienko Vladislav + - marie - Viacheslav Sychov - Alexandre Quercia (alquerci) - Helmut Hummel (helhum) @@ -1307,8 +1322,8 @@ Symfony is the result of the work of many people who made the code better - Ilia (aliance) - Chris McCafferty (cilefen) - Mo Di (modi) - - Tien Vo (tienvx) - Pablo Schläpfer + - SuRiKmAn - Gert de Pagter - Jelte Steijaert (jelte) - David Négrier (moufmouf) @@ -1319,11 +1334,13 @@ Symfony is the result of the work of many people who made the code better - Alex Vasilchenko - sez-open - Xavier Coureau + - fruty - ConneXNL - Aharon Perkel - matze - Rubén Calvo (rubencm) - Abdul.Mohsen B. A. A + - Swen van Zanten - Benoît Burnichon - pthompson - Malaney J. Hill @@ -1360,6 +1377,7 @@ Symfony is the result of the work of many people who made the code better - sl_toto (sl_toto) - Walter Dal Mut (wdalmut) - abluchet + - Ruud Arentsen - Matthieu - Albin Kerouaton - Sébastien HOUZÉ @@ -1385,11 +1403,11 @@ Symfony is the result of the work of many people who made the code better - Constantine Shtompel - Jules Lamur - Renato Mendes Figueiredo + - pdommelen - Eric Stern - ShiraNai7 - Cedrick Oka - Antal Áron (antalaron) - - Markus Fasselt (digilist) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) - Fleuv @@ -1408,6 +1426,7 @@ Symfony is the result of the work of many people who made the code better - Andreas Frömer - Philip Frank - Lance McNearney + - Jeroen Spee (jeroens) - Giorgio Premi - ncou - Ian Carroll @@ -1420,6 +1439,7 @@ Symfony is the result of the work of many people who made the code better - Luis Galeas - Martin Pärtel - Bastien Jaillot (bastnic) + - Daniel Rotter (danrot) - Frédéric Bouchery (fbouchery) - Patrick Daley (padrig) - Xavier Briand (xavierbriand) @@ -1440,7 +1460,6 @@ Symfony is the result of the work of many people who made the code better - Daniel González Zaballos (dem3trio) - Emmanuel Vella (emmanuel.vella) - Guillaume BRETOU (guiguiboy) - - Dāvis Zālītis (k0d3r1s) - Carsten Nielsen (phreaknerd) - Roger Guasch (rogerguasch) - Jay Severson @@ -1559,6 +1578,7 @@ Symfony is the result of the work of many people who made the code better - povilas - Gavin Staniforth - Alessandro Tagliapietra (alex88) + - Andy Palmer (andyexeter) - Biji (biji) - Jérôme Tanghe (deuchnord) - Alex Teterin (errogaht) @@ -1606,7 +1626,6 @@ Symfony is the result of the work of many people who made the code better - Jibé Barth (jibbarth) - Matthew Foster (mfoster) - Reyo Stallenberg (reyostallenberg) - - Ruben Jacobs (rubenj) - Paul Seiffert (seiffert) - Vasily Khayrulin (sirian) - Stefan Koopmanschap (skoop) @@ -1648,6 +1667,7 @@ Symfony is the result of the work of many people who made the code better - Peter Bex - Manatsawin Hanmongkolchai - Gunther Konig + - Joe Springe - Mickael GOETZ - Maciej Schmidt - Dennis Væversted @@ -1677,12 +1697,12 @@ Symfony is the result of the work of many people who made the code better - me_shaon - 蝦米 - Grayson Koonce (breerly) - - Mardari Dorel (dorumd) - Andrey Helldar (helldar) - Karim Cassam Chenaï (ka) - Maksym Slesarenko (maksym_slesarenko) - Michal Kurzeja (mkurzeja) - Nicolas Bastien (nicolas_bastien) + - Peter Bowyer (pbowyer) - Nikola Svitlica (thecelavi) - Denis (yethee) - Andrew Zhilin (zhil) @@ -1760,7 +1780,6 @@ Symfony is the result of the work of many people who made the code better - Yannick Warnier (ywarnier) - Kevin Decherf - Jason Woods - - Oleg Andreyev - klemens - dened - Dmitry Korotovsky @@ -1777,6 +1796,7 @@ Symfony is the result of the work of many people who made the code better - taiiiraaa - Trevor Suarez - gedrox + - Bohan Yang - Alan Bondarchuk - Joe Bennett - dropfen @@ -1810,6 +1830,7 @@ Symfony is the result of the work of many people who made the code better - Jan Marek (janmarek) - Mark de Haan (markdehaan) - Dan Patrick (mdpatrick) + - Geoffrey Monte (numerogeek) - Pedro Magalhães (pmmaga) - Rares Vlaseanu (raresvla) - tante kinast (tante) @@ -1907,6 +1928,7 @@ Symfony is the result of the work of many people who made the code better - hainey - Juan M Martínez - Gilles Gauthier + - Pavinthan - ddebree - Kuba Werłos - Gyula Szucs @@ -2032,11 +2054,13 @@ Symfony is the result of the work of many people who made the code better - jc - BenjaminBeck - Aurelijus Rožėnas + - Beno!t POLASZEK - Jordan Hoff - znerol - Christian Eikermann - Kai Eichinger - Antonio Angelino + - Jens Schulze - Matt Fields - Niklas Keller - Andras Debreczeni @@ -2115,6 +2139,7 @@ Symfony is the result of the work of many people who made the code better - Lebnik - nsbx - Shude + - Richard Hodgson - Ondřej Führer - Sema - Elan Ruusamäe @@ -2134,6 +2159,7 @@ Symfony is the result of the work of many people who made the code better - Benjamin Long - Ben Miller - Peter Gribanov + - Matteo Galli - kwiateusz - jspee - Ilya Bulakh @@ -2172,6 +2198,7 @@ Symfony is the result of the work of many people who made the code better - Lin Lu - arduanov - sualko + - Molkobain - Bilge - ADmad - Nicolas Roudaire @@ -2282,6 +2309,7 @@ Symfony is the result of the work of many people who made the code better - Wouter Sioen (wouter_sioen) - Xavier Amado (xamado) - Jesper Søndergaard Pedersen (zerrvox) + - Alexander Menshchikov (zmey_kk) - Florent Cailhol - szymek - Kovacs Nicolas diff --git a/link b/link index d839f8f8072d9..08687bbba5cc7 100755 --- a/link +++ b/link @@ -66,6 +66,6 @@ foreach (glob("$pathToProject/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as echo "\"$package\" has been linked to \"$sfPackages[$package]\".".PHP_EOL; } -foreach (glob("$pathToProject/var/cache/*") as $cacheDir) { +foreach (glob("$pathToProject/var/cache/*", GLOB_NOSORT) as $cacheDir) { $filesystem->remove($cacheDir); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php index e5d4303911105..d7d7cae5ecda9 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php @@ -30,7 +30,7 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase */ private $em; - protected static $supportedFeatureSetVersion = 304; + protected static $supportedFeatureSetVersion = 403; protected function getExtensions() { diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 0f530d37d62a4..0367fc3edfad4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -58,7 +58,7 @@ class EntityTypeTest extends BaseTypeTest */ private $emRegistry; - protected static $supportedFeatureSetVersion = 304; + protected static $supportedFeatureSetVersion = 403; protected function setUp(): void { @@ -187,7 +187,7 @@ public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ + $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => new \stdClass(), diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php index 2dcab2533d375..fbae0fbde2dac 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php @@ -173,7 +173,7 @@ public function testClassValidator(bool $expected, string $classValidatorRegexp public function regexpProvider() { return [ - [false, null], + [true, null], [true, '{^'.preg_quote(DoctrineLoaderEntity::class).'$|^'.preg_quote(Entity::class).'$}'], [false, '{^'.preg_quote(Entity::class).'$}'], ]; diff --git a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php index 76a2b4d8b0288..138b5d1cbbeee 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php +++ b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php @@ -43,7 +43,7 @@ public function __construct(EntityManagerInterface $entityManager, string $class public function loadClassMetadata(ClassMetadata $metadata): bool { $className = $metadata->getClassName(); - if (null === $this->classValidatorRegexp || !preg_match($this->classValidatorRegexp, $className)) { + if (null !== $this->classValidatorRegexp && !preg_match($this->classValidatorRegexp, $className)) { return false; } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php index 03696c69f0e4d..ed84eedf4a92a 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php @@ -257,14 +257,11 @@ public function endTest($test, $time) if (class_exists('PHPUnit_Util_Blacklist', false)) { $Test = 'PHPUnit_Util_Test'; $BaseTestRunner = 'PHPUnit_Runner_BaseTestRunner'; - $Warning = 'PHPUnit_Framework_Warning'; } else { $Test = 'PHPUnit\Util\Test'; $BaseTestRunner = 'PHPUnit\Runner\BaseTestRunner'; - $Warning = 'PHPUnit\Framework\Warning'; } $className = \get_class($test); - $classGroups = $Test::getGroups($className); $groups = $Test::getGroups($className, $test->getName(false)); if (null !== $this->reportUselessTests) { diff --git a/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php b/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php index 612bec14e5329..8925d588772a8 100644 --- a/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php +++ b/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php @@ -28,7 +28,6 @@ public function compile(Compiler $compiler) preg_match('/_([^_]+)$/', $this->getAttribute('name'), $matches); - $label = null; $arguments = iterator_to_array($this->getNode('arguments')); $blockNameSuffix = $matches[1]; diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php index cedc6b740e08d..e844801a5f0ce 100644 --- a/src/Symfony/Bridge/Twig/Node/TransNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -57,8 +57,6 @@ public function compile(Compiler $compiler) } list($msg, $defaults) = $this->compileString($this->getNode('body'), $defaults, (bool) $vars); - $method = !$this->hasNode('count') ? 'trans' : 'transChoice'; - $compiler ->write('echo $this->env->getExtension(\'Symfony\Bridge\Twig\Extension\TranslationExtension\')->trans(') ->subcompile($msg) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig index 7fcea4b0ecd25..e37de07d6b071 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig @@ -82,7 +82,6 @@ col-sm-10
{{- form_widget(form) -}} {{- form_help(form) -}} - {{- form_errors(form) -}}
{#--#} {%- endblock checkbox_row %} diff --git a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php index 06130dc817f3a..59539f278c05a 100644 --- a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php +++ b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php @@ -180,10 +180,10 @@ public function testGetFlashes() $flashMessages = $this->setFlashMessages(); $this->assertEquals($flashMessages, $this->appVariable->getFlashes([])); - $flashMessages = $this->setFlashMessages(); + $this->setFlashMessages(); $this->assertEquals([], $this->appVariable->getFlashes('this-does-not-exist')); - $flashMessages = $this->setFlashMessages(); + $this->setFlashMessages(); $this->assertEquals( ['this-does-not-exist' => []], $this->appVariable->getFlashes(['this-does-not-exist']) diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php index c862b08fe111d..a3362372c662a 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php @@ -52,7 +52,7 @@ public function testLintFileNotReadable() $filename = $this->createFile(''); unlink($filename); - $ret = $tester->execute(['filename' => [$filename]], ['decorated' => false]); + $tester->execute(['filename' => [$filename]], ['decorated' => false]); } public function testLintFileCompileTimeException() diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php index 2b38122e56410..35f3baa1b9b99 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php @@ -34,7 +34,7 @@ public function testTiming($template, $events) $twig->addExtension(new StopwatchExtension($this->getStopwatch($events))); try { - $nodes = $twig->render('template'); + $twig->render('template'); } catch (RuntimeError $e) { throw $e->getPrevious(); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index 81a5718496c02..7f2b3bb836b05 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -58,14 +58,14 @@ public function testTransUnknownKeyword() { $this->expectException('Twig\Error\SyntaxError'); $this->expectExceptionMessage('Unexpected token. Twig was looking for the "with", "from", or "into" keyword in "index" at line 3.'); - $output = $this->getTemplate("{% trans \n\nfoo %}{% endtrans %}")->render(); + $this->getTemplate("{% trans \n\nfoo %}{% endtrans %}")->render(); } public function testTransComplexBody() { $this->expectException('Twig\Error\SyntaxError'); $this->expectExceptionMessage('A message inside a trans tag must be a simple text in "index" at line 2.'); - $output = $this->getTemplate("{% trans %}\n{{ 1 + 2 }}{% endtrans %}")->render(); + $this->getTemplate("{% trans %}\n{{ 1 + 2 }}{% endtrans %}")->render(); } /** @@ -75,7 +75,7 @@ public function testTransChoiceComplexBody() { $this->expectException('Twig\Error\SyntaxError'); $this->expectExceptionMessage('A message inside a transchoice tag must be a simple text in "index" at line 2.'); - $output = $this->getTemplate("{% transchoice count %}\n{{ 1 + 2 }}{% endtranschoice %}")->render(); + $this->getTemplate("{% transchoice count %}\n{{ 1 + 2 }}{% endtranschoice %}")->render(); } public function getTransTests() diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 6687647ed8ce0..7fa6f3fc7a8dc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -508,7 +508,6 @@ private function renderEventListenerTable(EventDispatcherInterface $eventDispatc $tableHeaders = ['Order', 'Callable', 'Priority']; $tableRows = []; - $order = 1; foreach ($eventListeners as $order => $listener) { $tableRows[] = [sprintf('#%d', $order + 1), $this->formatCallable($listener), $eventDispatcher->getListenerPriority($event, $listener)]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 6d6195bbc9956..2b76352048c1c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -303,7 +303,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->scalarNode('property') - ->defaultValue('marking') + ->defaultNull() // In Symfony 5.0, set "marking" as default property ->end() ->scalarNode('service') ->cannotBeEmpty() @@ -481,6 +481,14 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) return $v; }) ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['marking_store']['property']) + && (!isset($v['marking_store']['type']) || 'method' !== $v['marking_store']['type']) + ; + }) + ->thenInvalid('"property" option is only supported by the "method" marking store.') + ->end() ->end() ->end() ->end() @@ -542,7 +550,7 @@ private function addSessionSection(ArrayNodeDefinition $rootNode) ->scalarNode('cookie_domain')->end() ->enumNode('cookie_secure')->values([true, false, 'auto'])->end() ->booleanNode('cookie_httponly')->defaultTrue()->end() - ->enumNode('cookie_samesite')->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT])->defaultNull()->end() + ->enumNode('cookie_samesite')->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT, Cookie::SAMESITE_NONE])->defaultNull()->end() ->booleanNode('use_cookies')->end() ->scalarNode('gc_divisor')->end() ->scalarNode('gc_probability')->defaultValue(1)->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 8cb3fdd242cde..a913f4f93db40 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -666,7 +666,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ if ('method' === $workflow['marking_store']['type']) { $markingStoreDefinition->setArguments([ 'state_machine' === $type, //single state - $workflow['marking_store']['property'], + $workflow['marking_store']['property'] ?? 'marking', ]); } else { foreach ($workflow['marking_store']['arguments'] as $argument) { @@ -997,8 +997,6 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co { $loader->load('assets.xml'); - $defaultVersion = null; - if ($config['version_strategy']) { $defaultVersion = new Reference($config['version_strategy']); } else { @@ -1649,6 +1647,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $defaultMiddleware = [ 'before' => [ ['id' => 'add_bus_name_stamp_middleware'], + ['id' => 'reject_redelivered_message_middleware'], ['id' => 'dispatch_after_current_bus'], ['id' => 'failed_message_processing_middleware'], ], diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml index 5ef47e751efd0..f50b613dc7856 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml @@ -48,6 +48,8 @@ + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index 1e6b90a9bf107..03b87fd6ebb8d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -98,11 +98,6 @@ public function load($resource, $type = null) } } - if (1 === substr_count($controller, ':')) { - $nonDeprecatedNotation = str_replace(':', '::', $controller); - // TODO deprecate this in 5.1 - } - $route->setDefault('_controller', $controller); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php index b65c6421a7525..7586e29818b02 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php @@ -277,8 +277,7 @@ public function testFileWhichDoesNotExist() $this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); $controller = $this->createController(); - /* @var BinaryFileResponse $response */ - $response = $controller->file('some-file.txt', 'test.php'); + $controller->file('some-file.txt', 'test.php'); } public function testIsGranted() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 8f54c30c091ee..5f2e007ddbcd4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -316,7 +316,7 @@ public function testWorkflowLegacy() public function testWorkflowAreValidated() { $this->expectException('Symfony\Component\Workflow\Exception\InvalidDefinitionException'); - $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" where found on StateMachine "my_workflow".'); + $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" were found on StateMachine "my_workflow".'); $this->createContainerFromFile('workflow_not_valid'); } @@ -739,6 +739,7 @@ public function testMessengerWithMultipleBuses() $this->assertSame([], $container->getDefinition('messenger.bus.commands')->getArgument(0)); $this->assertEquals([ ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], + ['id' => 'reject_redelivered_message_middleware'], ['id' => 'dispatch_after_current_bus'], ['id' => 'failed_message_processing_middleware'], ['id' => 'send_message'], @@ -748,6 +749,7 @@ public function testMessengerWithMultipleBuses() $this->assertSame([], $container->getDefinition('messenger.bus.events')->getArgument(0)); $this->assertEquals([ ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.events']], + ['id' => 'reject_redelivered_message_middleware'], ['id' => 'dispatch_after_current_bus'], ['id' => 'failed_message_processing_middleware'], ['id' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index 66099e29cc33e..da540da12b1f9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -56,7 +56,7 @@ public function testAssetPackageCannotHavePathAndUrl() public function testWorkflowValidationStateMachine() { $this->expectException('Symfony\Component\Workflow\Exception\InvalidDefinitionException'); - $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "a_to_b" from place/state "a" where found on StateMachine "article".'); + $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "a_to_b" from place/state "a" were found on StateMachine "article".'); $this->createContainerFromClosure(function ($container) { $container->loadFromExtension('framework', [ 'workflows' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php index 59670fdd19a24..0ae7669ce803a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php @@ -26,7 +26,7 @@ class TestExtension extends Extension implements PrependExtensionInterface public function load(array $configs, ContainerBuilder $container) { $configuration = $this->getConfiguration($configs, $container); - $config = $this->processConfiguration($configuration, $configs); + $this->processConfiguration($configuration, $configs); $container->setAlias('test.annotation_reader', new Alias('annotation_reader', true)); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDumpTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDumpTest.php index 8f826714782a9..f543058440582 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDumpTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDumpTest.php @@ -18,14 +18,14 @@ class ContainerDumpTest extends AbstractWebTestCase { public function testContainerCompilationInDebug() { - $client = $this->createClient(['test_case' => 'ContainerDump', 'root_config' => 'config.yml']); + $this->createClient(['test_case' => 'ContainerDump', 'root_config' => 'config.yml']); $this->assertTrue(static::$container->has('serializer')); } public function testContainerCompilation() { - $client = $this->createClient(['test_case' => 'ContainerDump', 'root_config' => 'config.yml', 'debug' => false]); + $this->createClient(['test_case' => 'ContainerDump', 'root_config' => 'config.yml', 'debug' => false]); $this->assertTrue(static::$container->has('serializer')); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php index c99b5e073e49a..3a87f7e4e6e0a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php @@ -59,7 +59,7 @@ public function testFlash($config, $insulate) } // set flash - $crawler = $client->request('GET', '/session_setflash/Hello%20world.'); + $client->request('GET', '/session_setflash/Hello%20world.'); // check flash displays on redirect $this->assertStringContainsString('Hello world.', $client->followRedirect()->text()); diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 2c198d2ca7105..57c344f68f22e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -42,7 +42,7 @@ "symfony/expression-language": "~3.4|~4.0", "symfony/http-client": "^4.3", "symfony/mailer": "^4.3", - "symfony/messenger": "^4.3", + "symfony/messenger": "^4.3.6", "symfony/mime": "^4.3", "symfony/process": "~3.4|~4.0", "symfony/security-csrf": "~3.4|~4.0", @@ -54,7 +54,7 @@ "symfony/twig-bundle": "~2.8|~3.2|~4.0", "symfony/validator": "^4.1", "symfony/var-dumper": "^4.3", - "symfony/workflow": "^4.3", + "symfony/workflow": "^4.3.6", "symfony/yaml": "~3.4|~4.0", "symfony/property-info": "~3.4|~4.0", "symfony/lock": "~3.4|~4.0", @@ -73,14 +73,14 @@ "symfony/dotenv": "<4.2", "symfony/dom-crawler": "<4.3", "symfony/form": "<4.3", - "symfony/messenger": "<4.3", + "symfony/messenger": "<4.3.6", "symfony/property-info": "<3.4", "symfony/serializer": "<4.2", "symfony/stopwatch": "<3.4", "symfony/translation": "<4.3", "symfony/twig-bridge": "<4.1.1", "symfony/validator": "<4.1", - "symfony/workflow": "<4.3" + "symfony/workflow": "<4.3.6" }, "suggest": { "ext-apcu": "For best performance of the system caches", diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index f7500f05e3b9f..28103a3522542 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -144,7 +144,7 @@ public function addConfiguration(NodeDefinition $node) if ('secure' === $name) { $builder->enumNode($name)->values([true, false, 'auto'])->defaultValue('auto' === $value ? null : $value); } elseif ('samesite' === $name) { - $builder->enumNode($name)->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT])->defaultValue($value); + $builder->enumNode($name)->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT, Cookie::SAMESITE_NONE])->defaultValue($value); } elseif (\is_bool($value)) { $builder->booleanNode($name)->defaultValue($value); } else { diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 1d2f0c4e503b3..9dfa8c2337dc4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -148,15 +148,15 @@ - - + false + false - + null diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php index 7e583facf7b09..450cb68ab119a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php @@ -258,7 +258,6 @@ public function providerCollectDecisionLog(): \Generator $eventDispatcher = $this->getMockBuilder(EventDispatcherInterface::class)->getMockForAbstractClass(); $decoratedVoter1 = new TraceableVoter($voter1, $eventDispatcher); - $decoratedVoter2 = new TraceableVoter($voter2, $eventDispatcher); yield [ AccessDecisionManager::STRATEGY_AFFIRMATIVE, diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php index 9eb9a08177700..01e03b0312bd9 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -61,7 +61,7 @@ public function testDefaultFailureHandler($serviceId, $defaultHandlerInjection) $options['failure_handler'] = $serviceId; } - list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); + list($container) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); $definition = $container->getDefinition('abstract_listener.foo'); $arguments = $definition->getArguments(); @@ -99,7 +99,7 @@ public function testDefaultSuccessHandler($serviceId, $defaultHandlerInjection) $options['success_handler'] = $serviceId; } - list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); + list($container) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); $definition = $container->getDefinition('abstract_listener.foo'); $arguments = $definition->getArguments(); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php index f327eece8f99c..fd812c13ae04c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php @@ -159,7 +159,7 @@ public function testCreateWithEntryPoint() 'authenticators' => ['authenticator123', 'authenticatorABC'], 'entry_point' => 'authenticatorABC', ]; - list($container, $entryPointId) = $this->executeCreate($config, null); + list(, $entryPointId) = $this->executeCreate($config, null); $this->assertEquals('authenticatorABC', $entryPointId); } @@ -172,7 +172,7 @@ private function executeCreate(array $config, $defaultEntryPointId) $userProviderId = 'my_user_provider'; $factory = new GuardAuthenticationFactory(); - list($providerId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); + list(, , $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); return [$container, $entryPointId]; } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/MissingUserProviderTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/MissingUserProviderTest.php index d795980b3ef38..6c8ba6482e45b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/MissingUserProviderTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/MissingUserProviderTest.php @@ -24,7 +24,7 @@ public function testUserProviderIsNeeded() $response = $client->getResponse(); $this->assertSame(500, $response->getStatusCode()); - $this->stringContains('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException', $response->getContent()); - $this->stringContains('"default" firewall requires a user provider but none was defined.', $response->getContent()); + $this->assertStringContainsString('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException', $response->getContent()); + $this->assertStringContainsString('"default" firewall requires a user provider but none was defined', html_entity_decode($response->getContent())); } } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig index 8c1f9af2a0d18..b7eb560002f9b 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig @@ -28,6 +28,7 @@ --shadow: 0px 0px 1px rgba(128, 128, 128, .2); --border: 1px solid #e0e0e0; --background-error: var(--color-error); + --trace-selected-background: #F7E5A1; --highlight-comment: #969896; --highlight-default: #222222; --highlight-keyword: #a71d5d; diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index d737ab0e063ad..e5a731cf19ca8 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -46,6 +46,8 @@ --base-4: #666; --base-5: #444; --base-6: #222; + --card-label-background: #eee; + --card-label-color: var(--base-6); } .theme-dark { @@ -85,6 +87,8 @@ --base-4: #666; --base-5: #e0e0e0; --base-6: #f5f5f5; + --card-label-background: var(--tab-active-background); + --card-label-color: var(--tab-active-color); } {# Basic styles @@ -436,8 +440,8 @@ table tbody td.num-col { margin-top: 0; } .card .label { - background-color: #EEE; - color: var(--base-6); + background-color: var(--card-label-background); + color: var(--card-label-color); } {# Status diff --git a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php index b705e6c6d4fdd..6318cae2d5c16 100644 --- a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php @@ -201,7 +201,7 @@ public function testConstructException() { $this->expectException('UnexpectedValueException'); $this->expectExceptionMessage('The cookie expiration time "string" is not valid.'); - $cookie = new Cookie('foo', 'bar', 'string'); + new Cookie('foo', 'bar', 'string'); } public function testSameSite() diff --git a/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php index 5e535e51fcb4b..13a4968ac596d 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php @@ -229,10 +229,14 @@ public function deleteItems(array $keys) unset($this->deferred[$key]); } - foreach ($this->doFetch($ids) as $id => $value) { - foreach ($value['tags'] ?? [] as $tag) { - $tagData[$this->getId(self::TAGS_PREFIX.$tag)][] = $id; + try { + foreach ($this->doFetch($ids) as $id => $value) { + foreach ($value['tags'] ?? [] as $tag) { + $tagData[$this->getId(self::TAGS_PREFIX.$tag)][] = $id; + } } + } catch (\Exception $e) { + // ignore unserialization failures } try { diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php index 0dd81a99704ed..67801e8ab9cc7 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php @@ -11,12 +11,10 @@ namespace Symfony\Component\Cache\Adapter; -use Symfony\Component\Cache\Exception\LogicException; use Symfony\Component\Cache\Marshaller\DefaultMarshaller; use Symfony\Component\Cache\Marshaller\MarshallerInterface; use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\Traits\FilesystemTrait; -use Symfony\Component\Filesystem\Filesystem; /** * Stores tag id <> cache id relationship as a symlink, and lookup on invalidation calls. @@ -29,8 +27,8 @@ class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface { use FilesystemTrait { - doSave as doSaveCache; - doDelete as doDeleteCache; + doSave as private doSaveCache; + doDelete as private doDeleteCache; } /** @@ -38,11 +36,6 @@ class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements Prune */ private const TAG_FOLDER = 'tags'; - /** - * @var Filesystem|null - */ - private $fs; - public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null) { $this->marshaller = $marshaller ?? new DefaultMarshaller(); @@ -57,7 +50,6 @@ protected function doSave(array $values, ?int $lifetime, array $addTagData = [], { $failed = $this->doSaveCache($values, $lifetime); - $fs = $this->getFilesystem(); // Add Tags as symlinks foreach ($addTagData as $tagId => $ids) { $tagFolder = $this->getTagFolder($tagId); @@ -67,12 +59,15 @@ protected function doSave(array $values, ?int $lifetime, array $addTagData = [], } $file = $this->getFile($id); - $fs->symlink($file, $this->getFile($id, true, $tagFolder)); + + if (!@symlink($file, $this->getFile($id, true, $tagFolder))) { + @unlink($file); + $failed[] = $id; + } } } // Unlink removed Tags - $files = []; foreach ($removeTagData as $tagId => $ids) { $tagFolder = $this->getTagFolder($tagId); foreach ($ids as $id) { @@ -80,10 +75,9 @@ protected function doSave(array $values, ?int $lifetime, array $addTagData = [], continue; } - $files[] = $this->getFile($id, false, $tagFolder); + @unlink($this->getFile($id, false, $tagFolder)); } } - $fs->remove($files); return $failed; } @@ -96,15 +90,12 @@ protected function doDelete(array $ids, array $tagData = []): bool $ok = $this->doDeleteCache($ids); // Remove tags - $files = []; - $fs = $this->getFilesystem(); foreach ($tagData as $tagId => $idMap) { $tagFolder = $this->getTagFolder($tagId); foreach ($idMap as $id) { - $files[] = $this->getFile($id, false, $tagFolder); + @unlink($this->getFile($id, false, $tagFolder)); } } - $fs->remove($files); return $ok; } @@ -115,33 +106,45 @@ protected function doDelete(array $ids, array $tagData = []): bool protected function doInvalidate(array $tagIds): bool { foreach ($tagIds as $tagId) { - $tagsFolder = $this->getTagFolder($tagId); - if (!file_exists($tagsFolder)) { + if (!file_exists($tagFolder = $this->getTagFolder($tagId))) { continue; } - foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($tagsFolder, \FilesystemIterator::SKIP_DOTS)) as $itemLink) { - if (!$itemLink->isLink()) { - throw new LogicException('Expected a (sym)link when iterating over tag folder, non link found: '.$itemLink); + set_error_handler(static function () {}); + + try { + if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -1))) { + $tagFolder = $renamed.\DIRECTORY_SEPARATOR; + } else { + $renamed = null; + } + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($tagFolder, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $itemLink) { + unlink(realpath($itemLink) ?: $itemLink); + unlink($itemLink); } - $valueFile = $itemLink->getRealPath(); - if ($valueFile && file_exists($valueFile)) { - @unlink($valueFile); + if (null === $renamed) { + continue; } - @unlink((string) $itemLink); + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + + for ($i = 0; $i < 38; ++$i) { + for ($j = 0; $j < 38; ++$j) { + rmdir($tagFolder.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j]); + } + rmdir($tagFolder.$chars[$i]); + } + rmdir($renamed); + } finally { + restore_error_handler(); } } return true; } - private function getFilesystem(): Filesystem - { - return $this->fs ?? $this->fs = new Filesystem(); - } - private function getTagFolder(string $tagId): string { return $this->getFile($tagId, false, $this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR; diff --git a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php index 3bc5d84b62eab..b83b09b013dee 100644 --- a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php @@ -97,7 +97,7 @@ protected function doSave(array $values, ?int $lifetime, array $addTagData = [], } // While pipeline isn't supported on RedisCluster, other setups will at least benefit from doing this in one op - $results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData) { + $results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData, $failed) { // Store cache items, force a ttl if none is set, as there is no MSETEX we need to set each one foreach ($serialized as $id => $value) { yield 'setEx' => [ @@ -109,11 +109,15 @@ protected function doSave(array $values, ?int $lifetime, array $addTagData = [], // Add and Remove Tags foreach ($addTagData as $tagId => $ids) { - yield 'sAdd' => array_merge([$tagId], $ids); + if (!$failed || $ids = array_diff($ids, $failed)) { + yield 'sAdd' => array_merge([$tagId], $ids); + } } foreach ($delTagData as $tagId => $ids) { - yield 'sRem' => array_merge([$tagId], $ids); + if (!$failed || $ids = array_diff($ids, $failed)) { + yield 'sRem' => array_merge([$tagId], $ids); + } } }); diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index fd810f726ea73..8bcf57e82c16a 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -160,7 +160,14 @@ public function hasItem($key) if (!$this->pool->hasItem($key)) { return false; } - if (!$itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key)->get()) { + + $itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key); + + if (!$itemTags->isHit()) { + return false; + } + + if (!$itemTags = $itemTags->get()) { return true; } @@ -300,7 +307,10 @@ private function generateItems($items, array $tagKeys) } unset($tagKeys[$key]); - $itemTags[$key] = $item->get() ?: []; + + if ($item->isHit()) { + $itemTags[$key] = $item->get() ?: []; + } if (!$tagKeys) { $tagVersions = $this->getTagVersions($itemTags); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index 63a7f39e537c2..a6132ebca2fc9 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -87,8 +87,8 @@ public function testRecursiveGet() $cache = $this->createCachePool(0, __FUNCTION__); $v = $cache->get('k1', function () use (&$counter, $cache) { - $v = $cache->get('k2', function () use (&$counter) { return ++$counter; }); - $v = $cache->get('k2', function () use (&$counter) { return ++$counter; }); + $cache->get('k2', function () use (&$counter) { return ++$counter; }); + $v = $cache->get('k2', function () use (&$counter) { return ++$counter; }); // ensure the callback is called once return $v; }); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php index 724aa9451cbce..8d0b70d619690 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php @@ -70,7 +70,7 @@ public function testTooLongNamespace() { $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException'); $this->expectExceptionMessage('Namespace must be 26 chars max, 40 given ("----------------------------------------")'); - $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + $this->getMockBuilder(MaxIdLengthAdapter::class) ->setConstructorArgs([str_repeat('-', 40)]) ->getMock(); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/NullAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/NullAdapterTest.php index 6c5710a7e4e29..ae3de76dc135d 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/NullAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/NullAdapterTest.php @@ -39,7 +39,7 @@ public function testGet() $adapter = $this->createCachePool(); $fetched = []; - $item = $adapter->get('myKey', function ($item) use (&$fetched) { $fetched[] = $item; }); + $adapter->get('myKey', function ($item) use (&$fetched) { $fetched[] = $item; }); $this->assertCount(1, $fetched); $item = $fetched[0]; $this->assertFalse($item->isHit()); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php index 573a1b1d01394..d4071baedbc91 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php @@ -31,8 +31,6 @@ public static function setUpBeforeClass(): void } self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); - - $pool = new PdoAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile])); } public static function tearDownAfterClass(): void diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php index ef939bae8c929..b4e1ebbcd83c6 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php @@ -99,6 +99,84 @@ public function testKnownTagVersionsTtl() $this->assertTrue($pool->getItem('foo')->isHit()); } + public function testTagEntryIsCreatedForItemWithoutTags() + { + $pool = $this->createCachePool(); + + $itemKey = 'foo'; + $item = $pool->getItem($itemKey); + $pool->save($item); + + $adapter = new FilesystemAdapter(); + $this->assertTrue($adapter->hasItem(TagAwareAdapter::TAGS_PREFIX.$itemKey)); + } + + public function testHasItemReturnsFalseWhenPoolDoesNotHaveItemTags() + { + $pool = $this->createCachePool(); + + $itemKey = 'foo'; + $item = $pool->getItem($itemKey); + $pool->save($item); + + $anotherPool = $this->createCachePool(); + + $adapter = new FilesystemAdapter(); + $adapter->deleteItem(TagAwareAdapter::TAGS_PREFIX.$itemKey); //simulate item losing tags pair + + $this->assertFalse($anotherPool->hasItem($itemKey)); + } + + public function testGetItemReturnsCacheMissWhenPoolDoesNotHaveItemTags() + { + $pool = $this->createCachePool(); + + $itemKey = 'foo'; + $item = $pool->getItem($itemKey); + $pool->save($item); + + $anotherPool = $this->createCachePool(); + + $adapter = new FilesystemAdapter(); + $adapter->deleteItem(TagAwareAdapter::TAGS_PREFIX.$itemKey); //simulate item losing tags pair + + $item = $anotherPool->getItem($itemKey); + $this->assertFalse($item->isHit()); + } + + public function testHasItemReturnsFalseWhenPoolDoesNotHaveItemAndOnlyHasTags() + { + $pool = $this->createCachePool(); + + $itemKey = 'foo'; + $item = $pool->getItem($itemKey); + $pool->save($item); + + $anotherPool = $this->createCachePool(); + + $adapter = new FilesystemAdapter(); + $adapter->deleteItem($itemKey); //simulate losing item but keeping tags + + $this->assertFalse($anotherPool->hasItem($itemKey)); + } + + public function testGetItemReturnsCacheMissWhenPoolDoesNotHaveItemAndOnlyHasTags() + { + $pool = $this->createCachePool(); + + $itemKey = 'foo'; + $item = $pool->getItem($itemKey); + $pool->save($item); + + $anotherPool = $this->createCachePool(); + + $adapter = new FilesystemAdapter(); + $adapter->deleteItem($itemKey); //simulate losing item but keeping tags + + $item = $anotherPool->getItem($itemKey); + $this->assertFalse($item->isHit()); + } + /** * @return MockObject|PruneableCacheInterface */ diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php index 484c44a7eb3a4..ccfc66b4a785a 100644 --- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -281,6 +281,7 @@ protected function doDelete(array $ids) foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) { if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) { $ok = false; + break; } } diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index fce8f6e2062a0..bf332fde32722 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -99,7 +99,9 @@ public function getIterator() $prefix = str_replace('\\', '/', $this->prefix); if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) { - foreach (glob($this->prefix.$this->pattern, \defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) { + $paths = glob($this->prefix.$this->pattern, GLOB_NOSORT | (\defined('GLOB_BRACE') ? GLOB_BRACE : 0)); + sort($paths); + foreach ($paths as $path) { if ($this->excludedPrefixes) { $normalizedPath = str_replace('\\', '/', $path); do { diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php index 34dc35d5f51f8..c30686721a6cc 100644 --- a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php +++ b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php @@ -154,7 +154,6 @@ private function getMetaFile() private function safelyUnserialize($file) { - $e = null; $meta = false; $content = file_get_contents($file); $signalingException = new \UnexpectedValueException(); diff --git a/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.php b/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.php index 07bd379b27a37..78c83044d8bc1 100644 --- a/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.php +++ b/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.php @@ -19,7 +19,7 @@ class DelegatingLoaderTest extends TestCase { public function testConstructor() { - $loader = new DelegatingLoader($resolver = new LoaderResolver()); + new DelegatingLoader($resolver = new LoaderResolver()); $this->assertTrue(true, '__construct() takes a loader resolver as its first argument'); } diff --git a/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php index 6b38e33baf4a2..ad331240debaf 100644 --- a/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php @@ -67,7 +67,7 @@ public function testExistsKo() $loadedClass = 123; - $res = new ClassExistenceResource('MissingFooClass', false); + new ClassExistenceResource('MissingFooClass', false); $this->assertSame(123, $loadedClass); } finally { diff --git a/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php index 90b70cc7249e7..aaadc8c4f430b 100644 --- a/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php @@ -67,7 +67,7 @@ public function testResourceDoesNotExist() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessageRegExp('/The directory ".*" does not exist./'); - $resource = new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999)); + new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999)); } public function testIsFresh() @@ -165,7 +165,7 @@ public function testSerializeUnserialize() { $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); - $unserialized = unserialize(serialize($resource)); + unserialize(serialize($resource)); $this->assertSame(realpath($this->directory), $resource->getResource()); $this->assertSame('/\.(foo|xml)$/', $resource->getPattern()); diff --git a/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php index 864604bb5b38f..f85a820fb95bb 100644 --- a/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php @@ -57,7 +57,7 @@ public function testResourceDoesNotExist() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessageRegExp('/The file ".*" does not exist./'); - $resource = new FileResource('/____foo/foobar'.mt_rand(1, 999999)); + new FileResource('/____foo/foobar'.mt_rand(1, 999999)); } public function testIsFresh() @@ -76,7 +76,7 @@ public function testIsFreshForDeletedResources() public function testSerializeUnserialize() { - $unserialized = unserialize(serialize($this->resource)); + unserialize(serialize($this->resource)); $this->assertSame(realpath($this->file), $this->resource->getResource()); } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 6bf1c4a50e5ae..ed72c6dfae926 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -568,7 +568,7 @@ public function testFindAlternativeExceptionMessageMultiple() // Subnamespace + plural try { - $a = $application->find('foo3:'); + $application->find('foo3:'); $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); } catch (\Exception $e) { $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php index 6d803fc251efe..1428cf5e97a5f 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php @@ -104,7 +104,7 @@ public function testCannotSetInvalidIndicatorCharacters() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage('Must have at least 2 indicator value characters.'); - $bar = new ProgressIndicator($this->getOutputStream(), null, 100, ['1']); + new ProgressIndicator($this->getOutputStream(), null, 100, ['1']); } public function testCannotStartAlreadyStartedIndicator() diff --git a/src/Symfony/Component/Console/Tests/Output/ConsoleSectionOutputTest.php b/src/Symfony/Component/Console/Tests/Output/ConsoleSectionOutputTest.php index e8d9a8b49253d..e8c65f9b21649 100644 --- a/src/Symfony/Component/Console/Tests/Output/ConsoleSectionOutputTest.php +++ b/src/Symfony/Component/Console/Tests/Output/ConsoleSectionOutputTest.php @@ -118,8 +118,8 @@ public function testOverwriteMultipleLines() public function testAddingMultipleSections() { $sections = []; - $output1 = new ConsoleSectionOutput($this->stream, $sections, OutputInterface::VERBOSITY_NORMAL, true, new OutputFormatter()); - $output2 = new ConsoleSectionOutput($this->stream, $sections, OutputInterface::VERBOSITY_NORMAL, true, new OutputFormatter()); + new ConsoleSectionOutput($this->stream, $sections, OutputInterface::VERBOSITY_NORMAL, true, new OutputFormatter()); + new ConsoleSectionOutput($this->stream, $sections, OutputInterface::VERBOSITY_NORMAL, true, new OutputFormatter()); $this->assertCount(2, $sections); } diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 42e803b9db4ed..1926775fd7e1b 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -482,7 +482,7 @@ private function darwinRealpath($real) } if (isset($dirFiles[$file])) { - return $real .= $dirFiles[$file]; + return $real.$dirFiles[$file]; } $kFile = strtolower($file); @@ -501,7 +501,7 @@ private function darwinRealpath($real) self::$darwinCache[$kDir][1] = $dirFiles; } - return $real .= $dirFiles[$kFile]; + return $real.$dirFiles[$kFile]; } /** diff --git a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php b/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php index 994a5efca1545..5498d9d4ab126 100644 --- a/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php +++ b/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php @@ -294,7 +294,7 @@ function () {}, // assertEquals() does not like NAN values. $this->assertEquals($array[$i][0], 'float'); - $this->assertNan($array[$i++][1]); + $this->assertNan($array[$i][1]); } public function testRecursionInArguments() diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 64dfee8573763..48267b0d033df 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -256,7 +256,7 @@ public function dump(array $options = []) foreach ($ids as $id) { $c .= ' '.$this->doExport($id)." => true,\n"; } - $files['removed-ids.php'] = $c .= "];\n"; + $files['removed-ids.php'] = $c."];\n"; } foreach ($this->generateServiceFiles($services) as $file => $c) { @@ -1889,7 +1889,7 @@ private function isSingleUsePrivateNode(ServiceReferenceGraphNode $node): bool if (!$value = $edge->getSourceNode()->getValue()) { continue; } - if ($edge->isLazy() || !$value->isShared()) { + if ($edge->isLazy() || !$value instanceof Definition || !$value->isShared()) { return false; } $ids[$edge->getSourceNode()->getId()] = true; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php index 48c3df595c3dc..a075b51d41ae2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php @@ -24,28 +24,28 @@ public function testProcess() { $container = new ContainerBuilder(); - $a = $container + $container ->register('a') ->addArgument($ref1 = new Reference('b')) ; - $b = $container + $container ->register('b') ->addMethodCall('setA', [$ref2 = new Reference('a')]) ; - $c = $container + $container ->register('c') ->addArgument($ref3 = new Reference('a')) ->addArgument($ref4 = new Reference('b')) ; - $d = $container + $container ->register('d') ->setProperty('foo', $ref5 = new Reference('b')) ; - $e = $container + $container ->register('e') ->setConfigurator([$ref6 = new Reference('b'), 'methodName']) ; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 83d3e23d3ba5a..fc300fde21c11 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -50,7 +50,7 @@ public function testProcessRemovesAndInlinesRecursively() ->setPublic(true) ; - $b = $container + $container ->register('b', '\stdClass') ->addArgument(new Reference('c')) ->setPublic(false) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php index 1dc2046453f4d..dfe37445d4e88 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php @@ -363,7 +363,7 @@ public function testBindings() ->setBindings(['a' => '1', 'b' => '2']) ; - $child = $container->setDefinition('child', new ChildDefinition('parent')) + $container->setDefinition('child', new ChildDefinition('parent')) ->setBindings(['b' => 'B', 'c' => 'C']) ; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php index 0ab6303164688..81e05fb284bf2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php @@ -87,8 +87,8 @@ public function testAmbiguousChildDefinition() $this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException'); $this->expectExceptionMessage('Service definition "App\Foo\Child" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.'); $container = new ContainerBuilder(); - $parent = $container->register('App\Foo', null); - $child = $container->setDefinition('App\Foo\Child', new ChildDefinition('App\Foo')); + $container->register('App\Foo', null); + $container->setDefinition('App\Foo\Child', new ChildDefinition('App\Foo')); (new ResolveClassPass())->process($container); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 4113c6941402c..fb3b2e99628d2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1272,7 +1272,7 @@ public function testNoClassFromGlobalNamespaceClassId() $this->expectExceptionMessage('The definition for "DateTime" has no class attribute, and appears to reference a class or interface in the global namespace.'); $container = new ContainerBuilder(); - $definition = $container->register(\DateTime::class); + $container->register(\DateTime::class); $container->compile(); } @@ -1302,7 +1302,7 @@ public function testNoClassFromNonClassId() $this->expectExceptionMessage('The definition for "123_abc" has no class.'); $container = new ContainerBuilder(); - $definition = $container->register('123_abc'); + $container->register('123_abc'); $container->compile(); } @@ -1312,7 +1312,7 @@ public function testNoClassFromNsSeparatorId() $this->expectExceptionMessage('The definition for "\foo" has no class.'); $container = new ContainerBuilder(); - $definition = $container->register('\\foo'); + $container->register('\\foo'); $container->compile(); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 20dada9b0b121..b78fcb32e4363 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -1036,7 +1036,7 @@ public function testInlineSelfRef() ->setPublic(true) ->addArgument($baz); - $passConfig = $container->getCompiler()->getPassConfig(); + $container->getCompiler()->getPassConfig(); $container->compile(); $dumper = new PhpDumper($container); @@ -1128,7 +1128,6 @@ public function testAdawsonContainer() $container->compile(); $dumper = new PhpDumper($container); - $dump = $dumper->dump(); $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_adawson.php', $dumper->dump()); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 7b8800f195df1..b520a8cc58b03 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -882,7 +882,6 @@ public function testTsantosContainer() $container->compile(); $dumper = new PhpDumper($container); - $dump = $dumper->dump(); $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_tsantos.php', $dumper->dump()); } diff --git a/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php b/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php index 61e776736a4b5..176ea5927fe1c 100644 --- a/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php @@ -19,7 +19,7 @@ public function testInitialize() { $node = $this->createNode('textarea', ''); try { - $field = new ChoiceFormField($node); + new ChoiceFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not an input or a select'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input or a select'); @@ -27,7 +27,7 @@ public function testInitialize() $node = $this->createNode('input', '', ['type' => 'text']); try { - $field = new ChoiceFormField($node); + new ChoiceFormField($node); $this->fail('->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio'); diff --git a/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php b/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php index 9b359d69785b3..b14bcc855e2ab 100644 --- a/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php @@ -24,7 +24,7 @@ public function testInitialize() $node = $this->createNode('textarea', ''); try { - $field = new FileFormField($node); + new FileFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not an input field'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input field'); @@ -32,7 +32,7 @@ public function testInitialize() $node = $this->createNode('input', '', ['type' => 'text']); try { - $field = new FileFormField($node); + new FileFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not a file input field'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a file input field'); diff --git a/src/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php b/src/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php index 5758f1b7bb3fe..a1f327bbc2f81 100644 --- a/src/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php @@ -24,7 +24,7 @@ public function testInitialize() $node = $this->createNode('textarea', ''); try { - $field = new InputFormField($node); + new InputFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not an input'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input'); @@ -32,7 +32,7 @@ public function testInitialize() $node = $this->createNode('input', '', ['type' => 'checkbox']); try { - $field = new InputFormField($node); + new InputFormField($node); $this->fail('->initialize() throws a \LogicException if the node is a checkbox'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is a checkbox'); @@ -40,7 +40,7 @@ public function testInitialize() $node = $this->createNode('input', '', ['type' => 'file']); try { - $field = new InputFormField($node); + new InputFormField($node); $this->fail('->initialize() throws a \LogicException if the node is a file'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is a file'); diff --git a/src/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php b/src/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php index 5d4d0038260db..192984ce29c2f 100644 --- a/src/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php @@ -24,7 +24,7 @@ public function testInitialize() $node = $this->createNode('input', ''); try { - $field = new TextareaFormField($node); + new TextareaFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not a textarea'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a textarea'); diff --git a/src/Symfony/Component/DomCrawler/Tests/FormTest.php b/src/Symfony/Component/DomCrawler/Tests/FormTest.php index 97ae46fe027f7..6fe44de5344f6 100644 --- a/src/Symfony/Component/DomCrawler/Tests/FormTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/FormTest.php @@ -39,14 +39,14 @@ public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor() $nodes = $dom->getElementsByTagName('input'); try { - $form = new Form($nodes->item(0), 'http://example.com'); + new Form($nodes->item(0), 'http://example.com'); $this->fail('__construct() throws a \\LogicException if the node has no form ancestor'); } catch (\LogicException $e) { $this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor'); } try { - $form = new Form($nodes->item(1), 'http://example.com'); + new Form($nodes->item(1), 'http://example.com'); $this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image'); } catch (\LogicException $e) { $this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image'); @@ -55,7 +55,7 @@ public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor() $nodes = $dom->getElementsByTagName('button'); try { - $form = new Form($nodes->item(0), 'http://example.com'); + new Form($nodes->item(0), 'http://example.com'); $this->fail('__construct() throws a \\LogicException if the node has no form ancestor'); } catch (\LogicException $e) { $this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor'); @@ -63,11 +63,19 @@ public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor() } /** - * __construct() should throw \\LogicException if the form attribute is invalid. + * @dataProvider constructorThrowsExceptionIfNoRelatedFormProvider + * + * __construct() should throw a \LogicException if the form attribute is invalid. */ - public function testConstructorThrowsExceptionIfNoRelatedForm() + public function testConstructorThrowsExceptionIfNoRelatedForm(\DOMElement $node) { $this->expectException('LogicException'); + + new Form($node, 'http://example.com'); + } + + public function constructorThrowsExceptionIfNoRelatedFormProvider() + { $dom = new \DOMDocument(); $dom->loadHTML(' @@ -81,8 +89,10 @@ public function testConstructorThrowsExceptionIfNoRelatedForm() $nodes = $dom->getElementsByTagName('input'); - $form = new Form($nodes->item(0), 'http://example.com'); - $form = new Form($nodes->item(1), 'http://example.com'); + return [ + [$nodes->item(0)], + [$nodes->item(1)], + ]; } public function testConstructorLoadsOnlyFieldsOfTheRightForm() diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index 66a2c105a8832..5c41d81d96554 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -257,29 +257,24 @@ private function lexValue() throw $this->createFormatException('Whitespace are not supported before the value'); } + $loadedVars = array_flip(explode(',', isset($_SERVER['SYMFONY_DOTENV_VARS']) ? $_SERVER['SYMFONY_DOTENV_VARS'] : (isset($_ENV['SYMFONY_DOTENV_VARS']) ? $_ENV['SYMFONY_DOTENV_VARS'] : ''))); + unset($loadedVars['']); $v = ''; do { if ("'" === $this->data[$this->cursor]) { - $value = ''; - ++$this->cursor; + $len = 0; - while ("\n" !== $this->data[$this->cursor]) { - if ("'" === $this->data[$this->cursor]) { - break; - } - $value .= $this->data[$this->cursor]; - ++$this->cursor; + do { + if ($this->cursor + ++$len === $this->end) { + $this->cursor += $len; - if ($this->cursor === $this->end) { throw $this->createFormatException('Missing quote to end the value'); } - } - if ("\n" === $this->data[$this->cursor]) { - throw $this->createFormatException('Missing quote to end the value'); - } - ++$this->cursor; - $v .= $value; + } while ("'" !== $this->data[$this->cursor + $len]); + + $v .= substr($this->data, 1 + $this->cursor, $len - 1); + $this->cursor += 1 + $len; } elseif ('"' === $this->data[$this->cursor]) { $value = ''; ++$this->cursor; @@ -295,8 +290,8 @@ private function lexValue() ++$this->cursor; $value = str_replace(['\\"', '\r', '\n'], ['"', "\r", "\n"], $value); $resolvedValue = $value; - $resolvedValue = $this->resolveVariables($resolvedValue); - $resolvedValue = $this->resolveCommands($resolvedValue); + $resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars); + $resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars); $resolvedValue = str_replace('\\\\', '\\', $resolvedValue); $v .= $resolvedValue; } else { @@ -318,8 +313,8 @@ private function lexValue() } $value = rtrim($value); $resolvedValue = $value; - $resolvedValue = $this->resolveVariables($resolvedValue); - $resolvedValue = $this->resolveCommands($resolvedValue); + $resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars); + $resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars); $resolvedValue = str_replace('\\\\', '\\', $resolvedValue); if ($resolvedValue === $value && preg_match('/\s+/', $value)) { @@ -372,7 +367,7 @@ private function skipEmptyLines() } } - private function resolveCommands($value) + private function resolveCommands($value, $loadedVars) { if (false === strpos($value, '$')) { return $value; @@ -388,7 +383,7 @@ private function resolveCommands($value) ) /x'; - return preg_replace_callback($regex, function ($matches) { + return preg_replace_callback($regex, function ($matches) use ($loadedVars) { if ('\\' === $matches[1]) { return substr($matches[0], 1); } @@ -403,7 +398,15 @@ private function resolveCommands($value) $process = method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline('echo '.$matches[0]) : new Process('echo '.$matches[0]); $process->inheritEnvironmentVariables(true); - $process->setEnv($this->values); + + $env = []; + foreach ($this->values as $name => $value) { + if (isset($loadedVars[$name]) || (!isset($_ENV[$name]) && !(isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')))) { + $env[$name] = $value; + } + } + $process->setEnv($env); + try { $process->mustRun(); } catch (ProcessException $e) { @@ -414,7 +417,7 @@ private function resolveCommands($value) }, $value); } - private function resolveVariables($value) + private function resolveVariables($value, array $loadedVars) { if (false === strpos($value, '$')) { return $value; @@ -430,7 +433,7 @@ private function resolveVariables($value) (?P\})? # optional closing brace /x'; - $value = preg_replace_callback($regex, function ($matches) { + $value = preg_replace_callback($regex, function ($matches) use ($loadedVars) { // odd number of backslashes means the $ character is escaped if (1 === \strlen($matches['backslashes']) % 2) { return substr($matches[0], 1); @@ -446,14 +449,16 @@ private function resolveVariables($value) } $name = $matches['name']; - if (isset($this->values[$name])) { + if (isset($loadedVars[$name]) && isset($this->values[$name])) { $value = $this->values[$name]; - } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { - $value = $_SERVER[$name]; } elseif (isset($_ENV[$name])) { $value = $_ENV[$name]; + } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { + $value = $_SERVER[$name]; + } elseif (isset($this->values[$name])) { + $value = $this->values[$name]; } else { - $value = (string) getenv($name); + $value = ''; } if (!$matches['opening_brace'] && isset($matches['closing_brace'])) { diff --git a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php index beda74838d594..e2b387ebc4ecd 100644 --- a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php +++ b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php @@ -40,7 +40,7 @@ public function getEnvDataWithFormatErrors() ['FOO', "Missing = in the environment variable declaration in \".env\" at line 1.\n...FOO...\n ^ line 1 offset 3"], ['FOO="foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo...\n ^ line 1 offset 8"], ['FOO=\'foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo...\n ^ line 1 offset 8"], - ['FOO=\'foo'."\n", "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo\\n...\n ^ line 1 offset 8"], + ['FOO=\'foo'."\n", "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo\\n...\n ^ line 1 offset 9"], ['export FOO', "Unable to unset an environment variable in \".env\" at line 1.\n...export FOO...\n ^ line 1 offset 10"], ['FOO=${FOO', "Unclosed braces on variable expansion in \".env\" at line 1.\n...FOO=\${FOO...\n ^ line 1 offset 9"], ['FOO= BAR', "Whitespace are not supported before the value in \".env\" at line 1.\n...FOO= BAR...\n ^ line 1 offset 4"], @@ -69,6 +69,7 @@ public function testParse($data, $expected) public function getEnvData() { putenv('LOCAL=local'); + $_ENV['LOCAL'] = 'local'; $_ENV['REMOTE'] = 'remote'; $_SERVER['SERVERVAR'] = 'servervar'; @@ -111,6 +112,7 @@ public function getEnvData() ['FOO="bar\rfoo"', ['FOO' => "bar\rfoo"]], ['FOO=\'bar\nfoo\'', ['FOO' => 'bar\nfoo']], ['FOO=\'bar\rfoo\'', ['FOO' => 'bar\rfoo']], + ["FOO='bar\nfoo'", ['FOO' => "bar\nfoo"]], ['FOO=" FOO "', ['FOO' => ' FOO ']], ['FOO=" "', ['FOO' => ' ']], ['PATH="c:\\\\"', ['PATH' => 'c:\\']], @@ -413,6 +415,22 @@ public function testOverridingEnvVarsWithNamesMemorizedInSpecialVar() $this->assertSame('/var/www', getenv('DOCUMENT_ROOT')); } + public function testGetVariablesValueFromEnvFirst() + { + $_ENV['APP_ENV'] = 'prod'; + $dotenv = new Dotenv(true); + + $test = "APP_ENV=dev\nTEST1=foo1_\${APP_ENV}"; + $values = $dotenv->parse($test); + $this->assertSame('foo1_prod', $values['TEST1']); + + if ('\\' !== \DIRECTORY_SEPARATOR) { + $test = "APP_ENV=dev\nTEST2=foo2_\$(php -r 'echo \$_SERVER[\"APP_ENV\"];')"; + $values = $dotenv->parse($test); + $this->assertSame('foo2_prod', $values['TEST2']); + } + } + /** * @group legacy * @expectedDeprecation The default value of "$usePutenv" argument of "%s" will be changed from "true" to "false" in Symfony 5.0. You should define its value explicitly. diff --git a/src/Symfony/Component/Dotenv/composer.json b/src/Symfony/Component/Dotenv/composer.json index 872ff3a4c5be8..dddeab1fb7e7f 100644 --- a/src/Symfony/Component/Dotenv/composer.json +++ b/src/Symfony/Component/Dotenv/composer.json @@ -19,7 +19,7 @@ "php": "^7.1.3" }, "require-dev": { - "symfony/process": "~3.4|~4.0" + "symfony/process": "^3.4.2|^4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Dotenv\\": "" }, diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index 92c2483790393..6e01b250caca5 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -158,7 +158,7 @@ public function testCachingWithDifferentNamesOrder() $cacheMock = $this->getMockBuilder('Psr\Cache\CacheItemPoolInterface')->getMock(); $cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock(); $expressionLanguage = new ExpressionLanguage($cacheMock); - $savedParsedExpressions = []; + $savedParsedExpression = null; $cacheMock ->expects($this->exactly(2)) diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index 3a0c3105ad0a9..b3931e0d81813 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -595,7 +595,8 @@ public function in($dirs) foreach ((array) $dirs as $dir) { if (is_dir($dir)) { $resolvedDirs[] = $this->normalizeDir($dir); - } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) { + } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR | GLOB_NOSORT)) { + sort($glob); $resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob)); } else { throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir)); diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php index b0737393e4e3f..167ea57faebbf 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php @@ -40,7 +40,11 @@ public function transform($value) throw new TransformationFailedException('Expected a \DateTimeImmutable.'); } - return \DateTime::createFromFormat(\DateTime::RFC3339, $value->format(\DateTime::RFC3339)); + if (\PHP_VERSION_ID >= 70300) { + return \DateTime::createFromImmutable($value); + } + + return \DateTime::createFromFormat('U.u', $value->format('U.u'))->setTimezone($value->getTimezone()); } /** diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index df7726a83f019..c2492fb662566 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -131,7 +131,8 @@ public function collectDefaultData(FormInterface $form) $hash = spl_object_hash($form); if (!isset($this->dataByForm[$hash])) { - $this->dataByForm[$hash] = []; + // field was created by form event + $this->collectConfiguration($form); } $this->dataByForm[$hash] = array_replace( diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php index 9313e4f1785cf..c8b6549778cce 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php @@ -16,16 +16,33 @@ class DateTimeImmutableToDateTimeTransformerTest extends TestCase { - public function testTransform() + /** + * @dataProvider provider + */ + public function testTransform(\DateTime $expectedOutput, \DateTimeImmutable $input) { $transformer = new DateTimeImmutableToDateTimeTransformer(); - $input = new \DateTimeImmutable('2010-02-03 04:05:06 UTC'); - $expectedOutput = new \DateTime('2010-02-03 04:05:06 UTC'); $actualOutput = $transformer->transform($input); - $this->assertInstanceOf(\DateTime::class, $actualOutput); $this->assertEquals($expectedOutput, $actualOutput); + $this->assertEquals($expectedOutput->getTimezone(), $actualOutput->getTimezone()); + } + + public function provider() + { + return [ + [ + new \DateTime('2010-02-03 04:05:06 UTC'), + new \DateTimeImmutable('2010-02-03 04:05:06 UTC'), + ], + [ + (new \DateTime('2019-10-07 +11:00')) + ->setTime(14, 27, 11, 10042), + (new \DateTimeImmutable('2019-10-07 +11:00')) + ->setTime(14, 27, 11, 10042), + ], + ]; } public function testTransformEmpty() @@ -43,16 +60,17 @@ public function testTransformFail() $transformer->transform(new \DateTime()); } - public function testReverseTransform() + /** + * @dataProvider provider + */ + public function testReverseTransform(\DateTime $input, \DateTimeImmutable $expectedOutput) { $transformer = new DateTimeImmutableToDateTimeTransformer(); - $input = new \DateTime('2010-02-03 04:05:06 UTC'); - $expectedOutput = new \DateTimeImmutable('2010-02-03 04:05:06 UTC'); $actualOutput = $transformer->reverseTransform($input); - $this->assertInstanceOf(\DateTimeImmutable::class, $actualOutput); $this->assertEquals($expectedOutput, $actualOutput); + $this->assertEquals($expectedOutput->getTimezone(), $actualOutput->getTimezone()); } public function testReverseTransformEmpty() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index 309a5dc4e3f46..808571cbef41e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -181,10 +181,10 @@ public function testTransformRequiresValidDateTime() public function testTransformWrapsIntlErrors() { - $transformer = new DateTimeToLocalizedStringTransformer(); - $this->markTestIncomplete('Checking for intl errors needs to be reimplemented'); + $transformer = new DateTimeToLocalizedStringTransformer(); + // HOW TO REPRODUCE? //$this->expectException('Symfony\Component\Form\Extension\Core\DataTransformer\TransformationFailedException'); diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php index 82eec8a45cf24..768002de46213 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -13,10 +13,20 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\Type\CollectionType; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\DataCollector\FormDataCollector; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormRegistry; use Symfony\Component\Form\FormView; +use Symfony\Component\Form\ResolvedFormTypeFactory; class FormDataCollectorTest extends TestCase { @@ -69,9 +79,9 @@ protected function setUp(): void { $this->dataExtractor = $this->getMockBuilder('Symfony\Component\Form\Extension\DataCollector\FormDataExtractorInterface')->getMock(); $this->dataCollector = new FormDataCollector($this->dataExtractor); - $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); - $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); - $this->dataMapper = $this->getMockBuilder('Symfony\Component\Form\DataMapperInterface')->getMock(); + $this->dispatcher = new EventDispatcher(); + $this->factory = new FormFactory(new FormRegistry([new CoreExtension()], new ResolvedFormTypeFactory())); + $this->dataMapper = new PropertyPathMapper(); $this->form = $this->createForm('name'); $this->childForm = $this->createForm('child'); $this->view = new FormView(); @@ -726,6 +736,56 @@ public function testReset() ); } + public function testCollectMissingDataFromChildFormAddedOnFormEvents() + { + $form = $this->factory->createNamedBuilder('root', FormType::class, ['items' => null]) + ->add('items', CollectionType::class, [ + 'entry_type' => TextType::class, + 'allow_add' => true, + // data is locked and modelData (null) is different to the + // configured data, so modifications of the configured data + // won't be allowed at this point. It also means *_SET_DATA + // events won't dispatched either. Therefore, no child form + // is created during the mapping of data to the form. + 'data' => ['foo'], + ]) + ->getForm() + ; + $this->dataExtractor->expects($extractConfiguration = $this->exactly(4)) + ->method('extractConfiguration') + ->willReturn([]) + ; + $this->dataExtractor->expects($extractDefaultData = $this->exactly(4)) + ->method('extractDefaultData') + ->willReturnCallback(static function (FormInterface $form) { + // this simulate the call in extractDefaultData() method + // where (if defaultDataSet is false) it fires *_SET_DATA + // events, adding the form related to the configured data + $form->getNormData(); + + return []; + }) + ; + $this->dataExtractor->expects($this->exactly(4)) + ->method('extractSubmittedData') + ->willReturn([]) + ; + + $this->dataCollector->collectConfiguration($form); + $this->assertSame(2, $extractConfiguration->getInvocationCount(), 'only "root" and "items" forms were collected, the "items" children do not exist yet.'); + + $this->dataCollector->collectDefaultData($form); + $this->assertSame(3, $extractConfiguration->getInvocationCount(), 'extracted missing configuration of the "items" children ["0" => foo].'); + $this->assertSame(3, $extractDefaultData->getInvocationCount()); + $this->assertSame(['foo'], $form->get('items')->getData()); + + $form->submit(['items' => ['foo', 'bar']]); + $this->dataCollector->collectSubmittedData($form); + $this->assertSame(4, $extractConfiguration->getInvocationCount(), 'extracted missing configuration of the "items" children ["1" => bar].'); + $this->assertSame(4, $extractDefaultData->getInvocationCount(), 'extracted missing default data of the "items" children ["1" => bar].'); + $this->assertSame(['foo', 'bar'], $form->get('items')->getData()); + } + private function createForm($name) { $builder = new FormBuilder($name, null, $this->dispatcher, $this->factory); diff --git a/src/Symfony/Component/Form/Tests/VersionAwareTest.php b/src/Symfony/Component/Form/Tests/VersionAwareTest.php index 2b8489a6a27cf..578bebcb0800f 100644 --- a/src/Symfony/Component/Form/Tests/VersionAwareTest.php +++ b/src/Symfony/Component/Form/Tests/VersionAwareTest.php @@ -13,7 +13,7 @@ trait VersionAwareTest { - protected static $supportedFeatureSetVersion = 304; + protected static $supportedFeatureSetVersion = 403; /** * @param int $requiredFeatureSetVersion diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 83413a23edf45..56a129783d481 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -197,6 +197,8 @@ public function request(string $method, string $url, array $options = []): Respo if ('POST' === $method) { // Use CURLOPT_POST to have browser-like POST-to-GET redirects for 301, 302 and 303 $curlopts[CURLOPT_POST] = true; + } elseif ('HEAD' === $method) { + $curlopts[CURLOPT_NOBODY] = true; } else { $curlopts[CURLOPT_CUSTOMREQUEST] = $method; } @@ -220,7 +222,7 @@ public function request(string $method, string $url, array $options = []): Respo // Prevent curl from sending its default Accept and Expect headers foreach (['accept', 'expect'] as $header) { - if (!isset($options['normalized_headers'][$header])) { + if (!isset($options['normalized_headers'][$header][0])) { $curlopts[CURLOPT_HTTPHEADER][] = $header.':'; } } @@ -413,7 +415,7 @@ private static function createRedirectResolver(array $options, string $host): \C return 0 !== stripos($h, 'Host:'); }); - if (isset($options['normalized_headers']['authorization']) || isset($options['normalized_headers']['cookie'])) { + if (isset($options['normalized_headers']['authorization'][0]) || isset($options['normalized_headers']['cookie'][0])) { $redirectHeaders['no_auth'] = array_filter($options['headers'], static function ($h) { return 0 !== stripos($h, 'Authorization:') && 0 !== stripos($h, 'Cookie:'); }); diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index 3fd9814df68f0..2914ab20b6022 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -57,7 +57,7 @@ private static function prepareRequest(?string $method, ?string $url, array $opt } if (!isset($options['normalized_headers']['accept'])) { - $options['normalized_headers']['accept'] = [$options['headers'][] = 'Accept: *']; + $options['normalized_headers']['accept'] = [$options['headers'][] = 'Accept: */*']; } if (isset($options['body'])) { @@ -196,7 +196,7 @@ private static function normalizeHeaders(array $headers): array $normalizedHeaders = []; foreach ($headers as $name => $values) { - if (\is_object($values) && method_exists('__toString')) { + if (\is_object($values) && method_exists($values, '__toString')) { $values = (string) $values; } diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index 532f4130860f3..6771dcb6f218a 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -142,7 +142,6 @@ public function __construct(CurlClientState $multi, $ch, array $options = null, // Schedule the request in a non-blocking way $multi->openHandles[$id] = [$ch, $options]; curl_multi_add_handle($multi->handle, $ch); - self::perform($multi); } /** @@ -274,6 +273,11 @@ private static function perform(CurlClientState $multi, array &$responses = null */ private static function select(CurlClientState $multi, float $timeout): int { + if (\PHP_VERSION_ID < 70123 || (70200 <= \PHP_VERSION_ID && \PHP_VERSION_ID < 70211)) { + // workaround https://bugs.php.net/76480 + $timeout = min($timeout, 0.01); + } + return curl_multi_select($multi->handle, $timeout); } @@ -318,6 +322,7 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & if (200 > $statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE)) { $multi->handlesActivity[$id][] = new InformationalChunk($statusCode, $headers); + $location = null; return \strlen($data); } @@ -341,9 +346,7 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & } } - $location = null; - - if ($statusCode < 300 || 400 <= $statusCode || curl_getinfo($ch, CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) { + if ($statusCode < 300 || 400 <= $statusCode || null === $location || curl_getinfo($ch, CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) { // Headers and redirects completed, time to get the response's body $multi->handlesActivity[$id][] = new FirstChunk(); @@ -356,6 +359,8 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & $logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url'])); } + $location = null; + return \strlen($data); } } diff --git a/src/Symfony/Component/HttpClient/Response/MockResponse.php b/src/Symfony/Component/HttpClient/Response/MockResponse.php index fa8abebea447b..3856112a09d39 100644 --- a/src/Symfony/Component/HttpClient/Response/MockResponse.php +++ b/src/Symfony/Component/HttpClient/Response/MockResponse.php @@ -176,7 +176,7 @@ protected static function perform(ClientState $multi, array &$responses): void try { $offset = 0; $chunk[1]->getStatusCode(); - $response->headers = $chunk[1]->getHeaders(false); + $chunk[1]->getHeaders(false); self::readResponse($response, $chunk[0], $chunk[1], $offset); $multi->handlesActivity[$id][] = new FirstChunk(); } catch (\Throwable $e) { @@ -257,7 +257,7 @@ private static function readResponse(self $response, array $options, ResponseInt $info = $mock->getInfo() ?: []; $response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode() ?: 200; $response->addResponseHeaders($info['response_headers'] ?? [], $response->info, $response->headers); - $dlSize = isset($response->headers['content-encoding']) ? 0 : (int) ($response->headers['content-length'][0] ?? 0); + $dlSize = isset($response->headers['content-encoding']) || 'HEAD' === $response->info['http_method'] || \in_array($response->info['http_code'], [204, 304], true) ? 0 : (int) ($response->headers['content-length'][0] ?? 0); $response->info = [ 'start_time' => $response->info['start_time'], diff --git a/src/Symfony/Component/HttpClient/Response/NativeResponse.php b/src/Symfony/Component/HttpClient/Response/NativeResponse.php index 2f414e3ba2d17..a9865ed09e494 100644 --- a/src/Symfony/Component/HttpClient/Response/NativeResponse.php +++ b/src/Symfony/Component/HttpClient/Response/NativeResponse.php @@ -174,8 +174,16 @@ private function open(): void $this->inflate = null; } - $this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->inflate, $this->content, $this->onProgress, &$this->remaining, &$this->info]; $this->multi->handlesActivity[$this->id] = [new FirstChunk()]; + + if ('HEAD' === $context['http']['method'] || \in_array($this->info['http_code'], [204, 304], true)) { + $this->multi->handlesActivity[$this->id][] = null; + $this->multi->handlesActivity[$this->id][] = null; + + return; + } + + $this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->inflate, $this->content, $this->onProgress, &$this->remaining, &$this->info]; } /** diff --git a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php index 8a1d9a9077ce1..7e0e06b8dcebc 100644 --- a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php +++ b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php @@ -104,7 +104,6 @@ public function getContent(bool $throw = true): string if (null === $this->content) { $content = null; - $chunk = null; foreach (self::stream([$this]) as $chunk) { if (!$chunk->isLast()) { @@ -112,11 +111,15 @@ public function getContent(bool $throw = true): string } } - if (null === $content) { - throw new TransportException('Cannot get the content of the response twice: the request was issued with option "buffer" set to false.'); + if (null !== $content) { + return $content; } - return $content; + if ('HEAD' === $this->info['http_method'] || \in_array($this->info['http_code'], [204, 304], true)) { + return ''; + } + + throw new TransportException('Cannot get the content of the response twice: the request was issued with option "buffer" set to false.'); } foreach (self::stream([$this]) as $chunk) { diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index 3d5a70a0a96e5..2acf01fd0a642 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -15,6 +15,59 @@ abstract class HttpClientTestCase extends BaseHttpClientTestCase { + public function testAcceptHeader() + { + $client = $this->getHttpClient(__FUNCTION__); + + $response = $client->request('GET', 'http://localhost:8057'); + $requestHeaders = $response->toArray(); + + $this->assertSame('*/*', $requestHeaders['HTTP_ACCEPT']); + + $response = $client->request('GET', 'http://localhost:8057', [ + 'headers' => [ + 'Accept' => 'foo/bar', + ], + ]); + $requestHeaders = $response->toArray(); + + $this->assertSame('foo/bar', $requestHeaders['HTTP_ACCEPT']); + + $response = $client->request('GET', 'http://localhost:8057', [ + 'headers' => [ + 'Accept' => null, + ], + ]); + $requestHeaders = $response->toArray(); + + $this->assertArrayNotHasKey('HTTP_ACCEPT', $requestHeaders); + } + + public function testInfoOnCanceledResponse() + { + $this->markTestSkipped('Implemented as of version 4.4'); + } + + public function testBufferSink() + { + $this->markTestSkipped('Implemented as of version 4.4'); + } + + public function testConditionalBuffering() + { + $this->markTestSkipped('Implemented as of version 4.4'); + } + + public function testReentrantBufferCallback() + { + $this->markTestSkipped('Implemented as of version 4.4'); + } + + public function testThrowingBufferCallback() + { + $this->markTestSkipped('Implemented as of version 4.4'); + } + public function testMaxDuration() { $this->markTestSkipped('Implemented as of version 4.4'); diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php index 559f2b62270df..e1636a5d5f84d 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php @@ -172,7 +172,7 @@ public function provideRemoveDotSegments() public function testAuthBearerOption() { [, $options] = self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => 'foobar'], HttpClientInterface::OPTIONS_DEFAULTS); - $this->assertSame(['Accept: *', 'Authorization: Bearer foobar'], $options['headers']); + $this->assertSame(['Accept: */*', 'Authorization: Bearer foobar'], $options['headers']); $this->assertSame(['Authorization: Bearer foobar'], $options['normalized_headers']['authorization']); } diff --git a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php index 8a3936b442f7a..f8f2c3e18b1db 100644 --- a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php @@ -36,6 +36,7 @@ protected function getHttpClient(string $testCase): HttpClientInterface "SERVER_NAME": "127.0.0.1", "REQUEST_URI": "/", "REQUEST_METHOD": "GET", + "HTTP_ACCEPT": "*/*", "HTTP_FOO": "baR", "HTTP_HOST": "localhost:8057" }'; @@ -47,7 +48,7 @@ protected function getHttpClient(string $testCase): HttpClientInterface return new MockHttpClient(function (string $method, string $url, array $options) use ($client) { try { // force the request to be completed so that we don't test side effects of the transport - $response = $client->request($method, $url, $options); + $response = $client->request($method, $url, ['buffer' => false] + $options); $content = $response->getContent(false); return new MockResponse($content, $response->getInfo()); @@ -113,6 +114,12 @@ protected function getHttpClient(string $testCase): HttpClientInterface $responses[] = $mock; break; + case 'testAcceptHeader': + $responses[] = new MockResponse($body, ['response_headers' => $headers]); + $responses[] = new MockResponse(str_replace('*/*', 'foo/bar', $body), ['response_headers' => $headers]); + $responses[] = new MockResponse(str_replace('"HTTP_ACCEPT": "*/*",', '', $body), ['response_headers' => $headers]); + break; + case 'testResolve': $responses[] = new MockResponse($body, ['response_headers' => $headers]); $responses[] = new MockResponse($body, ['response_headers' => $headers]); diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php index 9633f0174ec69..7a6da17dc0358 100644 --- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php +++ b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -94,7 +94,7 @@ public function guess($path) $type = trim(ob_get_clean()); - if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) { + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) { // it's not a type, but an error message return null; } diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 9af2c28256fab..db82ffce815a6 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1447,14 +1447,11 @@ public function isMethod($method) * * @see https://tools.ietf.org/html/rfc7231#section-4.2.1 * - * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default. - * * @return bool */ - public function isMethodSafe(/* $andCacheable = true */) + public function isMethodSafe() { - if (!\func_num_args() || func_get_arg(0)) { - // setting $andCacheable to false should be deprecated in 4.1 + if (\func_num_args() > 0 && func_get_arg(0)) { throw new \BadMethodCallException('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is not supported.'); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php b/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php index b31bfcb5bb1be..17d319581f97f 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php @@ -152,7 +152,7 @@ public function testMoveLocalFileIsNotAllowed() UPLOAD_ERR_OK ); - $movedFile = $file->move(__DIR__.'/Fixtures/directory'); + $file->move(__DIR__.'/Fixtures/directory'); } public function failedUploadedFile() diff --git a/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php index dcc266f69c41a..cabe038bdf00b 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php @@ -59,7 +59,7 @@ public function testGetDateException() { $this->expectException('RuntimeException'); $bag = new HeaderBag(['foo' => 'Tue']); - $headerDate = $bag->getDate('foo'); + $bag->getDate('foo'); } public function testGetCacheControlHeader() diff --git a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php index 92f4876da4ff1..e1ff3bf2bdb98 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php @@ -29,13 +29,13 @@ public function testGenerateMetaRedirect() public function testRedirectResponseConstructorNullUrl() { $this->expectException('InvalidArgumentException'); - $response = new RedirectResponse(null); + new RedirectResponse(null); } public function testRedirectResponseConstructorWrongStatusCode() { $this->expectException('InvalidArgumentException'); - $response = new RedirectResponse('foo.bar', 404); + new RedirectResponse('foo.bar', 404); } public function testGenerateLocationHeader() diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index d56ef31476f59..03493b5327304 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -2132,7 +2132,7 @@ public function testMethodSafeChecksCacheable() $this->expectException('BadMethodCallException'); $request = new Request(); $request->setMethod('OPTIONS'); - $request->isMethodSafe(); + $request->isMethodSafe(true); } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php index cd167fb13ab0d..368af6a3e3e10 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php @@ -27,7 +27,7 @@ class NativeFileSessionHandlerTest extends TestCase { public function testConstruct() { - $storage = new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler(sys_get_temp_dir())); + new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler(sys_get_temp_dir())); $this->assertEquals('user', ini_get('session.save_handler')); @@ -40,7 +40,7 @@ public function testConstruct() */ public function testConstructSavePath($savePath, $expectedSavePath, $path) { - $handler = new NativeFileSessionHandler($savePath); + new NativeFileSessionHandler($savePath); $this->assertEquals($expectedSavePath, ini_get('session.save_path')); $this->assertDirectoryExists(realpath($path)); @@ -61,13 +61,13 @@ public function savePathDataProvider() public function testConstructException() { $this->expectException('InvalidArgumentException'); - $handler = new NativeFileSessionHandler('something;invalid;with;too-many-args'); + new NativeFileSessionHandler('something;invalid;with;too-many-args'); } public function testConstructDefault() { $path = ini_get('session.save_path'); - $storage = new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler()); + new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler()); $this->assertEquals($path, ini_get('session.save_path')); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php index 0d246e1aa560b..f793db144c6ac 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php @@ -28,7 +28,7 @@ class NullSessionHandlerTest extends TestCase { public function testSaveHandlers() { - $storage = $this->getStorage(); + $this->getStorage(); $this->assertEquals('user', ini_get('session.save_handler')); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index d080ce3ca6e5c..5aee54bab160e 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -54,7 +54,7 @@ public function testWrongPdoErrMode() $pdo = $this->getMemorySqlitePdo(); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); - $storage = new PdoSessionHandler($pdo); + new PdoSessionHandler($pdo); } public function testInexistentTable() diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php index 17f46bef5e1a1..4efc8d2e9aa97 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php @@ -145,7 +145,7 @@ public function testDefaultSessionCacheLimiter() { $this->iniSet('session.cache_limiter', 'nocache'); - $storage = new NativeSessionStorage(); + new NativeSessionStorage(); $this->assertEquals('', ini_get('session.cache_limiter')); } @@ -153,7 +153,7 @@ public function testExplicitSessionCacheLimiter() { $this->iniSet('session.cache_limiter', 'nocache'); - $storage = new NativeSessionStorage(['cache_limiter' => 'public']); + new NativeSessionStorage(['cache_limiter' => 'public']); $this->assertEquals('public', ini_get('session.cache_limiter')); } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 32624c963fa07..144ef46742a04 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -49,7 +49,6 @@ public function collect(Request $request, Response $response, \Exception $except } } - $content = null; try { $content = $request->getContent(); } catch (\LogicException $e) { @@ -59,7 +58,6 @@ public function collect(Request $request, Response $response, \Exception $except $sessionMetadata = []; $sessionAttributes = []; - $session = null; $flashes = []; if ($request->hasSession()) { $session = $request->getSession(); @@ -80,9 +78,9 @@ public function collect(Request $request, Response $response, \Exception $except } $dotenvVars = []; - foreach (explode(',', getenv('SYMFONY_DOTENV_VARS')) as $name) { - if ('' !== $name && false !== $value = getenv($name)) { - $dotenvVars[$name] = $value; + foreach (explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? '') as $name) { + if ('' !== $name && isset($_ENV[$name])) { + $dotenvVars[$name] = $_ENV[$name]; } } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php index 25c071c335a02..3bdf0f5199891 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php @@ -130,7 +130,6 @@ public function update(Response $response) $response->headers->set('Cache-Control', implode(', ', array_keys($flags))); $maxAge = null; - $sMaxage = null; if (is_numeric($this->ageDirectives['max-age'])) { $maxAge = $this->ageDirectives['max-age'] + $this->age; diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 7d18d221d5c18..d91b4f64d9ce6 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 $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.3.5'; - const VERSION_ID = 40305; + const VERSION = '4.3.6'; + const VERSION_ID = 40306; const MAJOR_VERSION = 4; const MINOR_VERSION = 3; - const RELEASE_VERSION = 5; + const RELEASE_VERSION = 6; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2020'; @@ -754,7 +754,7 @@ protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container $fs->dumpFile($dir.$file, $code); @chmod($dir.$file, 0666 & ~umask()); } - $legacyFile = \dirname($dir.$file).'.legacy'; + $legacyFile = \dirname($dir.key($content)).'.legacy'; if (file_exists($legacyFile)) { @unlink($legacyFile); } diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index 3c5b19783774f..624658f727cef 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -256,7 +256,7 @@ public function testArgumentWithNoTypeHintIsOk() public function testControllersAreMadePublic() { $container = new ContainerBuilder(); - $resolver = $container->register('argument_resolver.service')->addArgument([]); + $container->register('argument_resolver.service')->addArgument([]); $container->register('foo', ArgumentWithoutTypeController::class) ->setPublic(false) @@ -364,7 +364,7 @@ public function testBindingsOnChildDefinitions() public function testNotTaggedControllerServiceReceivesLocatorArgument() { $container = new ContainerBuilder(); - $resolver = $container->register('argument_resolver.not_tagged_controller')->addArgument([]); + $container->register('argument_resolver.not_tagged_controller')->addArgument([]); $pass = new RegisterControllerArgumentLocatorsPass(); $pass->process($container); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php index 2887c14f5d574..6e56dbe0bbd97 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php @@ -52,7 +52,7 @@ public function testReadsAnEmptyArrayWithReadWhenNothingCachedAtKey() public function testUnlockFileThatDoesExist() { - $cacheKey = $this->storeSimpleEntry(); + $this->storeSimpleEntry(); $this->store->lock($this->request); $this->assertTrue($this->store->unlock($this->request)); @@ -92,7 +92,7 @@ public function testSetsTheXContentDigestResponseHeaderBeforeStoring() { $cacheKey = $this->storeSimpleEntry(); $entries = $this->getStoreMetadata($cacheKey); - list($req, $res) = $entries[0]; + list(, $res) = $entries[0]; $this->assertEquals('en9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', $res['x-content-digest'][0]); } @@ -208,7 +208,7 @@ public function testOverwritesNonVaryingResponseWithStore() { $req1 = Request::create('/test', 'get', [], [], [], ['HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar']); $res1 = new Response('test 1', 200, ['Vary' => 'Foo Bar']); - $key = $this->store->write($req1, $res1); + $this->store->write($req1, $res1); $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 1')), $this->store->lookup($req1)->getContent()); $req2 = Request::create('/test', 'get', [], [], [], ['HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam']); @@ -229,7 +229,7 @@ public function testLocking() $req = Request::create('/test', 'get', [], [], [], ['HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar']); $this->assertTrue($this->store->lock($req)); - $path = $this->store->lock($req); + $this->store->lock($req); $this->assertTrue($this->store->isLocked($req)); $this->store->unlock($req); diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php index 9217eaa49f46b..c8e313e7b7391 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php @@ -17,6 +17,7 @@ use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\NamedAddress; use Symfony\Contracts\HttpClient\HttpClientInterface; /** @@ -60,11 +61,15 @@ private function getPayload(Email $email, SmtpEnvelope $envelope): array 'html' => $email->getHtmlBody(), 'text' => $email->getTextBody(), 'subject' => $email->getSubject(), - 'from_email' => $envelope->getSender()->toString(), + 'from_email' => $envelope->getSender()->getAddress(), 'to' => $this->getRecipients($email, $envelope), ], ]; + if ($envelope->getSender() instanceof NamedAddress) { + $payload['message']['from_name'] = $envelope->getSender()->getName(); + } + foreach ($email->getAttachments() as $attachment) { $headers = $attachment->getPreparedHeaders(); $disposition = $headers->getHeaderBody('Content-Disposition'); @@ -104,10 +109,16 @@ protected function getRecipients(Email $email, SmtpEnvelope $envelope): array $type = 'cc'; } - $recipients[] = [ - 'email' => $recipient->toString(), + $recipientPayload = [ + 'email' => $recipient->getAddress(), 'type' => $type, ]; + + if ($recipient instanceof NamedAddress) { + $recipientPayload['name'] = $recipient->getName(); + } + + $recipients[] = $recipientPayload; } return $recipients; diff --git a/src/Symfony/Component/Mailer/Tests/SmtpEnvelopeTest.php b/src/Symfony/Component/Mailer/Tests/SmtpEnvelopeTest.php index f963a5a0f4af4..edb8b97cabbf8 100644 --- a/src/Symfony/Component/Mailer/Tests/SmtpEnvelopeTest.php +++ b/src/Symfony/Component/Mailer/Tests/SmtpEnvelopeTest.php @@ -41,13 +41,13 @@ public function testConstructorWithAddressRecipients() public function testConstructorWithNoRecipients() { $this->expectException(\InvalidArgumentException::class); - $e = new SmtpEnvelope(new Address('fabien@symfony.com'), []); + new SmtpEnvelope(new Address('fabien@symfony.com'), []); } public function testConstructorWithWrongRecipients() { $this->expectException(\InvalidArgumentException::class); - $e = new SmtpEnvelope(new Address('fabien@symfony.com'), ['lucas@symfony.com']); + new SmtpEnvelope(new Address('fabien@symfony.com'), ['lucas@symfony.com']); } public function testSenderFromHeaders() diff --git a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php index 194d42c109ac4..291ed85aeba57 100644 --- a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php @@ -62,8 +62,7 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io) /** @var SentToFailureTransportStamp|null $sentToFailureTransportStamp */ $sentToFailureTransportStamp = $envelope->last(SentToFailureTransportStamp::class); - /** @var RedeliveryStamp|null $lastRedeliveryStamp */ - $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class); + $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope); $rows = [ ['Class', \get_class($envelope->getMessage())], @@ -73,13 +72,13 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io) $rows[] = ['Message Id', $id]; } - $flattenException = null === $lastRedeliveryStamp ? null : $lastRedeliveryStamp->getFlattenException(); + $flattenException = null === $lastRedeliveryStampWithException ? null : $lastRedeliveryStampWithException->getFlattenException(); if (null === $sentToFailureTransportStamp) { $io->warning('Message does not appear to have been sent to this transport after failing'); } else { $rows = array_merge($rows, [ - ['Failed at', null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')], - ['Error', null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getExceptionMessage()], + ['Failed at', null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getRedeliveredAt()->format('Y-m-d H:i:s')], + ['Error', null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getExceptionMessage()], ['Error Class', null === $flattenException ? '(unknown)' : $flattenException->getClass()], ['Transport', $sentToFailureTransportStamp->getOriginalReceiverName()], ]); @@ -121,4 +120,16 @@ protected function getReceiver(): ReceiverInterface { return $this->receiver; } + + protected function getLastRedeliveryStampWithException(Envelope $envelope): ?RedeliveryStamp + { + /** @var RedeliveryStamp $stamp */ + foreach (array_reverse($envelope->all(RedeliveryStamp::class)) as $stamp) { + if (null !== $stamp->getExceptionMessage()) { + return $stamp; + } + } + + return null; + } } diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php index 0444d79f447ff..b0f16b704e3d8 100644 --- a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php +++ b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php @@ -18,7 +18,6 @@ use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; /** @@ -83,14 +82,13 @@ private function listMessages(SymfonyStyle $io, int $max) $rows = []; foreach ($envelopes as $envelope) { - /** @var RedeliveryStamp|null $lastRedeliveryStamp */ - $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class); + $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope); $rows[] = [ $this->getMessageId($envelope), \get_class($envelope->getMessage()), - null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s'), - null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getExceptionMessage(), + null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getRedeliveredAt()->format('Y-m-d H:i:s'), + null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getExceptionMessage(), ]; } diff --git a/src/Symfony/Component/Messenger/Exception/RejectRedeliveredMessageException.php b/src/Symfony/Component/Messenger/Exception/RejectRedeliveredMessageException.php new file mode 100644 index 0000000000000..79b94fa2656f2 --- /dev/null +++ b/src/Symfony/Component/Messenger/Exception/RejectRedeliveredMessageException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Exception; + +/** + * @author Tobias Schultze + * + * @experimental in 4.3 + */ +class RejectRedeliveredMessageException extends RuntimeException +{ +} diff --git a/src/Symfony/Component/Messenger/Middleware/RejectRedeliveredMessageMiddleware.php b/src/Symfony/Component/Messenger/Middleware/RejectRedeliveredMessageMiddleware.php new file mode 100644 index 0000000000000..2c6e6b6ff718f --- /dev/null +++ b/src/Symfony/Component/Messenger/Middleware/RejectRedeliveredMessageMiddleware.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\Component\Messenger\Middleware; + +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Exception\RejectRedeliveredMessageException; +use Symfony\Component\Messenger\Stamp\ReceivedStamp; +use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceivedStamp; + +/** + * Middleware that throws a RejectRedeliveredMessageException when a message is detected that has been redelivered by AMQP. + * + * The middleware runs before the HandleMessageMiddleware and prevents redelivered messages from being handled directly. + * The thrown exception is caught by the worker and will trigger the retry logic according to the retry strategy. + * + * AMQP redelivers messages when they do not get acknowledged or rejected. This can happen when the connection times out + * or an exception is thrown before acknowledging or rejecting. When such errors happen again while handling the + * redelivered message, the message would get redelivered again and again. The purpose of this middleware is to prevent + * infinite redelivery loops and to unblock the queue by republishing the redelivered messages as retries with a retry + * limit and potential delay. + * + * @experimental in 4.3 + * + * @author Tobias Schultze + */ +class RejectRedeliveredMessageMiddleware implements MiddlewareInterface +{ + public function handle(Envelope $envelope, StackInterface $stack): Envelope + { + // ignore the dispatched messages for retry + if (null !== $envelope->last(ReceivedStamp::class)) { + $amqpReceivedStamp = $envelope->last(AmqpReceivedStamp::class); + + if ($amqpReceivedStamp instanceof AmqpReceivedStamp && $amqpReceivedStamp->getAmqpEnvelope()->isRedelivery()) { + throw new RejectRedeliveredMessageException('Redelivered message from AMQP detected that will be rejected and trigger the retry logic.'); + } + } + + return $stack->next()->handle($envelope, $stack); + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php index bc69a1c2e8a2e..bcc67f79d566b 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php @@ -13,15 +13,12 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Debug\Exception\FlattenException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Messenger\Command\FailedMessagesRetryCommand; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; -use Symfony\Component\Messenger\Retry\RetryStrategyInterface; -use Symfony\Component\Messenger\Stamp\ReceivedStamp; -use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; +use Symfony\Component\Messenger\Worker; class FailedMessagesRetryCommandTest extends TestCase { @@ -37,52 +34,16 @@ public function testBasicRun() // the bus should be called in the worker $bus->expects($this->exactly(2))->method('dispatch')->willReturn(new Envelope(new \stdClass())); - $command = new FailedMessagesRetryCommand('failure_receiver', $receiver, $bus, $dispatcher); + $command = new FailedMessagesRetryCommand( + 'failure_receiver', + $receiver, + $bus, + $dispatcher + ); $tester = new CommandTester($command); $tester->execute(['id' => [10, 12]]); $this->assertStringContainsString('[OK]', $tester->getDisplay()); } - - public function testExceptionOnRetry() - { - $receiver = $this->createMock(ListableReceiverInterface::class); - $receiver->expects($this->once())->method('find')->with(10)->willReturn(new Envelope(new \stdClass())); - // message will eventually be ack'ed in Worker - $receiver->expects($this->once())->method('ack'); - - $dispatcher = $this->createMock(EventDispatcherInterface::class); - $bus = $this->createMock(MessageBusInterface::class); - // the bus should be called in the worker - $bus->expects($this->at(0)) - ->method('dispatch') - ->with($this->callback(function (Envelope $envelope) { - $lastReceivedStamp = $envelope->last(ReceivedStamp::class); - - return $lastReceivedStamp instanceof ReceivedStamp && \is_string($lastReceivedStamp->getTransportName()); - })) - ->will($this->throwException(new \Exception('Mock test exception'))); - - $bus->expects($this->at(1)) - ->method('dispatch') - ->with($this->callback(function (Envelope $envelope) { - $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class); - - return $lastRedeliveryStamp instanceof RedeliveryStamp && - \is_string($lastRedeliveryStamp->getExceptionMessage()) && - $lastRedeliveryStamp->getFlattenException() instanceof FlattenException; - })) - ->willReturn(new Envelope(new \stdClass())); - - $retryStrategy = $this->createMock(RetryStrategyInterface::class); - $retryStrategy->expects($this->once())->method('isRetryable')->with($this->isInstanceOf(Envelope::class))->willReturn(true); - - $command = new FailedMessagesRetryCommand('failure_receiver', $receiver, $bus, $dispatcher, $retryStrategy); - - $tester = new CommandTester($command); - $tester->execute(['id' => [10]]); - - $this->assertStringContainsString('[OK]', $tester->getDisplay()); - } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php index bd77f3f14f8a8..f632d9890b343 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php @@ -58,4 +58,40 @@ public function testBasicRun() $redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')), $tester->getDisplay(true)); } + + public function testMultipleRedeliveryFails() + { + $sentToFailureStamp = new SentToFailureTransportStamp('async'); + $redeliveryStamp1 = new RedeliveryStamp(0, 'failure_receiver', 'Things are bad!'); + $redeliveryStamp2 = new RedeliveryStamp(0, 'failure_receiver'); + $envelope = new Envelope(new \stdClass(), [ + new TransportMessageIdStamp(15), + $sentToFailureStamp, + $redeliveryStamp1, + $redeliveryStamp2, + ]); + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->expects($this->once())->method('find')->with(15)->willReturn($envelope); + + $command = new FailedMessagesShowCommand( + 'failure_receiver', + $receiver + ); + + $tester = new CommandTester($command); + $tester->execute(['id' => 15]); + + $this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')), + $tester->getDisplay(true)); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php index 6e7724d506927..b7ac30bee1c8a 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/ConnectionTest.php @@ -44,8 +44,11 @@ public function testGetAMessageWillChangeItsStatus() $queryBuilder ->method('getParameters') ->willReturn([]); + $queryBuilder + ->method('getParameterTypes') + ->willReturn([]); $driverConnection - ->method('prepare') + ->method('executeQuery') ->willReturn($stmt); $connection = new Connection([], $driverConnection, $schemaSynchronizer); @@ -65,13 +68,17 @@ public function testGetWithNoPendingMessageWillReturnNull() $queryBuilder ->method('getParameters') ->willReturn([]); + $queryBuilder + ->method('getParameterTypes') + ->willReturn([]); $driverConnection->expects($this->once()) ->method('createQueryBuilder') ->willReturn($queryBuilder); - $driverConnection->method('prepare') - ->willReturn($stmt); $driverConnection->expects($this->never()) ->method('update'); + $driverConnection + ->method('executeQuery') + ->willReturn($stmt); $connection = new Connection([], $driverConnection, $schemaSynchronizer); $doctrineEnvelope = $connection->get(); @@ -273,7 +280,7 @@ public function testFind() ->method('getParameters') ->willReturn([]); $driverConnection - ->method('prepare') + ->method('executeQuery') ->willReturn($stmt); $connection = new Connection([], $driverConnection, $schemaSynchronizer); @@ -316,8 +323,11 @@ public function testFindAll() $queryBuilder ->method('getParameters') ->willReturn([]); + $queryBuilder + ->method('getParameterTypes') + ->willReturn([]); $driverConnection - ->method('prepare') + ->method('executeQuery') ->willReturn($stmt); $connection = new Connection([], $driverConnection, $schemaSynchronizer); diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/DoctrineIntegrationTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/DoctrineIntegrationTest.php index fa05a43b0b66b..a01e68db39e2a 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/DoctrineIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Doctrine/DoctrineIntegrationTest.php @@ -80,25 +80,25 @@ public function testItRetrieveTheFirstAvailableMessage() 'body' => '{"message": "Hi handled"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'delivered_at' => Connection::formatDateTime(new \DateTime()), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'delivered_at' => $this->formatDateTime(new \DateTime()), ]); // one available later $this->driverConnection->insert('messenger_messages', [ 'body' => '{"message": "Hi delayed"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 13:00:00')), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 13:00:00')), ]); // one available $this->driverConnection->insert('messenger_messages', [ 'body' => '{"message": "Hi available"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:30:00')), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:30:00')), ]); $encoded = $this->connection->get(); @@ -114,33 +114,33 @@ public function testItCountMessages() 'body' => '{"message": "Hi handled"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'delivered_at' => Connection::formatDateTime(new \DateTime()), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'delivered_at' => $this->formatDateTime(new \DateTime()), ]); // one available later $this->driverConnection->insert('messenger_messages', [ 'body' => '{"message": "Hi delayed"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime((new \DateTime())->modify('+1 minute')), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime((new \DateTime())->modify('+1 minute')), ]); // one available $this->driverConnection->insert('messenger_messages', [ 'body' => '{"message": "Hi available"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:30:00')), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:30:00')), ]); // another available $this->driverConnection->insert('messenger_messages', [ 'body' => '{"message": "Hi available"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:30:00')), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:30:00')), ]); $this->assertSame(2, $this->connection->getMessageCount()); @@ -155,16 +155,16 @@ public function testItRetrieveTheMessageThatIsOlderThanRedeliverTimeout() 'body' => '{"message": "Hi requeued"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'delivered_at' => Connection::formatDateTime($twoHoursAgo), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'delivered_at' => $this->formatDateTime($twoHoursAgo), ]); $this->driverConnection->insert('messenger_messages', [ 'body' => '{"message": "Hi available"}', 'headers' => json_encode(['type' => DummyMessage::class]), 'queue_name' => 'default', - 'created_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:00:00')), - 'available_at' => Connection::formatDateTime(new \DateTime('2019-03-15 12:30:00')), + 'created_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:00:00')), + 'available_at' => $this->formatDateTime(new \DateTime('2019-03-15 12:30:00')), ]); $next = $this->connection->get(); @@ -181,4 +181,9 @@ public function testTheTransportIsSetupOnGet() $envelope = $this->connection->get(); $this->assertEquals('the body', $envelope['body']); } + + private function formatDateTime(\DateTime $dateTime) + { + return $dateTime->format($this->driverConnection->getDatabasePlatform()->getDateTimeFormatString()); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php index 4e525702aa5f8..81baaac8d96a2 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php @@ -57,13 +57,8 @@ public function testFromDsn() public function testFromDsnWithOptions() { $this->assertEquals( - new Connection(['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false], [ - 'host' => 'localhost', - 'port' => 6379, - ], [ - 'serializer' => 2, - ]), - Connection::fromDsn('redis://localhost/queue/group1/consumer1', ['serializer' => 2, 'auto_setup' => false]) + Connection::fromDsn('redis://localhost', ['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false, 'serializer' => 2]), + Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&auto_setup=0') ); } @@ -99,7 +94,21 @@ public function testAuth() $redis = $this->getMockBuilder(\Redis::class)->disableOriginalConstructor()->getMock(); $redis->expects($this->exactly(1))->method('auth') - ->with('password'); + ->with('password') + ->willReturn(true); + + Connection::fromDsn('redis://password@localhost/queue', [], $redis); + } + + public function testFailedAuth() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Redis connection failed'); + $redis = $this->getMockBuilder(\Redis::class)->disableOriginalConstructor()->getMock(); + + $redis->expects($this->exactly(1))->method('auth') + ->with('password') + ->willReturn(false); Connection::fromDsn('redis://password@localhost/queue', [], $redis); } diff --git a/src/Symfony/Component/Messenger/Tests/WorkerTest.php b/src/Symfony/Component/Messenger/Tests/WorkerTest.php index ad7477253e86b..9a09c0a04a333 100644 --- a/src/Symfony/Component/Messenger/Tests/WorkerTest.php +++ b/src/Symfony/Component/Messenger/Tests/WorkerTest.php @@ -118,8 +118,8 @@ public function testDispatchCausesRetry() } }); - // old message acknowledged - $this->assertSame(1, $receiver->getAcknowledgeCount()); + // old message rejected + $this->assertSame(1, $receiver->getRejectCount()); } public function testUnrecoverableMessageHandlingExceptionPreventsRetries() diff --git a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php index 2c8bcd35cc459..f665e8b31c492 100644 --- a/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php @@ -124,8 +124,14 @@ public function send(string $body, array $headers, int $delay = 0): string $body, json_encode($headers), $this->configuration['queue_name'], - self::formatDateTime($now), - self::formatDateTime($availableAt), + $now, + $availableAt, + ], [ + null, + null, + null, + Type::DATETIME, + Type::DATETIME, ]); return $this->driverConnection->lastInsertId(); @@ -143,7 +149,8 @@ public function get(): ?array // use SELECT ... FOR UPDATE to lock table $doctrineEnvelope = $this->executeQuery( $query->getSQL().' '.$this->driverConnection->getDatabasePlatform()->getWriteLockSQL(), - $query->getParameters() + $query->getParameters(), + $query->getParameterTypes() )->fetch(); if (false === $doctrineEnvelope) { @@ -160,8 +167,10 @@ public function get(): ?array ->where('id = ?'); $now = new \DateTime(); $this->executeQuery($queryBuilder->getSQL(), [ - self::formatDateTime($now), + $now, $doctrineEnvelope['id'], + ], [ + Type::DATETIME, ]); $this->driverConnection->commit(); @@ -228,7 +237,7 @@ public function getMessageCount(): int ->select('COUNT(m.id) as message_count') ->setMaxResults(1); - return $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters())->fetchColumn(); + return $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters(), $queryBuilder->getParameterTypes())->fetchColumn(); } public function findAll(int $limit = null): array @@ -238,7 +247,7 @@ public function findAll(int $limit = null): array $queryBuilder->setMaxResults($limit); } - $data = $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters())->fetchAll(); + $data = $this->executeQuery($queryBuilder->getSQL(), $queryBuilder->getParameters(), $queryBuilder->getParameterTypes())->fetchAll(); return array_map(function ($doctrineEnvelope) { return $this->decodeEnvelopeHeaders($doctrineEnvelope); @@ -267,9 +276,12 @@ private function createAvailableMessagesQueryBuilder(): QueryBuilder ->andWhere('m.available_at <= ?') ->andWhere('m.queue_name = ?') ->setParameters([ - self::formatDateTime($redeliverLimit), - self::formatDateTime($now), + $redeliverLimit, + $now, $this->configuration['queue_name'], + ], [ + Type::DATETIME, + Type::DATETIME, ]); } @@ -280,12 +292,10 @@ private function createQueryBuilder(): QueryBuilder ->from($this->configuration['table_name'], 'm'); } - private function executeQuery(string $sql, array $parameters = []) + private function executeQuery(string $sql, array $parameters = [], array $types = []) { - $stmt = null; try { - $stmt = $this->driverConnection->prepare($sql); - $stmt->execute($parameters); + $stmt = $this->driverConnection->executeQuery($sql, $parameters, $types); } catch (TableNotFoundException $e) { if ($this->driverConnection->isTransactionActive()) { throw $e; @@ -295,11 +305,7 @@ private function executeQuery(string $sql, array $parameters = []) if ($this->autoSetup) { $this->setup(); } - // statement not prepared ? SQLite throw on exception on prepare if the table does not exist - if (null === $stmt) { - $stmt = $this->driverConnection->prepare($sql); - } - $stmt->execute($parameters); + $stmt = $this->driverConnection->executeQuery($sql, $parameters, $types); } return $stmt; @@ -332,11 +338,6 @@ private function getSchema(): Schema return $schema; } - public static function formatDateTime(\DateTimeInterface $dateTime) - { - return $dateTime->format('Y-m-d\TH:i:s'); - } - private function decodeEnvelopeHeaders(array $doctrineEnvelope): array { $doctrineEnvelope['headers'] = json_decode($doctrineEnvelope['headers'], true); diff --git a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php index 59ec9f029d6a4..6ded724ca5bfa 100644 --- a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php @@ -20,6 +20,7 @@ * * @author Alexander Schranz * @author Antoine Bluchet + * @author Robin Chalas * * @internal * @final @@ -52,8 +53,8 @@ public function __construct(array $configuration, array $connectionCredentials = $this->connection->connect($connectionCredentials['host'] ?? '127.0.0.1', $connectionCredentials['port'] ?? 6379); $this->connection->setOption(\Redis::OPT_SERIALIZER, $redisOptions['serializer'] ?? \Redis::SERIALIZER_PHP); - if (isset($connectionCredentials['auth'])) { - $this->connection->auth($connectionCredentials['auth']); + if (isset($connectionCredentials['auth']) && !$this->connection->auth($connectionCredentials['auth'])) { + throw new InvalidArgumentException(sprintf('Redis connection failed: %s', $redis->getLastError())); } $this->stream = $configuration['stream'] ?? self::DEFAULT_OPTIONS['stream']; @@ -70,9 +71,9 @@ public static function fromDsn(string $dsn, array $redisOptions = [], \Redis $re $pathParts = explode('/', $parsedUrl['path'] ?? ''); - $stream = $pathParts[1] ?? null; - $group = $pathParts[2] ?? null; - $consumer = $pathParts[3] ?? null; + $stream = $pathParts[1] ?? $redisOptions['stream'] ?? null; + $group = $pathParts[2] ?? $redisOptions['group'] ?? null; + $consumer = $pathParts[3] ?? $redisOptions['consumer'] ?? null; $connectionCredentials = [ 'host' => $parsedUrl['host'] ?? '127.0.0.1', @@ -105,7 +106,6 @@ public function get(): ?array $messageId = '0'; // will receive consumers pending messages } - $e = null; try { $messages = $this->connection->xreadgroup( $this->group, diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php index 793da4a44802a..9a42bcc699bbf 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/PhpSerializer.php @@ -52,7 +52,6 @@ public function encode(Envelope $envelope): array private function safelyUnserialize($contents) { - $e = null; $signalingException = new MessageDecodingFailedException(sprintf('Could not decode message using PHP serialization: %s.', $contents)); $prevUnserializeHandler = ini_set('unserialize_callback_func', self::class.'::handleUnserializeCallback'); $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) { diff --git a/src/Symfony/Component/Messenger/Worker.php b/src/Symfony/Component/Messenger/Worker.php index 3ce1bd79e5857..6205baefd43bd 100644 --- a/src/Symfony/Component/Messenger/Worker.php +++ b/src/Symfony/Component/Messenger/Worker.php @@ -12,13 +12,13 @@ namespace Symfony\Component\Messenger; use Psr\Log\LoggerInterface; -use Symfony\Component\Debug\Exception\FlattenException; use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent; use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent; use Symfony\Component\Messenger\Event\WorkerStoppedEvent; use Symfony\Component\Messenger\Exception\HandlerFailedException; +use Symfony\Component\Messenger\Exception\RejectRedeliveredMessageException; use Symfony\Component\Messenger\Exception\UnrecoverableExceptionInterface; use Symfony\Component\Messenger\Retry\RetryStrategyInterface; use Symfony\Component\Messenger\Stamp\DelayStamp; @@ -136,6 +136,13 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, try { $envelope = $this->bus->dispatch($envelope->with(new ReceivedStamp($transportName))); } catch (\Throwable $throwable) { + $rejectFirst = $throwable instanceof RejectRedeliveredMessageException; + if ($rejectFirst) { + // redelivered messages are rejected first so that continuous failures in an event listener or while + // publishing for retry does not cause infinite redelivery loops + $receiver->reject($envelope); + } + if ($throwable instanceof HandlerFailedException) { $envelope = $throwable->getEnvelope(); } @@ -154,18 +161,18 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, // add the delay and retry stamp info + remove ReceivedStamp $retryEnvelope = $envelope->with(new DelayStamp($delay)) - ->with(new RedeliveryStamp($retryCount, $transportName, $throwable->getMessage(), $this->flattenedException($throwable))) + ->with(new RedeliveryStamp($retryCount, $transportName)) ->withoutAll(ReceivedStamp::class); - // re-send the message + // re-send the message for retry $this->bus->dispatch($retryEnvelope); - // acknowledge the previous message has received - $receiver->ack($envelope); } else { if (null !== $this->logger) { $this->logger->critical('Error thrown while handling message {class}. Removing from transport after {retryCount} retries. Error: "{error}"', $context + ['retryCount' => $retryCount, 'error' => $throwable->getMessage(), 'exception' => $throwable]); } + } + if (!$rejectFirst) { $receiver->reject($envelope); } @@ -217,17 +224,4 @@ private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInt return $retryStrategy->isRetryable($envelope); } - - private function flattenedException(\Throwable $throwable): ?FlattenException - { - if (!class_exists(FlattenException::class)) { - return null; - } - - if ($throwable instanceof HandlerFailedException) { - $throwable = $throwable->getNestedExceptions()[0]; - } - - return FlattenException::createFromThrowable($throwable); - } } diff --git a/src/Symfony/Component/Mime/CharacterStream.php b/src/Symfony/Component/Mime/CharacterStream.php index 9b80b2efa481a..045b477093c66 100644 --- a/src/Symfony/Component/Mime/CharacterStream.php +++ b/src/Symfony/Component/Mime/CharacterStream.php @@ -116,7 +116,6 @@ public function read(int $length): ?string if ($this->currentPos >= $this->charCount) { return null; } - $ret = null; $length = ($this->currentPos + $length > $this->charCount) ? $this->charCount - $this->currentPos : $length; if ($this->fixedWidth > 0) { $len = $length * $this->fixedWidth; diff --git a/src/Symfony/Component/Mime/FileBinaryMimeTypeGuesser.php b/src/Symfony/Component/Mime/FileBinaryMimeTypeGuesser.php index a25ebe4d5cdcd..e00ce6525b71a 100644 --- a/src/Symfony/Component/Mime/FileBinaryMimeTypeGuesser.php +++ b/src/Symfony/Component/Mime/FileBinaryMimeTypeGuesser.php @@ -85,7 +85,7 @@ public function guessMimeType(string $path): ?string $type = trim(ob_get_clean()); - if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) { + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) { // it's not a type, but an error message return null; } diff --git a/src/Symfony/Component/Mime/Tests/EmailTest.php b/src/Symfony/Component/Mime/Tests/EmailTest.php index 764f66b079480..1822da12aaa45 100644 --- a/src/Symfony/Component/Mime/Tests/EmailTest.php +++ b/src/Symfony/Component/Mime/Tests/EmailTest.php @@ -320,8 +320,6 @@ public function testGenerateBody() $e->text('text content'); $e->attach($file); $e->attach($image, 'test.gif'); - $fullhtml = new TextPart($content, 'utf-8', 'html'); - $inlinedimg = (new DataPart($image, 'test.gif'))->asInline(); $body = $e->getBody(); $this->assertInstanceOf(MixedPart::class, $body); $this->assertCount(2, $related = $body->getParts()); @@ -378,7 +376,7 @@ public function testSerialize() $e->from('fabien@symfony.com'); $e->text($r); $e->html($r); - $contents = file_get_contents($name = __DIR__.'/Fixtures/mimetypes/test', 'r'); + $name = __DIR__.'/Fixtures/mimetypes/test'; $file = fopen($name, 'r'); $e->attach($file, 'test'); $expected = clone $e; diff --git a/src/Symfony/Component/Mime/Tests/Header/HeadersTest.php b/src/Symfony/Component/Mime/Tests/Header/HeadersTest.php index 3568c9a6e45d6..e2eb75a6977f3 100644 --- a/src/Symfony/Component/Mime/Tests/Header/HeadersTest.php +++ b/src/Symfony/Component/Mime/Tests/Header/HeadersTest.php @@ -173,8 +173,6 @@ public function testAllReturnsEmptyArrayIfNoneSet() public function testRemoveRemovesAllHeadersWithName() { - $header0 = new UnstructuredHeader('X-Test', 'some@id'); - $header1 = new UnstructuredHeader('X-Test', 'other@id'); $headers = new Headers(); $headers->addIdHeader('X-Test', 'some@id'); $headers->addIdHeader('X-Test', 'other@id'); @@ -185,7 +183,6 @@ public function testRemoveRemovesAllHeadersWithName() public function testHasIsNotCaseSensitive() { - $header = new IdentificationHeader('Message-ID', 'some@id'); $headers = new Headers(); $headers->addIdHeader('Message-ID', 'some@id'); $this->assertTrue($headers->has('message-id')); @@ -209,7 +206,6 @@ public function testAllIsNotCaseSensitive() public function testRemoveIsNotCaseSensitive() { - $header = new IdentificationHeader('Message-ID', 'some@id'); $headers = new Headers(); $headers->addIdHeader('Message-ID', 'some@id'); $headers->remove('message-id'); diff --git a/src/Symfony/Component/Mime/Tests/Header/IdentificationHeaderTest.php b/src/Symfony/Component/Mime/Tests/Header/IdentificationHeaderTest.php index b7f0095d3a164..7d274ab162d55 100644 --- a/src/Symfony/Component/Mime/Tests/Header/IdentificationHeaderTest.php +++ b/src/Symfony/Component/Mime/Tests/Header/IdentificationHeaderTest.php @@ -103,7 +103,7 @@ public function testInvalidIdLeftThrowsException() { $this->expectException('Exception'); $this->expectExceptionMessage('Email "a b c@d" does not comply with addr-spec of RFC 2822.'); - $header = new IdentificationHeader('References', 'a b c@d'); + new IdentificationHeader('References', 'a b c@d'); } public function testIdRightCanBeDotAtom() @@ -139,7 +139,7 @@ public function testInvalidIdRightThrowsException() { $this->expectException('Exception'); $this->expectExceptionMessage('Email "a@b c d" does not comply with addr-spec of RFC 2822.'); - $header = new IdentificationHeader('References', 'a@b c d'); + new IdentificationHeader('References', 'a@b c d'); } public function testMissingAtSignThrowsException() @@ -149,7 +149,7 @@ public function testMissingAtSignThrowsException() /* -- RFC 2822, 3.6.4. msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] */ - $header = new IdentificationHeader('References', 'abc'); + new IdentificationHeader('References', 'abc'); } public function testSetBody() diff --git a/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php b/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php index 6bc029aee4cea..a8386f89462a9 100644 --- a/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php +++ b/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php @@ -26,7 +26,7 @@ public function testSingleAddressCanBeSetAndFetched() public function testAddressMustComplyWithRfc2822() { $this->expectException('Exception'); - $header = new PathHeader('Return-Path', new Address('chr is@swiftmailer.org')); + new PathHeader('Return-Path', new Address('chr is@swiftmailer.org')); } public function testValueIsAngleAddrWithValidAddress() diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index f47aef128451a..e9de7800b6540 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -917,7 +917,7 @@ public function offsetGet($option/*, bool $triggerDeprecation = true*/) // Validate the type of the resolved option if (isset($this->allowedTypes[$option])) { - $valid = false; + $valid = true; $invalidTypes = []; foreach ($this->allowedTypes[$option] as $type) { @@ -929,13 +929,18 @@ public function offsetGet($option/*, bool $triggerDeprecation = true*/) } if (!$valid) { - $keys = array_keys($invalidTypes); - - if (1 === \count($keys) && '[]' === substr($keys[0], -2)) { - throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0])); + $fmtActualValue = $this->formatValue($value); + $fmtAllowedTypes = implode('" or "', $this->allowedTypes[$option]); + $fmtProvidedTypes = implode('|', array_keys($invalidTypes)); + $allowedContainsArrayType = \count(array_filter($this->allowedTypes[$option], static function ($item) { + return '[]' === substr(self::$typeAliases[$item] ?? $item, -2); + })) > 0; + + if (\is_array($value) && $allowedContainsArrayType) { + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } - throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes)))); + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } } @@ -1040,26 +1045,23 @@ private function verifyTypes(string $type, $value, array &$invalidTypes, int $le { if (\is_array($value) && '[]' === substr($type, -2)) { $type = substr($type, 0, -2); + $valid = true; foreach ($value as $val) { if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) { - return false; + $valid = false; } } - return true; + return $valid; } if (('null' === $type && null === $value) || (\function_exists($func = 'is_'.$type) && $func($value)) || $value instanceof $type) { return true; } - if (!$invalidTypes) { - $suffix = ''; - while (\strlen($suffix) < $level * 2) { - $suffix .= '[]'; - } - $invalidTypes[$this->formatTypeOf($value).$suffix] = true; + if (!$invalidTypes || $level > 0) { + $invalidTypes[$this->formatTypeOf($value)] = true; } return false; diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 43ebf408e90b0..0211b22b26c00 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -776,7 +776,7 @@ public function testFailIfSetAllowedTypesFromLazyOption() public function testResolveFailsIfInvalidTypedArray() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[]'); @@ -796,7 +796,7 @@ public function testResolveFailsWithNonArray() public function testResolveFailsIfTypedArrayContainsInvalidTypes() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass|array|DateTime".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[]'); $values = range(1, 5); @@ -811,7 +811,7 @@ public function testResolveFailsIfTypedArrayContainsInvalidTypes() public function testResolveFailsWithCorrectLevelsButWrongScalar() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[][]'); @@ -847,6 +847,11 @@ public function provideInvalidTypes() [42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "integer".'], [null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "NULL".'], ['bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'], + [['foo', 12], 'string[]', 'The option "option" with value array is expected to be of type "string[]", but one of the elements is of type "integer".'], + [123, ['string[]', 'string'], 'The option "option" with value 123 is expected to be of type "string[]" or "string", but is of type "integer".'], + [[null], ['string[]', 'string'], 'The option "option" with value array is expected to be of type "string[]" or "string", but one of the elements is of type "NULL".'], + [['string', null], ['string[]', 'string'], 'The option "option" with value array is expected to be of type "string[]" or "string", but one of the elements is of type "NULL".'], + [[\stdClass::class], ['string'], 'The option "option" with value array is expected to be of type "string", but is of type "array".'], ]; } @@ -1898,7 +1903,7 @@ public function testNested2Arrays() public function testNestedArraysException() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'float[][][][]'); @@ -1916,7 +1921,7 @@ public function testNestedArraysException() public function testNestedArrayException1() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean|string|array".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[][]'); $this->resolver->resolve([ @@ -1929,7 +1934,7 @@ public function testNestedArrayException1() public function testNestedArrayException2() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean|string|array".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[][]'); $this->resolver->resolve([ @@ -1942,7 +1947,7 @@ public function testNestedArrayException2() public function testNestedArrayException3() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string|integer".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'string[][][]'); $this->resolver->resolve([ @@ -1955,7 +1960,7 @@ public function testNestedArrayException3() public function testNestedArrayException4() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'string[][][]'); $this->resolver->resolve([ @@ -1969,7 +1974,7 @@ public function testNestedArrayException4() public function testNestedArrayException5() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'string[]'); $this->resolver->resolve([ diff --git a/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php index a400273964613..2a0278f8e0456 100644 --- a/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php @@ -132,9 +132,6 @@ public function testFindProcessInOpenBasedir() $this->assertSamePath(PHP_BINARY, $result); } - /** - * @requires PHP 5.4 - */ public function testFindBatchExecutableOnWindows() { if (ini_get('open_basedir')) { diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index c7a96d4bf9969..d2f1a5150893a 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -119,8 +119,8 @@ public function getProperties($class, array $context = []) if (!$propertyName || isset($properties[$propertyName])) { continue; } - if (!$reflectionClass->hasProperty($propertyName) && !preg_match('/^[A-Z]{2,}/', $propertyName)) { - $propertyName = lcfirst($propertyName); + if ($reflectionClass->hasProperty($lowerCasedPropertyName = lcfirst($propertyName)) || (!$reflectionClass->hasProperty($propertyName) && !preg_match('/^[A-Z]{2,}/', $propertyName))) { + $propertyName = $lowerCasedPropertyName; } $properties[$propertyName] = $propertyName; } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index fe8d52a3c503a..8b2d087be8d8a 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -67,6 +67,8 @@ public function testGetProperties() '123', 'self', 'realParent', + 'xTotals', + 'YT', 'c', 'd', 'e', diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index 9bd856bd47df5..460c08d3628e5 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -93,6 +93,16 @@ class Dummy extends ParentDummy */ public $j; + /** + * @var array + */ + private $xTotals; + + /** + * @var string + */ + private $YT; + /** * This should not be removed. * @@ -181,4 +191,18 @@ public function setSelf(self $self) public function setRealParent(parent $realParent) { } + + /** + * @return array + */ + public function getXTotals() + { + } + + /** + * @return string + */ + public function getYT() + { + } } diff --git a/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php index a315cfb4ad07e..b8155b1f61b9e 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/RoutingConfigurator.php @@ -39,7 +39,8 @@ public function __construct(RouteCollection $collection, PhpFileLoader $loader, final public function import($resource, $type = null, $ignoreErrors = false) { $this->loader->setCurrentDir(\dirname($this->path)); - $imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file); + $imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file) ?: []; + if (!\is_array($imported)) { return new ImportConfigurator($this->collection, $imported); } diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php index ed4faf3915b72..bd61721fe2c17 100644 --- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php @@ -169,7 +169,7 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, $ $this->setCurrentDir(\dirname($path)); /** @var RouteCollection[] $imported */ - $imported = $this->import($resource, ('' !== $type ? $type : null), false, $file); + $imported = $this->import($resource, ('' !== $type ? $type : null), false, $file) ?: []; if (!\is_array($imported)) { $imported = [$imported]; diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php index 15c223ecadcf4..d9d7d28af1ca0 100644 --- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php @@ -187,7 +187,7 @@ protected function parseImport(RouteCollection $collection, array $config, $path $this->setCurrentDir(\dirname($path)); - $imported = $this->import($config['resource'], $type, false, $file); + $imported = $this->import($config['resource'], $type, false, $file) ?: []; if (!\is_array($imported)) { $imported = [$imported]; diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php index ab67e6885ac92..b3a93886796b4 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherDumper.php @@ -121,7 +121,7 @@ static function (\$condition, \$context, \$request) { // \$checkCondition } } EOF; - $compiledRoutes[4] = $forDump ? $checkConditionCode .= ",\n" : eval('return '.$checkConditionCode.';'); + $compiledRoutes[4] = $forDump ? $checkConditionCode.",\n" : eval('return '.$checkConditionCode.';'); } else { $compiledRoutes[4] = $forDump ? " null, // \$checkCondition\n" : null; } diff --git a/src/Symfony/Component/Routing/Tests/Annotation/RouteTest.php b/src/Symfony/Component/Routing/Tests/Annotation/RouteTest.php index 58c59dcbbfa15..2c193a8a09588 100644 --- a/src/Symfony/Component/Routing/Tests/Annotation/RouteTest.php +++ b/src/Symfony/Component/Routing/Tests/Annotation/RouteTest.php @@ -19,13 +19,13 @@ class RouteTest extends TestCase public function testInvalidRouteParameter() { $this->expectException('BadMethodCallException'); - $route = new Route(['foo' => 'bar']); + new Route(['foo' => 'bar']); } public function testTryingToSetLocalesDirectly() { $this->expectException('BadMethodCallException'); - $route = new Route(['locales' => ['nl' => 'bar']]); + new Route(['locales' => ['nl' => 'bar']]); } /** diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php index ee8f4b071a368..c06fb43f6887e 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php @@ -6,7 +6,7 @@ trait FooTrait { public function doBar() { - $baz = self::class; + self::class; if (true) { } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/controller/empty_wildcard/.gitignore b/src/Symfony/Component/Routing/Tests/Fixtures/controller/empty_wildcard/.gitignore new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.yml b/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.yml index 90dce0ea1bfc4..057b7b2d6b9ce 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.yml +++ b/src/Symfony/Component/Routing/Tests/Fixtures/import_with_name_prefix/routing.yml @@ -5,3 +5,7 @@ api: resource: ../controller/routing.yml name_prefix: api_ prefix: /api + +empty_wildcard: + resource: ../controller/empty_wildcard/* + prefix: /empty_wildcard diff --git a/src/Symfony/Component/Routing/Tests/Generator/Dumper/CompiledUrlGeneratorDumperTest.php b/src/Symfony/Component/Routing/Tests/Generator/Dumper/CompiledUrlGeneratorDumperTest.php index 513e1c80e1e66..521f0f126cda7 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/Dumper/CompiledUrlGeneratorDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/Dumper/CompiledUrlGeneratorDumperTest.php @@ -194,7 +194,7 @@ public function testGenerateNonExistingRoute() file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump()); $projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext()); - $url = $projectUrlGenerator->generate('NonExisting', []); + $projectUrlGenerator->generate('NonExisting', []); } public function testDumpForRouteWithDefaults() diff --git a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php index 8ea60eb279f3c..5e81b8f5d5755 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php @@ -211,7 +211,7 @@ public function testGenerateNonExistingRoute() include $this->testTmpFilepath; $projectUrlGenerator = new \NonExistingRoutesUrlGenerator(new RequestContext()); - $url = $projectUrlGenerator->generate('NonExisting', []); + $projectUrlGenerator->generate('NonExisting', []); } public function testDumpForRouteWithDefaults() diff --git a/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php b/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php index a54e18b583834..5729d4caa5450 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php @@ -247,7 +247,7 @@ public function testRouteWithSameVariableTwice() $this->expectException('LogicException'); $route = new Route('/{name}/{name}'); - $compiled = $route->compile(); + $route->compile(); } public function testRouteCharsetMismatch() @@ -255,7 +255,7 @@ public function testRouteCharsetMismatch() $this->expectException('LogicException'); $route = new Route("/\xE9/{bar}", [], ['bar' => '.'], ['utf8' => true]); - $compiled = $route->compile(); + $route->compile(); } public function testRequirementCharsetMismatch() @@ -263,7 +263,7 @@ public function testRequirementCharsetMismatch() $this->expectException('LogicException'); $route = new Route('/foo/{bar}', [], ['bar' => "\xE9"], ['utf8' => true]); - $compiled = $route->compile(); + $route->compile(); } public function testRouteWithFragmentAsPathParameter() @@ -271,7 +271,7 @@ public function testRouteWithFragmentAsPathParameter() $this->expectException('InvalidArgumentException'); $route = new Route('/{_fragment}'); - $compiled = $route->compile(); + $route->compile(); } /** diff --git a/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php index 1e09992afed4e..364cd19236ec2 100644 --- a/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/NativePasswordEncoder.php @@ -45,7 +45,7 @@ public function __construct(int $opsLimit = null, int $memLimit = null, int $cos throw new \InvalidArgumentException('$cost must be in the range of 4-31.'); } - $this->algo = \defined('PASSWORD_ARGON2I') ? max(PASSWORD_DEFAULT, \defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_ARGON2I) : PASSWORD_DEFAULT; + $this->algo = \defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : (\defined('PASSWORD_ARGON2I') ? PASSWORD_ARGON2I : PASSWORD_BCRYPT); $this->options = [ 'cost' => $cost, 'time_cost' => $opsLimit, @@ -59,20 +59,13 @@ public function __construct(int $opsLimit = null, int $memLimit = null, int $cos */ public function encodePassword($raw, $salt) { - if (\strlen($raw) > self::MAX_PASSWORD_LENGTH) { + if (\strlen($raw) > self::MAX_PASSWORD_LENGTH || (PASSWORD_BCRYPT === $this->algo && 72 < \strlen($raw))) { throw new BadCredentialsException('Invalid password.'); } // Ignore $salt, the auto-generated one is always the best - $encoded = password_hash($raw, $this->algo, $this->options); - - if (72 < \strlen($raw) && 0 === strpos($encoded, '$2')) { - // BCrypt encodes only the first 72 chars - throw new BadCredentialsException('Invalid password.'); - } - - return $encoded; + return password_hash($raw, $this->algo, $this->options); } /** @@ -80,11 +73,23 @@ public function encodePassword($raw, $salt) */ public function isPasswordValid($encoded, $raw, $salt) { - if (72 < \strlen($raw) && 0 === strpos($encoded, '$2')) { - // BCrypt encodes only the first 72 chars + if (\strlen($raw) > self::MAX_PASSWORD_LENGTH) { return false; } - return \strlen($raw) <= self::MAX_PASSWORD_LENGTH && password_verify($raw, $encoded); + if (0 === strpos($encoded, '$2')) { + // BCrypt encodes only the first 72 chars + return 72 >= \strlen($raw) && password_verify($raw, $encoded); + } + + if (\extension_loaded('sodium') && version_compare(\SODIUM_LIBRARY_VERSION, '1.0.14', '>=')) { + return sodium_crypto_pwhash_str_verify($encoded, $raw); + } + + if (\extension_loaded('libsodium') && version_compare(phpversion('libsodium'), '1.0.14', '>=')) { + return \Sodium\crypto_pwhash_str_verify($encoded, $raw); + } + + return password_verify($raw, $encoded); } } diff --git a/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php index 934a3fdfca528..9c794004111a6 100644 --- a/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php +++ b/src/Symfony/Component/Security/Core/Encoder/SodiumPasswordEncoder.php @@ -93,6 +93,6 @@ public function isPasswordValid($encoded, $raw, $salt) return \Sodium\crypto_pwhash_str_verify($encoded, $raw); } - throw new LogicException('Libsodium is not available. You should either install the sodium extension, upgrade to PHP 7.2+ or use a different encoder.'); + return false; } } diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php index 0b2c36c72de4a..48b7ebcbb269f 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php @@ -117,7 +117,7 @@ public function testGetInvalidNamedEncoderForEncoderAware() $user = new EncAwareUser('user', 'pass'); $user->encoderName = 'invalid_encoder_name'; - $encoder = $factory->getEncoder($user); + $factory->getEncoder($user); } public function testGetEncoderForEncoderAwareWithClassName() diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index 17b4ab98f92df..2ad03a5ce385a 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -162,7 +162,7 @@ public function testGuardWithNoLongerAuthenticatedTriggersLogout() $token->setAuthenticated(false); $provider = new GuardAuthenticationProvider([], $this->userProvider, $providerKey, $this->userChecker); - $actualToken = $provider->authenticate($token); + $provider->authenticate($token); } public function testSupportsChecksGuardAuthenticatorsTokenOrigin() diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php index 7cb438e3a7834..5eed09392802e 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php @@ -21,7 +21,7 @@ class LogoutListenerTest extends TestCase { public function testHandleUnmatchedPath() { - list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener(); + list($listener, , $httpUtils, $options) = $this->getListener(); list($event, $request) = $this->getGetResponseEvent(); @@ -131,7 +131,7 @@ public function testSuccessHandlerReturnsNonResponse() $this->expectException('RuntimeException'); $successHandler = $this->getSuccessHandler(); - list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener($successHandler); + list($listener, , $httpUtils, $options) = $this->getListener($successHandler); list($event, $request) = $this->getGetResponseEvent(); @@ -153,7 +153,7 @@ public function testCsrfValidationFails() $this->expectException('Symfony\Component\Security\Core\Exception\LogoutException'); $tokenManager = $this->getTokenManager(); - list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener(null, $tokenManager); + list($listener, , $httpUtils, $options) = $this->getListener(null, $tokenManager); list($event, $request) = $this->getGetResponseEvent(); diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php index c50b8c218c2d0..ceb557b139d0a 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php @@ -224,7 +224,7 @@ public function testOnCoreSecurity() public function testSessionStrategy() { - list($listener, $tokenStorage, $service, $manager, , $dispatcher, $sessionStrategy) = $this->getListener(false, true, true); + list($listener, $tokenStorage, $service, $manager, , , $sessionStrategy) = $this->getListener(false, true, true); $tokenStorage ->expects($this->once()) @@ -289,7 +289,7 @@ public function testSessionStrategy() public function testSessionIsMigratedByDefault() { - list($listener, $tokenStorage, $service, $manager, , $dispatcher, $sessionStrategy) = $this->getListener(false, true, false); + list($listener, $tokenStorage, $service, $manager) = $this->getListener(false, true, false); $tokenStorage ->expects($this->once()) diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php index 02d1ba03ce441..fd29297a5778e 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php @@ -60,7 +60,7 @@ public function testGetPreAuthenticatedDataNoUser() $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData'); $method->setAccessible(true); - $result = $method->invokeArgs($listener, [$request]); + $method->invokeArgs($listener, [$request]); } public function testGetPreAuthenticatedDataWithDifferentKeys() diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php index e35e685d5bf88..c81b2d589ed06 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php @@ -98,7 +98,7 @@ public function testGetPreAuthenticatedDataNoData() $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData'); $method->setAccessible(true); - $result = $method->invokeArgs($listener, [$request]); + $method->invokeArgs($listener, [$request]); } public function testGetPreAuthenticatedDataWithDifferentKeys() diff --git a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php index bb81bc36a7086..e3e04d8f1573d 100644 --- a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php +++ b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Http\Firewall; @@ -52,8 +51,6 @@ public function testOnKernelRequestRegistersExceptionListener() public function testOnKernelRequestStopsWhenThereIsAResponse() { - $response = new Response(); - $called = []; $first = function () use (&$called) { diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php index 912868a25621f..9c4fa730b1143 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php @@ -66,8 +66,6 @@ public function testRememberMeCookieIsNotSendWithResponse() public function testItSubscribesToTheOnKernelResponseEvent() { - $listener = new ResponseListener(); - $this->assertSame([KernelEvents::RESPONSE => 'onKernelResponse'], ResponseListener::getSubscribedEvents()); } diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index 30bcacec95c42..9a92d5d866ad1 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -465,7 +465,7 @@ private function buildXml(\DOMNode $parentNode, $data, string $xmlRootNodeName = return $this->appendNode($parentNode, $data, 'data'); } - throw new NotEncodableValueException(sprintf('An unexpected value could not be serialized: %s', var_export($data, true))); + throw new NotEncodableValueException(sprintf('An unexpected value could not be serialized: %s', !\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data)))); } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 5f165b56ac774..056744fa4b0f8 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -552,7 +552,6 @@ protected function createChildContext(array $parentContext, $attribute/*, ?strin { if (\func_num_args() < 3) { @trigger_error(sprintf('Method "%s::%s()" will have a third "?string $format" argument in version 5.0; not defining it is deprecated since Symfony 4.3.', \get_class($this), __FUNCTION__), E_USER_DEPRECATED); - $format = null; } if (isset($parentContext[self::ATTRIBUTES][$attribute])) { $parentContext[self::ATTRIBUTES] = $parentContext[self::ATTRIBUTES][$attribute]; diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 8b5230724a6ec..3b439abdcc3c3 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -173,7 +173,7 @@ public function normalize($data, $format = null, array $context = []) throw new NotNormalizableValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', \get_class($data))); } - throw new NotNormalizableValueException(sprintf('An unexpected value could not be normalized: %s', var_export($data, true))); + throw new NotNormalizableValueException(sprintf('An unexpected value could not be normalized: %s', !\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data)))); } /** diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php index 60e73aa36fdb9..3df016f067640 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\XmlEncoder; +use Symfony\Component\Serializer\Exception\NotEncodableValueException; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Serializer; @@ -796,6 +797,14 @@ public function testEncodeXmlWithDateTimeObjectField() $this->assertEquals($this->createXmlWithDateTimeField(), $actualXml); } + public function testNotEncodableValueExceptionMessageForAResource() + { + $this->expectException(NotEncodableValueException::class); + $this->expectExceptionMessage('An unexpected value could not be serialized: stream resource'); + + (new XmlEncoder())->encode(tmpfile(), 'xml'); + } + public function testEncodeComment() { $expected = <<<'XML' diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index ed0b35b3d5b5f..7cfab8c94985b 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -17,6 +17,7 @@ use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\ClassMetadata; @@ -468,6 +469,14 @@ public function testExceptionWhenTypeIsNotInTheBodyToDeserialiaze() $this->serializerWithClassDiscriminator()->deserialize('{"one":1}', DummyMessageInterface::class, 'json'); } + public function testNotNormalizableValueExceptionMessageForAResource() + { + $this->expectException(NotNormalizableValueException::class); + $this->expectExceptionMessage('An unexpected value could not be normalized: stream resource'); + + (new Serializer())->normalize(tmpfile()); + } + private function serializerWithClassDiscriminator() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); diff --git a/src/Symfony/Component/Templating/Tests/Loader/FilesystemLoaderTest.php b/src/Symfony/Component/Templating/Tests/Loader/FilesystemLoaderTest.php index 36a63bce3b679..242dc42a63bd7 100644 --- a/src/Symfony/Component/Templating/Tests/Loader/FilesystemLoaderTest.php +++ b/src/Symfony/Component/Templating/Tests/Loader/FilesystemLoaderTest.php @@ -27,7 +27,6 @@ public static function setUpBeforeClass(): void public function testConstructor() { $pathPattern = self::$fixturesPath.'/templates/%name%.%engine%'; - $path = self::$fixturesPath.'/templates'; $loader = new ProjectTemplateLoader2($pathPattern); $this->assertEquals([$pathPattern], $loader->getTemplatePathPatterns(), '__construct() takes a path as its second argument'); $loader = new ProjectTemplateLoader2([$pathPattern]); diff --git a/src/Symfony/Component/Translation/Resources/bin/translation-status.php b/src/Symfony/Component/Translation/Resources/bin/translation-status.php index 669b8f2864730..44918c92ec527 100644 --- a/src/Symfony/Component/Translation/Resources/bin/translation-status.php +++ b/src/Symfony/Component/Translation/Resources/bin/translation-status.php @@ -89,7 +89,8 @@ function findTranslationFiles($originalFilePath, $localeToAnalyze) $originalFileName = basename($originalFilePath); $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName); - $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern); + $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern, GLOB_NOSORT); + sort($translationFiles); foreach ($translationFiles as $filePath) { $locale = extractLocaleFromFilePath($filePath); diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index 72666692138f7..145607964d7ff 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -61,7 +61,7 @@ public function validate($value, Constraint $constraint) $binaryFormat = null === $constraint->binaryFormat ? true : $constraint->binaryFormat; } - list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes(0, $limitInBytes, $binaryFormat); + list(, $limitAsString, $suffix) = $this->factorizeSizes(0, $limitInBytes, $binaryFormat); $this->context->buildViolation($constraint->uploadIniSizeErrorMessage) ->setParameter('{{ limit }}', $limitAsString) ->setParameter('{{ suffix }}', $suffix) diff --git a/src/Symfony/Component/Validator/DependencyInjection/AddAutoMappingConfigurationPass.php b/src/Symfony/Component/Validator/DependencyInjection/AddAutoMappingConfigurationPass.php index 177f42e655d84..f28b78650ca0e 100644 --- a/src/Symfony/Component/Validator/DependencyInjection/AddAutoMappingConfigurationPass.php +++ b/src/Symfony/Component/Validator/DependencyInjection/AddAutoMappingConfigurationPass.php @@ -59,6 +59,10 @@ public function process(ContainerBuilder $container) $validatorBuilder = $container->getDefinition($this->validatorBuilderService); foreach ($container->findTaggedServiceIds($this->tag) as $id => $tags) { $regexp = $this->getRegexp(array_merge($globalNamespaces, $servicesToNamespaces[$id] ?? [])); + if (null === $regexp) { + $container->removeDefinition($id); + continue; + } $container->getDefinition($id)->setArgument('$classValidatorRegexp', $regexp); $validatorBuilder->addMethodCall('addLoader', [new Reference($id)]); diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf index db534528d1d99..bfa9b1284e8d9 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf @@ -334,6 +334,38 @@ This value should be valid JSON. Verdien er ikke gyldig JSON. + + This collection should contain only unique elements. + Samlingen kan kun inneholde unike elementer. + + + This value should be positive. + Denne verdien må være positiv. + + + This value should be either positive or zero. + Denne verdien må være positiv eller null. + + + This value should be negative. + Denne verdien må være negativ. + + + This value should be either negative or zero. + Denne verdien må være negativ eller null. + + + This value is not a valid timezone. + Verdien er ikke en gyldig tidssone. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Dette passordet er lekket i et datainnbrudd, det må ikke tas i bruk. Vennligst bruk et annet passord. + + + This value should be between {{ min }} and {{ max }}. + Verdien må være mellom {{ min }} og {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf index db534528d1d99..bfa9b1284e8d9 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf @@ -334,6 +334,38 @@ This value should be valid JSON. Verdien er ikke gyldig JSON. + + This collection should contain only unique elements. + Samlingen kan kun inneholde unike elementer. + + + This value should be positive. + Denne verdien må være positiv. + + + This value should be either positive or zero. + Denne verdien må være positiv eller null. + + + This value should be negative. + Denne verdien må være negativ. + + + This value should be either negative or zero. + Denne verdien må være negativ eller null. + + + This value is not a valid timezone. + Verdien er ikke en gyldig tidssone. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Dette passordet er lekket i et datainnbrudd, det må ikke tas i bruk. Vennligst bruk et annet passord. + + + This value should be between {{ min }} and {{ max }}. + Verdien må være mellom {{ min }} og {{ max }}. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf index b3e8f0f42f124..bf7da2f06c907 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sv.xlf @@ -334,6 +334,38 @@ This value should be valid JSON. Detta värde ska vara giltig JSON. + + This collection should contain only unique elements. + Denna samling bör endast innehålla unika element. + + + This value should be positive. + Detta värde bör vara positivt. + + + This value should be either positive or zero. + Detta värde bör vara antingen positivt eller noll. + + + This value should be negative. + Detta värde bör vara negativt. + + + This value should be either negative or zero. + Detta värde bör vara antingen negativt eller noll. + + + This value is not a valid timezone. + Detta värde är inte en giltig tidszon. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Det här lösenordet har läckt ut vid ett dataintrång, det får inte användas. Använd ett annat lösenord. + + + This value should be between {{ min }} and {{ max }}. + Detta värde bör ligga mellan {{ min }} och {{ max }}. + diff --git a/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php b/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php index 698d95d91348d..f8dc69efc16bb 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintViolationTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\Tests\Fixtures\CustomArrayObject; -use Symfony\Component\Validator\Tests\Fixtures\ToString; class ConstraintViolationTest extends TestCase { @@ -114,7 +113,7 @@ public function testToStringOmitsEmptyCodes() public function testMessageCannotBeArray() { $this->expectException(\TypeError::class); - $violation = new ConstraintViolation( + new ConstraintViolation( ['cannot be an array'], '', [], @@ -127,7 +126,7 @@ public function testMessageCannotBeArray() public function testMessageObjectMustBeStringable() { $this->expectException(\TypeError::class); - $violation = new ConstraintViolation( + new ConstraintViolation( new CustomArrayObject(), '', [], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index 16d5b7f42422f..58bc4e98fd991 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -456,7 +456,7 @@ public function uploadedFileErrorProvider() $reflection = new \ReflectionClass(\get_class(new FileValidator())); $method = $reflection->getMethod('factorizeSizes'); $method->setAccessible(true); - list($sizeAsString, $limit, $suffix) = $method->invokeArgs(new FileValidator(), [0, UploadedFile::getMaxFilesize(), false]); + list(, $limit, $suffix) = $method->invokeArgs(new FileValidator(), [0, UploadedFile::getMaxFilesize(), false]); // it correctly parses the maxSize option and not only uses simple string comparison // 1000M should be bigger than the ini value diff --git a/src/Symfony/Component/Validator/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php b/src/Symfony/Component/Validator/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php index bce1101ef5cca..711bf8fc4f610 100644 --- a/src/Symfony/Component/Validator/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php +++ b/src/Symfony/Component/Validator/Tests/DependencyInjection/AddAutoMappingConfigurationPassTest.php @@ -81,6 +81,6 @@ public function testDoNotMapAllClassesWhenConfigIsEmpty() (new AddAutoMappingConfigurationPass())->process($container); - $this->assertNull($container->getDefinition('loader')->getArgument('$classValidatorRegexp')); + $this->assertFalse($container->hasDefinition('loader')); } } diff --git a/src/Symfony/Component/Validator/Tests/Util/PropertyPathTest.php b/src/Symfony/Component/Validator/Tests/Util/PropertyPathTest.php index f796463bbc320..99bf9e6eb2ebe 100644 --- a/src/Symfony/Component/Validator/Tests/Util/PropertyPathTest.php +++ b/src/Symfony/Component/Validator/Tests/Util/PropertyPathTest.php @@ -32,6 +32,7 @@ public function provideAppendPaths() ['foo', 'bar', 'foo.bar', 'It append the subPath to the basePath'], ['foo', '[bar]', 'foo[bar]', 'It does not include the dot separator if subPath uses the array notation'], ['0', 'bar', '0.bar', 'Leading zeros are kept.'], + ['0', 1, '0.1', 'Numeric subpaths do not cause PHP 7.4 errors.'], ]; } } diff --git a/src/Symfony/Component/Validator/Util/PropertyPath.php b/src/Symfony/Component/Validator/Util/PropertyPath.php index 4108a02c24f25..5d062356772c0 100644 --- a/src/Symfony/Component/Validator/Util/PropertyPath.php +++ b/src/Symfony/Component/Validator/Util/PropertyPath.php @@ -36,12 +36,13 @@ class PropertyPath */ public static function append($basePath, $subPath) { - if ('' !== (string) $subPath) { + $subPath = (string) $subPath; + if ('' !== $subPath) { if ('[' === $subPath[0]) { return $basePath.$subPath; } - return '' !== (string) $basePath ? $basePath.'.'.$subPath : $subPath; + return '' !== $basePath ? $basePath.'.'.$subPath : $subPath; } return $basePath; diff --git a/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php b/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php index 78acb90b66a68..bd8b595a3dc02 100644 --- a/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php @@ -30,7 +30,8 @@ public static function castRequest(Request $request, array $a, Stub $stub, $isNe $clone = null; foreach (self::$requestGetters as $prop => $getter) { - if (null === $a[Caster::PREFIX_PROTECTED.$prop]) { + $key = Caster::PREFIX_PROTECTED.$prop; + if (\array_key_exists($key, $a) && null === $a[$key]) { if (null === $clone) { $clone = clone $request; } @@ -44,7 +45,9 @@ public static function castRequest(Request $request, array $a, Stub $stub, $isNe public static function castHttpClient($client, array $a, Stub $stub, $isNested) { $multiKey = sprintf("\0%s\0multi", \get_class($client)); - $a[$multiKey] = new CutStub($a[$multiKey]); + if (isset($a[$multiKey])) { + $a[$multiKey] = new CutStub($a[$multiKey]); + } return $a; } diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 9b258f4505266..8b11ab9251146 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -28,7 +28,7 @@ class CliDumper extends AbstractDumper protected $maxStringWidth = 0; protected $styles = [ // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics - 'default' => '38;5;208', + 'default' => '0;38;5;208', 'num' => '1;38;5;38', 'const' => '1;38;5;208', 'str' => '1;38;5;113', diff --git a/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php b/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php index 21902b5cf1aa4..a67cb76bf11be 100644 --- a/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php @@ -72,7 +72,7 @@ public function testNoServer() $connection = new Connection(self::VAR_DUMPER_SERVER); $start = microtime(true); $this->assertFalse($connection->write($data)); - $this->assertLessThan(1, microtime(true) - $start); + $this->assertLessThan(4, microtime(true) - $start); } private function getServerProcess(): Process diff --git a/src/Symfony/Component/Workflow/Tests/DefinitionTest.php b/src/Symfony/Component/Workflow/Tests/DefinitionTest.php index 35d89329e21b9..6ba3c1ef47f02 100644 --- a/src/Symfony/Component/Workflow/Tests/DefinitionTest.php +++ b/src/Symfony/Component/Workflow/Tests/DefinitionTest.php @@ -38,7 +38,7 @@ public function testSetInitialPlaceAndPlaceIsNotDefined() { $this->expectException('Symfony\Component\Workflow\Exception\LogicException'); $this->expectExceptionMessage('Place "d" cannot be the initial place as it does not exist.'); - $definition = new Definition([], [], 'd'); + new Definition([], [], 'd'); } public function testAddTransition() diff --git a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php index 8514eea830e84..b559344364db8 100644 --- a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php +++ b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php @@ -314,7 +314,7 @@ public function testApplyWithSameNameTransition() $this->assertFalse($marking->has('b')); $this->assertFalse($marking->has('c')); - $marking = $workflow->apply($subject, 'a_to_bc'); + $workflow->apply($subject, 'a_to_bc'); $marking = $workflow->apply($subject, 'b_to_c'); $this->assertFalse($marking->has('a')); @@ -406,7 +406,7 @@ public function testApplyWithEventDispatcher() 'workflow.workflow_name.announce.t2', ]; - $marking = $workflow->apply($subject, 't1'); + $workflow->apply($subject, 't1'); $this->assertSame($eventNameExpected, $eventDispatcher->dispatchedEvents); } @@ -446,7 +446,7 @@ public function testApplyDoesNotTriggerExtraGuardWithEventDispatcher() 'workflow.workflow_name.announce', ]; - $marking = $workflow->apply($subject, 'a-b'); + $workflow->apply($subject, 'a-b'); $this->assertSame($eventNameExpected, $eventDispatcher->dispatchedEvents); } diff --git a/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php b/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php index b2a5c83b52e59..0971622737225 100644 --- a/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php +++ b/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php @@ -37,7 +37,7 @@ public function validate(Definition $definition, $name) // Enforcing uniqueness of the names of transitions starting at each node $from = reset($froms); if (isset($transitionFromNames[$from][$transition->getName()])) { - throw new InvalidDefinitionException(sprintf('A transition from a place/state must have an unique name. Multiple transitions named "%s" from place/state "%s" where found on StateMachine "%s".', $transition->getName(), $from, $name)); + throw new InvalidDefinitionException(sprintf('A transition from a place/state must have an unique name. Multiple transitions named "%s" from place/state "%s" were found on StateMachine "%s".', $transition->getName(), $from, $name)); } $transitionFromNames[$from][$transition->getName()] = true; diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index b9b399937be67..1fbfeed7ba7dc 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -94,15 +94,15 @@ public static function parse(string $value = null, int $flags = 0, array $refere $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references); } - if (null !== $tag && '' !== $tag) { - return new TaggedValue($tag, $result); - } - // some comments are allowed at the end if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } + if (null !== $tag && '' !== $tag) { + return new TaggedValue($tag, $result); + } + return $result; } finally { if (isset($mbEncoding)) { diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index ef53f2a5e6e8c..6ad165ad4e313 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -85,7 +85,6 @@ public function parse(string $value, int $flags = 0) $this->refs = []; $mbEncoding = null; - $data = null; if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); @@ -108,14 +107,6 @@ public function parse(string $value, int $flags = 0) return $data; } - /** - * @internal - */ - public function getLastLineNumberBeforeDeprecation(): int - { - return $this->getRealCurrentLineNb(); - } - private function doParse(string $value, int $flags) { $this->currentLineNb = -1; diff --git a/src/Symfony/Component/Yaml/Tests/Command/LintCommandTest.php b/src/Symfony/Component/Yaml/Tests/Command/LintCommandTest.php index 4ab7d17d7c2e8..e5bb8fc2b7e80 100644 --- a/src/Symfony/Component/Yaml/Tests/Command/LintCommandTest.php +++ b/src/Symfony/Component/Yaml/Tests/Command/LintCommandTest.php @@ -97,7 +97,7 @@ public function testLintFileNotReadable() $filename = $this->createFile(''); unlink($filename); - $ret = $tester->execute(['filename' => $filename], ['decorated' => false]); + $tester->execute(['filename' => $filename], ['decorated' => false]); } /** diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 74dc8ff19a5c9..a18adfff6fa2d 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -167,6 +167,12 @@ public function testParseInvalidSequenceShouldThrowException() Inline::parse('{ foo: bar } bar'); } + public function testParseInvalidTaggedSequenceShouldThrowException() + { + $this->expectException('Symfony\Component\Yaml\Exception\ParseException'); + Inline::parse('!foo { bar: baz } qux', Yaml::PARSE_CUSTOM_TAGS); + } + public function testParseScalarWithCorrectlyQuotedStringShouldReturnString() { $value = "'don''t do somthin'' like that'"; diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 00b383e7f3955..e05fad796a270 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -64,7 +64,7 @@ public function testTabsInYaml() foreach ($yamls as $yaml) { try { - $content = $this->parser->parse($yaml); + $this->parser->parse($yaml); $this->fail('YAML files must not contain tabs'); } catch (\Exception $e) { diff --git a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php index ec03bb61c2b84..08dd1b6dcac7f 100644 --- a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php +++ b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php @@ -29,15 +29,20 @@ } } +$json = json_encode($vars, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + switch ($vars['REQUEST_URI']) { default: exit; + case '/head': + header('Content-Length: '.strlen($json), true); + break; + case '/': case '/?a=a&b=b': case 'http://127.0.0.1:8057/': case 'http://localhost:8057/': - header('Content-Type: application/json'); ob_start('ob_gzhandler'); break; @@ -82,6 +87,12 @@ header('Location: ..', true, 302); break; + case '/304': + header('Content-Length: 10', true, 304); + echo '12345'; + + return; + case '/307': header('Location: http://localhost:8057/post', true, 307); break; @@ -138,4 +149,4 @@ header('Content-Type: application/json', true); -echo json_encode($vars, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); +echo $json; diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php index 0547c752ac9d5..e8d8b19eee9c6 100644 --- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php +++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php @@ -72,6 +72,31 @@ public function testGetRequest() $response->getContent(); } + public function testHeadRequest() + { + $client = $this->getHttpClient(__FUNCTION__); + $response = $client->request('HEAD', 'http://localhost:8057/head', [ + 'headers' => ['Foo' => 'baR'], + 'user_data' => $data = new \stdClass(), + 'buffer' => false, + ]); + + $this->assertSame([], $response->getInfo('response_headers')); + $this->assertSame(200, $response->getStatusCode()); + + $info = $response->getInfo(); + $this->assertSame('HTTP/1.1 200 OK', $info['response_headers'][0]); + $this->assertSame('Host: localhost:8057', $info['response_headers'][1]); + + $headers = $response->getHeaders(); + + $this->assertSame('localhost:8057', $headers['host'][0]); + $this->assertSame(['application/json'], $headers['content-type']); + $this->assertTrue(0 < $headers['content-length'][0]); + + $this->assertSame('', $response->getContent()); + } + public function testNonBufferedGetRequest() { $client = $this->getHttpClient(__FUNCTION__); @@ -233,6 +258,18 @@ public function testBadRequestBody() $response->getStatusCode(); } + public function test304() + { + $client = $this->getHttpClient(__FUNCTION__); + $response = $client->request('GET', 'http://localhost:8057/304', [ + 'headers' => ['If-Match' => '"abc"'], + 'buffer' => false, + ]); + + $this->assertSame(304, $response->getStatusCode()); + $this->assertSame('', $response->getContent(false)); + } + public function testRedirects() { $client = $this->getHttpClient(__FUNCTION__); @@ -597,9 +634,9 @@ public function testNotATimeout() { $client = $this->getHttpClient(__FUNCTION__); $response = $client->request('GET', 'http://localhost:8057/timeout-header', [ - 'timeout' => 0.5, + 'timeout' => 0.9, ]); - usleep(510000); + sleep(1); $this->assertSame(200, $response->getStatusCode()); } @@ -669,7 +706,7 @@ public function testDestruct() $duration = microtime(true) - $start; $this->assertGreaterThan(1, $duration); - $this->assertLessThan(3, $duration); + $this->assertLessThan(4, $duration); } public function testProxy() diff --git a/src/Symfony/Contracts/Service/Test/ServiceLocatorTest.php b/src/Symfony/Contracts/Service/Test/ServiceLocatorTest.php index 408c017c3ee40..5ed9149529655 100644 --- a/src/Symfony/Contracts/Service/Test/ServiceLocatorTest.php +++ b/src/Symfony/Contracts/Service/Test/ServiceLocatorTest.php @@ -15,9 +15,9 @@ use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\ServiceLocatorTrait; -class ServiceLocatorTest extends TestCase +abstract class ServiceLocatorTest extends TestCase { - public function getServiceLocator(array $factories) + protected function getServiceLocator(array $factories) { return new class($factories) implements ContainerInterface { use ServiceLocatorTrait;