diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index c9bd72085e803..42d2f02fbd24f 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -44,11 +44,6 @@ jobs: with: fetch-depth: 2 - - name: Configure for PHP >= 8.2 - if: "matrix.php >= '8.2'" - run: | - composer config platform.php 8.1.99 - - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -70,7 +65,7 @@ jobs: echo COLUMNS=120 >> $GITHUB_ENV echo PHPUNIT="$(pwd)/phpunit --exclude-group tty,benchmark,intl-data,integration" >> $GITHUB_ENV - echo COMPOSER_UP='composer update --no-progress --ansi' >> $GITHUB_ENV + echo COMPOSER_UP='composer update --no-progress --ansi'$([[ "${{ matrix.php }}" = "8.2" ]] && echo ' --ignore-platform-req=php+') >> $GITHUB_ENV SYMFONY_VERSIONS=$(git ls-remote -q --heads | cut -f2 | grep -o '/[1-9][0-9]*\.[0-9].*' | sort -V) SYMFONY_VERSION=$(grep ' VERSION = ' src/Symfony/Component/HttpKernel/Kernel.php | cut -d "'" -f2 | cut -d '.' -f 1-2) diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md index b3498d37227df..9c685daa837a5 100644 --- a/CHANGELOG-4.4.md +++ b/CHANGELOG-4.4.md @@ -7,6 +7,22 @@ in 4.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.4.0...v4.4.1 +* 4.4.46 (2022-09-30) + + * bug #47547 [Ldap] Do not run ldap_set_option on failed connection (tatankat) + * bug #47578 [Security] Fix AbstractFormLoginAuthenticator return types (AndrolGenhald) + * bug #47614 [FrameworkBundle] Fix a phpdoc in mailer assertions (HeahDude) + * bug #47516 [HttpFoundation] Prevent BinaryFileResponse::prepare from adding content type if no content is sent (naitsirch) + * bug #47533 [Messenger] decode URL-encoded characters in DSN's usernames/passwords (xabbuh) + * bug #47530 [HttpFoundation] Always return strings from accept headers (ausi) + * bug #47497 [Bridge] Fix mkdir() race condition in ProxyCacheWarmer (andrey-tech) + * bug #47415 [HttpClient] Psr18Client ignore invalid HTTP headers (nuryagdym) + * bug #47435 [HttpKernel] lock when writting profiles (nicolas-grekas) + * bug #47437 [Mime] Fix email rendering when having inlined parts that are not related to the content (fabpot) + * bug #47434 [HttpFoundation] move flushing outside of Response::closeOutputBuffers (nicolas-grekas) + * bug #47351 [FrameworkBundle] Do not throw when describing a factory definition (MatTheCat) + * bug #47403 [Mailer] Fix edge cases in STMP transports (fabpot) + * 4.4.45 (2022-08-26) * bug #47358 Fix broken request stack state if throwable is thrown. (Warxcell) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 167d559dc9e9f..d652d3de50a5c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -16,15 +16,15 @@ The Symfony Connect username in parenthesis allows to get more information - Grégoire Pineau (lyrixx) - Wouter de Jong (wouterj) - Maxime Steinhausser (ogizanagi) - - Christophe Coevoet (stof) - Kévin Dunglas (dunglas) + - Christophe Coevoet (stof) - Jordi Boggiano (seldaek) - Roland Franssen (ro0) - Victor Berchet (victor) - Yonel Ceruto (yonelceruto) - Tobias Nyholm (tobias) - - Oskar Stark (oskarstark) - Javier Eguiluz (javier.eguiluz) + - Oskar Stark (oskarstark) - Ryan Weaver (weaverryan) - Johannes S (johannes) - Jakub Zalas (jakubzalas) @@ -52,8 +52,8 @@ The Symfony Connect username in parenthesis allows to get more information - HypeMC (hypemc) - Matthias Pigulla (mpdude) - Laurent VOULLEMIER (lvo) - - Pierre du Plessis (pierredup) - Antoine Makdessi (amakdessi) + - Pierre du Plessis (pierredup) - Grégoire Paris (greg0ire) - Gabriel Ostrolucký (gadelat) - Jonathan Wage (jwage) @@ -76,14 +76,14 @@ The Symfony Connect username in parenthesis allows to get more information - Bulat Shakirzyanov (avalanche123) - Iltar van der Berg - Miha Vrhovnik (mvrhov) - - Saša Stamenković (umpirsky) - Mathieu Piot (mpiot) + - Saša Stamenković (umpirsky) - Alex Pott - Guilhem N (guilhemn) + - Vincent Langlet (deviling) - Vladimir Reznichenko (kalessil) - Sarah Khalil (saro0h) - Konstantin Kudryashov (everzet) - - Vincent Langlet (deviling) - Tomas Norkūnas (norkunas) - Bilal Amarni (bamarni) - Eriksen Costa @@ -115,6 +115,7 @@ The Symfony Connect username in parenthesis allows to get more information - Maxime Helias (maxhelias) - Ener-Getick - Ruud Kamphuis (ruudk) + - Mathieu Lechat (mat_the_cat) - Sebastiaan Stok (sstok) - Jérôme Vasseur (jvasseur) - Ion Bazan (ionbazan) @@ -126,6 +127,7 @@ The Symfony Connect username in parenthesis allows to get more information - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - Smaine Milianni (ismail1432) + - Antoine Lamirault - John Wards (johnwards) - Dariusz Ruminski - Lars Strojny (lstrojny) @@ -134,7 +136,6 @@ The Symfony Connect username in parenthesis allows to get more information - Konstantin.Myakshin - Rokas Mikalkėnas (rokasm) - Arman Hosseini (arman) - - Antoine Lamirault - Arnaud Le Blanc (arnaud-lb) - Maxime STEINHAUSSER - Peter Kokot (maastermedia) @@ -147,7 +148,6 @@ The Symfony Connect username in parenthesis allows to get more information - YaFou - Gary PEGEOT (gary-p) - Chris Wilkinson (thewilkybarkid) - - Mathieu Lechat (mat_the_cat) - Brice BERNARD (brikou) - Roman Martinuk (a2a4) - Gregor Harlan (gharlan) @@ -159,13 +159,14 @@ The Symfony Connect username in parenthesis allows to get more information - Jesse Rushlow (geeshoe) - Théo FIDRY - jeremyFreeAgent (jeremyfreeagent) + - Michael Babker (mbabker) - Włodzimierz Gajda (gajdaw) - Christian Scheb - Guillaume (guill) - Tugdual Saunier (tucksaun) - Jacob Dreesen (jdreesen) + - Jeroen Spee (jeroens) - Joel Wurtz (brouznouf) - - Michael Babker (mbabker) - Olivier Dolbeau (odolbeau) - Florian Voutzinos (florianv) - zairig imad (zairigimad) @@ -173,7 +174,6 @@ The Symfony Connect username in parenthesis allows to get more information - Javier Spagnoletti (phansys) - excelwebzone - Jérôme Parmentier (lctrs) - - Jeroen Spee (jeroens) - HeahDude - Richard van Laak (rvanlaak) - Paráda József (paradajozsef) @@ -181,6 +181,7 @@ The Symfony Connect username in parenthesis allows to get more information - Alexander Schwenn (xelaris) - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) + - François-Xavier de Guillebon (de-gui_f) - Christopher Hertel (chertel) - Gabriel Caruso - Anthony GRASSIOT (antograssiot) @@ -195,6 +196,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tigran Azatyan (tigranazatyan) - Eric GELOEN (gelo) - Matthieu Napoli (mnapoli) + - Andreas Schempp (aschempp) - Tomáš Votruba (tomas_votruba) - Joshua Thijssen - Stefano Sala (stefano.sala) @@ -211,13 +213,12 @@ The Symfony Connect username in parenthesis allows to get more information - Timo Bakx (timobakx) - Juti Noppornpitak (shiroyuki) - Joe Bennett (kralos) + - Nate Wiebe (natewiebe13) - Hugo Alliaume (kocal) - Anthony MARTIN - Colin O'Dell (colinodell) - Sebastian Hörl (blogsh) - Ben Davies (bendavies) - - Andreas Schempp (aschempp) - - François-Xavier de Guillebon (de-gui_f) - Daniel Gomes (danielcsgomes) - Michael Käfer (michael_kaefer) - Hidenori Goto (hidenorigoto) @@ -226,12 +227,12 @@ The Symfony Connect username in parenthesis allows to get more information - Guilherme Blanco (guilhermeblanco) - Chi-teck - Antonio Pauletich (x-coder264) - - Nate Wiebe (natewiebe13) - Michael Voříšek - SpacePossum - Pablo Godel (pgodel) - Romaric Drigon (romaricdrigon) - Andréia Bohner (andreia) + - Dāvis Zālītis (k0d3r1s) - Jannik Zschiesche - Rafael Dohms (rdohms) - George Mponos (gmponos) @@ -257,7 +258,6 @@ The Symfony Connect username in parenthesis allows to get more information - Justin Hileman (bobthecow) - Niels Keurentjes (curry684) - Vyacheslav Pavlov - - Dāvis Zālītis (k0d3r1s) - Richard Shank (iampersistent) - Andre Rømcke (andrerom) - Dmitrii Poddubnyi (karser) @@ -314,6 +314,7 @@ The Symfony Connect username in parenthesis allows to get more information - Wojciech Kania - Alexey Kopytko (sanmai) - Sergey Linnik (linniksa) + - Warxcell (warxcell) - Richard Miller - Leo Feyer (leofeyer) - Mario A. Alvarez Garcia (nomack84) @@ -346,6 +347,7 @@ The Symfony Connect username in parenthesis allows to get more information - Loick Piera (pyrech) - Vitalii Ekert (comrade42) - Clara van Miert + - Martin Auswöger - Alexander Menshchikov - Stepan Anchugov (kix) - bronze1man @@ -358,8 +360,8 @@ The Symfony Connect username in parenthesis allows to get more information - Edi Modrić (emodric) - Philipp Wahala (hifi) - Nikolay Labinskiy (e-moe) - - Warxcell (warxcell) - Martin Schuhfuß (usefulthink) + - Phil Taylor (prazgod) - apetitpa - Vladyslav Loboda - Pierre Minnieur (pminnieur) @@ -385,7 +387,6 @@ The Symfony Connect username in parenthesis allows to get more information - Markus Fasselt (digilist) - Maxime Veber (nek-) - Marcin Sikoń (marphi) - - Martin Auswöger - Sullivan SENECHAL (soullivaneuh) - Rui Marinho (ruimarinho) - Marc Weistroff (futurecat) @@ -404,7 +405,6 @@ The Symfony Connect username in parenthesis allows to get more information - Alex Bowers - Simon Podlipsky (simpod) - Marcel Beerta (mazen) - - Phil Taylor (prazgod) - flack (flack) - Craig Duncan (duncan3dc) - Mantis Development @@ -500,6 +500,7 @@ The Symfony Connect username in parenthesis allows to get more information - Chris Tanaskoski - julien57 - Loïc Frémont (loic425) + - Ippei Sumida (ippey_s) - Ben Ramsey (ramsey) - Matthieu Auger (matthieuauger) - Kévin THERAGE (kevin_therage) @@ -536,6 +537,7 @@ The Symfony Connect username in parenthesis allows to get more information - Soufian EZ ZANTAR (soezz) - Jan van Thoor (janvt) - Martin Kirilov (wucdbm) + - Axel Guckelsberger (guite) - Chris Smith (cs278) - Florian Klein (docteurklein) - Bilge @@ -561,6 +563,7 @@ The Symfony Connect username in parenthesis allows to get more information - Yannick Ihmels (ihmels) - Saif Eddin G - Emmanuel BORGES (eborges78) + - siganushka (siganushka) - Aurelijus Valeiša (aurelijus) - Evert Harmeling (evertharmeling) - Jan Decavele (jandc) @@ -633,7 +636,6 @@ The Symfony Connect username in parenthesis allows to get more information - Andy Palmer (andyexeter) - Marko H. Tamminen (gzumba) - Francesco Levorato - - Ippei Sumida (ippey_s) - DerManoMann - David Molineus - Desjardins Jérôme (jewome62) @@ -708,7 +710,6 @@ The Symfony Connect username in parenthesis allows to get more information - Steve Grunwell - - Alex (aik099) - - Axel Guckelsberger (guite) - Greg Thornton (xdissent) - BENOIT POLASZEK (bpolaszek) - Shaharia Azam @@ -771,7 +772,6 @@ The Symfony Connect username in parenthesis allows to get more information - Shakhobiddin - Kai - Lee Rowlands - - siganushka (siganushka) - Alain Hippolyte (aloneh) - Karoly Negyesi (chx) - Xavier HAUSHERR @@ -903,6 +903,7 @@ The Symfony Connect username in parenthesis allows to get more information - Damien Fa - Kevin McBride - Sergio Santoro + - AndrolGenhald - Philipp Rieber (bicpi) - Dennis Væversted (srnzitcom) - Manuel de Ruiter (manuel) @@ -917,6 +918,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jon Dufresne - Chad Sikorra (chadsikorra) - Mathias Brodala (mbrodala) + - naitsirch (naitsirch) - Evan S Kaufman (evanskaufman) - Jonathan Sui Lioung Lee Slew (jlslew) - mcben @@ -989,6 +991,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ziumin - Matthias Schmidt - Lenar Lõhmus + - Ilija Tovilo (ilijatovilo) - Samaël Villette (samadu61) - Zach Badgett (zachbadgett) - Loïc Faugeron @@ -1001,6 +1004,7 @@ The Symfony Connect username in parenthesis allows to get more information - Philipp Keck - Disquedur - Markus S. (staabm) + - Guilherme Ferreira - Geoffrey Tran (geoff) - Elan Ruusamäe (glen) - Brad Jones @@ -1050,6 +1054,7 @@ The Symfony Connect username in parenthesis allows to get more information - Safonov Nikita (ns3777k) - Simon DELICATA - Thibault Buathier (gwemox) + - Julien Boudry - vitaliytv - Andreas Hennings - Arnaud Frézet @@ -1074,6 +1079,7 @@ The Symfony Connect username in parenthesis allows to get more information - pizzaminded - Matthieu Calie (matth--) - Stéphane Escandell (sescandell) + - Philippe SEGATORI (tigitz) - ivan - linh - Oleg Krasavin (okwinza) @@ -1354,7 +1360,6 @@ The Symfony Connect username in parenthesis allows to get more information - Toni Peric (tperic) - yclian - Aleksey Prilipko - - AndrolGenhald - Andrew Berry - Wybren Koelmans (wybren_koelmans) - Dmytro Dzubenko @@ -1432,6 +1437,7 @@ The Symfony Connect username in parenthesis allows to get more information - Dennis Hotson - Andrew Tchircoff (andrewtch) - Lars Vierbergen (vierbergenlars) + - Barney Hanlon - Bart Wach - Jos Elstgeest - Kirill Lazarev @@ -1568,6 +1574,7 @@ The Symfony Connect username in parenthesis allows to get more information - Gennadi Janzen - SenTisso - Joe Springe + - Ivan Kurnosov - Flinsch - botbotbot - Timon van der Vorm @@ -1675,7 +1682,6 @@ The Symfony Connect username in parenthesis allows to get more information - Cyrille Bourgois (cyrilleb) - Gerard van Helden (drm) - Johnny Peck (johnnypeck) - - naitsirch (naitsirch) - Geoffrey Monte (numerogeek) - Martijn Boers (plebian) - Plamen Mishev (pmishev) @@ -1969,6 +1975,7 @@ The Symfony Connect username in parenthesis allows to get more information - Kirill Nesmeyanov (serafim) - Reece Fowell (reecefowell) - Guillaume Gammelin + - wuchen90 - Valérian Galliat - d-ph - Renan Taranto (renan-taranto) @@ -1982,7 +1989,6 @@ The Symfony Connect username in parenthesis allows to get more information - Mikkel Paulson - ergiegonzaga - Liverbool (liverbool) - - Julien Boudry - Dalibor Karlović - Sam Malone - Ha Phan (haphan) @@ -2083,8 +2089,10 @@ The Symfony Connect username in parenthesis allows to get more information - Troy McCabe - Ville Mattila - gr1ev0us + - Léo VINCENT - mlazovla - Max Beutel + - Nathan Sepulveda - Antanas Arvasevicius - Pierre Dudoret - Michal Trojanowski @@ -2187,6 +2195,7 @@ The Symfony Connect username in parenthesis allows to get more information - Vladimir Luchaninov (luchaninov) - spdionis - rchoquet + - v.shevelev - gitlost - Taras Girnyk - Sergio @@ -2201,6 +2210,7 @@ The Symfony Connect username in parenthesis allows to get more information - Gilles Doge (gido) - abulford - Philipp Kretzschmar + - Jairo Pastor - Ilya Vertakov - Brooks Boyd - Axel Venet @@ -2252,6 +2262,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sergey Yuferev - Tobias Stöckler - Mario Young + - martkop26 - Sander Hagen - Ilia (aliance) - cilefen (cilefen) @@ -2356,6 +2367,7 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel Richter (richtermeister) - Sandro Hopf (senaria) - ChrisC + - jack.shpartko - Kim Laï Trinh - Jason Desrosiers - m.chwedziak @@ -2401,6 +2413,7 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel González Zaballos (dem3trio) - Emmanuel Vella (emmanuel.vella) - Guillaume BRETOU (guiguiboy) + - nuryagdy mustapayev (nueron) - Carsten Nielsen (phreaknerd) - Jay Severson - René Kerner @@ -2614,6 +2627,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jesper Noordsij - DerStoffel - Maciej Schmidt + - tatankat - nuncanada - Thierry Marianne - František Bereň @@ -2626,6 +2640,7 @@ The Symfony Connect username in parenthesis allows to get more information - Kamil Madejski (kmadejski) - Nicolas Tallefourtané (nicolab) - Botond Dani (picur) + - Radek Wionczek (rwionczek) - Nick Stemerdink - David Stone - Grayson Koonce @@ -2694,6 +2709,7 @@ The Symfony Connect username in parenthesis allows to get more information - Artem Lopata (bumz) - alex - Roman Orlov + - Andreas Allacher - VolCh - Alexey Popkov - Gijs Kunze @@ -2779,18 +2795,21 @@ The Symfony Connect username in parenthesis allows to get more information - Alexander Zogheb - Rémi Blaise - Nicolas Séverin + - Houssem - Joel Marcey - zolikonta - David Christmann - root - pf - Vincent Chalnot + - Patrizio Bekerle - Tom Maguire - Mateusz Lerczak - Richard Quadling - David Zuelke - Adrian - neFAST + - Peter Gribanov - Pierre Rineau - Florian Morello - Maxim Lovchikov @@ -2840,7 +2859,6 @@ The Symfony Connect username in parenthesis allows to get more information - David Windell - Gabriel Birke - Derek Bonner - - Ivan Kurnosov - martijn - annesosensio - NothingWeAre @@ -3049,6 +3067,7 @@ The Symfony Connect username in parenthesis allows to get more information - Neophy7e - bokonet - Arrilot + - andrey-tech - Shaun Simmons - Markus Staab - Pierre-Louis LAUNAY @@ -3120,6 +3139,7 @@ The Symfony Connect username in parenthesis allows to get more information - Lin Lu - arduanov - sualko + - Martin Komischke - ADmad - Nicolas Roudaire - Abdouni Karim (abdounikarim) @@ -3154,7 +3174,6 @@ The Symfony Connect username in parenthesis allows to get more information - Gerry Vandermaesen (gerryvdm) - Arash Tabrizian (ghost098) - Vladislav Krupenkin (ideea) - - Ilija Tovilo (ilijatovilo) - Peter Orosz (ill_logical) - Imangazaliev Muhammad (imangazaliev) - j0k (j0k) diff --git a/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php b/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php index 24cf25be59f3d..08f9fef880e51 100644 --- a/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php +++ b/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php @@ -49,7 +49,7 @@ public function warmUp($cacheDir) foreach ($this->registry->getManagers() as $em) { // we need the directory no matter the proxy cache generation strategy if (!is_dir($proxyCacheDir = $em->getConfiguration()->getProxyDir())) { - if (false === @mkdir($proxyCacheDir, 0777, true)) { + if (false === @mkdir($proxyCacheDir, 0777, true) && !is_dir($proxyCacheDir)) { throw new \RuntimeException(sprintf('Unable to create the Doctrine Proxy directory "%s".', $proxyCacheDir)); } } elseif (!is_writable($proxyCacheDir)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 64841b1a25d4e..3af3ec03f437a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -219,7 +219,7 @@ private function getContainerDefinitionData(Definition $definition, bool $omitTa if ($factory[0] instanceof Reference) { $data['factory_service'] = (string) $factory[0]; } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $data['factory_service'] = sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured'); } else { $data['factory_class'] = $factory[0]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index a2360a094ee9a..4d48620236f79 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -196,7 +196,7 @@ protected function describeContainerDefinition(Definition $definition, array $op if ($factory[0] instanceof Reference) { $output .= "\n".'- Factory Service: `'.$factory[0].'`'; } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $output .= "\n".sprintf('- Factory Service: inline factory service (%s)', $factory[0]->getClass() ? sprintf('`%s`', $factory[0]->getClass()) : 'not configured'); } else { $output .= "\n".'- Factory Class: `'.$factory[0].'`'; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 67669e5a3c3cc..5163a8730795b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -309,7 +309,7 @@ protected function describeContainerDefinition(Definition $definition, array $op if ($factory[0] instanceof Reference) { $tableRows[] = ['Factory Service', $factory[0]]; } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $tableRows[] = ['Factory Service', sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured')]; } else { $tableRows[] = ['Factory Class', $factory[0]]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 65e3dbc17b077..28044126f9a7f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -294,7 +294,7 @@ private function getContainerDefinitionDocument(Definition $definition, string $ if ($factory[0] instanceof Reference) { $factoryXML->setAttribute('service', (string) $factory[0]); } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $factoryXML->setAttribute('service', sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'not configured')); } else { $factoryXML->setAttribute('class', $factory[0]); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php index d0ca84e25ae7a..fe20731a78a7b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php @@ -91,7 +91,7 @@ public static function assertEmailAddressContains(RawMessage $email, string $hea } /** - * @return MessageEvents[] + * @return MessageEvent[] */ public static function getMailerEvents(string $transport = null): array { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php index a097e058be7dd..4d96e90f0ef27 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php @@ -123,6 +123,7 @@ public static function getContainerDefinitions() { $definition1 = new Definition('Full\\Qualified\\Class1'); $definition2 = new Definition('Full\\Qualified\\Class2'); + $definition3 = new Definition('Full\\Qualified\\Class3'); return [ 'definition_1' => $definition1 @@ -154,6 +155,9 @@ public static function getContainerDefinitions() ->addTag('tag2') ->addMethodCall('setMailer', [new Reference('mailer')]) ->setFactory([new Reference('factory.service'), 'get']), + '.definition_3' => $definition3 + ->setFile('/path/to/file') + ->setFactory([new Definition('Full\\Qualified\\FactoryClass'), 'get']), 'definition_without_class' => new Definition(), ]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json index 0eda1932f7a15..401c588c03d42 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json @@ -34,6 +34,20 @@ "parameters": [] } ] + }, + ".definition_3": { + "class": "Full\\Qualified\\Class3", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "inline factory service (Full\\Qualified\\FactoryClass)", + "factory_method": "get", + "tags": [] } }, "aliases": { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md index 2d0edfd01952e..f8667daa86793 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md @@ -25,6 +25,20 @@ Definitions - Attr3: val3 - Tag: `tag2` +### .definition_3 + +- Class: `Full\Qualified\Class3` +- Public: no +- Synthetic: no +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: inline factory service (`Full\Qualified\FactoryClass`) +- Factory Method: `get` + Aliases ------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt index 82b4909242d84..daf47ddc39187 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt @@ -7,5 +7,6 @@ --------------- ------------------------ .alias_2 alias for ".service_2" .definition_2 Full\Qualified\Class2 + .definition_3 Full\Qualified\Class3 --------------- ------------------------ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml index a311a2e2bb991..b9416fd069d05 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml @@ -17,4 +17,7 @@ + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.json new file mode 100644 index 0000000000000..4bf56746493f8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.json @@ -0,0 +1,14 @@ +{ + "class": "Full\\Qualified\\Class3", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "inline factory service (Full\\Qualified\\FactoryClass)", + "factory_method": "get", + "tags": [] +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.md new file mode 100644 index 0000000000000..68f51634db99f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.md @@ -0,0 +1,11 @@ +- Class: `Full\Qualified\Class3` +- Public: no +- Synthetic: no +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: inline factory service (`Full\Qualified\FactoryClass`) +- Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.txt new file mode 100644 index 0000000000000..35ddaf3e452a8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.txt @@ -0,0 +1,18 @@ + ----------------- ------------------------------------------------------ +  Option   Value  + ----------------- ------------------------------------------------------ + Service ID - + Class Full\Qualified\Class3 + Tags - + Public no + Synthetic no + Lazy no + Shared yes + Abstract no + Autowired no + Autoconfigured no + Required File /path/to/file + Factory Service inline factory service (Full\Qualified\FactoryClass) + Factory Method get + ----------------- ------------------------------------------------------ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.xml new file mode 100644 index 0000000000000..e81c77014253f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.json new file mode 100644 index 0000000000000..94c2fda5402fc --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.json @@ -0,0 +1,15 @@ +{ + "class": "Full\\Qualified\\Class3", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [], + "file": "\/path\/to\/file", + "factory_service": "inline factory service (Full\\Qualified\\FactoryClass)", + "factory_method": "get", + "tags": [] +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.md new file mode 100644 index 0000000000000..2ce1f264dfc6c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.md @@ -0,0 +1,12 @@ +- Class: `Full\Qualified\Class3` +- Public: no +- Synthetic: no +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- Arguments: no +- File: `/path/to/file` +- Factory Service: inline factory service (`Full\Qualified\FactoryClass`) +- Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.txt new file mode 100644 index 0000000000000..6e400de44e8ff --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.txt @@ -0,0 +1,18 @@ + ----------------- ------------------------------------------------------ +  Option   Value  + ----------------- ------------------------------------------------------ + Service ID - + Class Full\Qualified\Class3 + Tags - + Public no + Synthetic no + Lazy no + Shared yes + Abstract no + Autowired no + Autoconfigured no + Required File /path/to/file + Factory Service inline factory service (Full\Qualified\FactoryClass) + Factory Method get + ----------------- ------------------------------------------------------ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.xml new file mode 100644 index 0000000000000..e81c77014253f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php index e7ce0ec218976..d752cd4c2f13d 100644 --- a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php +++ b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php @@ -46,7 +46,7 @@ public function applyVersion($path) { $versionized = sprintf($this->format, ltrim($path, '/'), $this->getVersion($path)); - if ($path && '/' == $path[0]) { + if ($path && '/' === $path[0]) { return '/'.$versionized; } diff --git a/src/Symfony/Component/Cache/README.md b/src/Symfony/Component/Cache/README.md index 74052052c8c33..c466d57883c2f 100644 --- a/src/Symfony/Component/Cache/README.md +++ b/src/Symfony/Component/Cache/README.md @@ -1,13 +1,13 @@ Symfony PSR-6 implementation for caching ======================================== -The Cache component provides an extended -[PSR-6](http://www.php-fig.org/psr/psr-6/) implementation for adding cache to +The Cache component provides extended +[PSR-6](https://www.php-fig.org/psr/psr-6/) implementations for adding cache to your applications. It is designed to have a low overhead so that caching is -fastest. It ships with a few caching adapters for the most widespread and -suited to caching backends. It also provides a `doctrine/cache` proxy adapter -to cover more advanced caching needs and a proxy adapter for greater -interoperability between PSR-6 implementations. +fastest. It ships with adapters for the most widespread caching backends. +It also provides a [PSR-16](https://www.php-fig.org/psr/psr-16/) adapter, +and implementations for [symfony/cache-contracts](https://github.com/symfony/cache-contracts)' +`CacheInterface` and `TagAwareCacheInterface`. Resources --------- diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index 6ef5eac991d20..7a9e8df5a40e4 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -1,7 +1,7 @@ { "name": "symfony/cache", "type": "library", - "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation", + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", "keywords": ["caching", "psr6"], "homepage": "https://symfony.com", "license": "MIT", diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php index 51d42494d1def..bae85a86fbec5 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php @@ -121,7 +121,7 @@ public function reverseTransform($value) $outputTz = new \DateTimeZone($this->outputTimezone); $dateTime = \DateTime::createFromFormat($this->parseFormat, $value, $outputTz); - $lastErrors = \DateTime::getLastErrors(); + $lastErrors = \DateTime::getLastErrors() ?: ['error_count' => 0, 'warning_count' => 0]; if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) { throw new TransformationFailedException(implode(', ', array_merge(array_values($lastErrors['warnings']), array_values($lastErrors['errors'])))); diff --git a/src/Symfony/Component/HttpClient/Psr18Client.php b/src/Symfony/Component/HttpClient/Psr18Client.php index 67c2fdb8f07bc..7f79af16426a1 100644 --- a/src/Symfony/Component/HttpClient/Psr18Client.php +++ b/src/Symfony/Component/HttpClient/Psr18Client.php @@ -100,7 +100,11 @@ public function sendRequest(RequestInterface $request): ResponseInterface foreach ($response->getHeaders(false) as $name => $values) { foreach ($values as $value) { - $psrResponse = $psrResponse->withAddedHeader($name, $value); + try { + $psrResponse = $psrResponse->withAddedHeader($name, $value); + } catch (\InvalidArgumentException $e) { + // ignore invalid header + } } } diff --git a/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php b/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php index 1ef36fc5bd09e..366d555ae03f9 100644 --- a/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php @@ -13,10 +13,12 @@ use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\NativeHttpClient; use Symfony\Component\HttpClient\Psr18Client; use Symfony\Component\HttpClient\Psr18NetworkException; use Symfony\Component\HttpClient\Psr18RequestException; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Contracts\HttpClient\Test\TestHttpServer; class Psr18ClientTest extends TestCase @@ -81,4 +83,22 @@ public function test404() $response = $client->sendRequest($factory->createRequest('GET', 'http://localhost:8057/404')); $this->assertSame(404, $response->getStatusCode()); } + + public function testInvalidHeaderResponse() + { + $responseHeaders = [ + // space in header name not allowed in RFC 7230 + ' X-XSS-Protection' => '0', + 'Cache-Control' => 'no-cache', + ]; + $response = new MockResponse('body', ['response_headers' => $responseHeaders]); + $this->assertArrayHasKey(' x-xss-protection', $response->getHeaders()); + + $client = new Psr18Client(new MockHttpClient($response)); + $request = $client->createRequest('POST', 'http://localhost:8057/post') + ->withBody($client->createStream('foo=0123456789')); + + $resultResponse = $client->sendRequest($request); + $this->assertCount(1, $resultResponse->getHeaders()); + } } diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index f44eb6daf44e9..2cd105c3bb0f3 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -201,15 +201,17 @@ public function setContentDisposition($disposition, $filename = '', $filenameFal */ public function prepare(Request $request) { - if (!$this->headers->has('Content-Type')) { - $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream'); - } + parent::prepare($request); - if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) { - $this->setProtocolVersion('1.1'); + if ($this->isInformational() || $this->isEmpty()) { + $this->maxlen = 0; + + return $this; } - $this->ensureIEOverSSLCompatibility($request); + if (!$this->headers->has('Content-Type')) { + $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream'); + } $this->offset = 0; $this->maxlen = -1; @@ -217,6 +219,7 @@ public function prepare(Request $request) if (false === $fileSize = $this->file->getSize()) { return $this; } + $this->headers->remove('Transfer-Encoding'); $this->headers->set('Content-Length', $fileSize); if (!$this->headers->has('Accept-Ranges')) { @@ -286,6 +289,10 @@ public function prepare(Request $request) } } + if ($request->isMethod('HEAD')) { + $this->maxlen = 0; + } + return $this; } @@ -309,40 +316,42 @@ private function hasValidIfRangeHeader(?string $header): bool */ public function sendContent() { - if (!$this->isSuccessful()) { - return parent::sendContent(); - } + try { + if (!$this->isSuccessful()) { + return parent::sendContent(); + } - if (0 === $this->maxlen) { - return $this; - } + if (0 === $this->maxlen) { + return $this; + } - $out = fopen('php://output', 'w'); - $file = fopen($this->file->getPathname(), 'r'); + $out = fopen('php://output', 'w'); + $file = fopen($this->file->getPathname(), 'r'); - ignore_user_abort(true); + ignore_user_abort(true); - if (0 !== $this->offset) { - fseek($file, $this->offset); - } + if (0 !== $this->offset) { + fseek($file, $this->offset); + } - $length = $this->maxlen; - while ($length && !feof($file)) { - $read = ($length > $this->chunkSize) ? $this->chunkSize : $length; - $length -= $read; + $length = $this->maxlen; + while ($length && !feof($file)) { + $read = ($length > $this->chunkSize) ? $this->chunkSize : $length; + $length -= $read; - stream_copy_to_stream($file, $out, $read); + stream_copy_to_stream($file, $out, $read); - if (connection_aborted()) { - break; + if (connection_aborted()) { + break; + } } - } - fclose($out); - fclose($file); - - if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) { - unlink($this->file->getPathname()); + fclose($out); + fclose($file); + } finally { + if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) { + unlink($this->file->getPathname()); + } } return $this; diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 4b2c4d96752c4..32e07b532e379 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1646,7 +1646,8 @@ public function getLanguages() $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); $this->languages = []; - foreach ($languages as $lang => $acceptHeaderItem) { + foreach ($languages as $acceptHeaderItem) { + $lang = $acceptHeaderItem->getValue(); if (str_contains($lang, '-')) { $codes = explode('-', $lang); if ('i' === $codes[0]) { @@ -1684,7 +1685,7 @@ public function getCharsets() return $this->charsets; } - return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()); + return $this->charsets = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all())); } /** @@ -1698,7 +1699,7 @@ public function getEncodings() return $this->encodings; } - return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all()); + return $this->encodings = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all())); } /** @@ -1712,7 +1713,7 @@ public function getAcceptableContentTypes() return $this->acceptableContentTypes; } - return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()); + return $this->acceptableContentTypes = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all())); } /** diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index c14a7bbfdfcb5..75f64fa9eb907 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -384,6 +384,7 @@ public function send() fastcgi_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); + flush(); } return $this; @@ -1236,7 +1237,6 @@ public static function closeOutputBuffers(int $targetLevel, bool $flush): void while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) { if ($flush) { ob_end_flush(); - flush(); } else { ob_end_clean(); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php index f88c8a48df1aa..4e4ddbf4446ef 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php @@ -373,6 +373,21 @@ public function testStream() $this->assertNull($response->headers->get('Content-Length')); } + public function testPrepareNotAddingContentTypeHeaderIfNoContentResponse() + { + $request = Request::create('/'); + $request->headers->set('If-Modified-Since', date('D, d M Y H:i:s').' GMT'); + + $response = new BinaryFileResponse(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']); + $response->setLastModified(new \DateTimeImmutable('-1 day')); + $response->isNotModified($request); + + $response->prepare($request); + + $this->assertSame(BinaryFileResponse::HTTP_NOT_MODIFIED, $response->getStatusCode()); + $this->assertFalse($response->headers->has('Content-Type')); + } + protected function provideResponse() { return new BinaryFileResponse(__DIR__.'/../README.md', 200, ['Content-Type' => 'application/octet-stream']); diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 6035dd5d32da9..060de405e58ae 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1582,6 +1582,20 @@ public function testGetLanguages() $this->assertEquals(['zh', 'cherokee'], $request->getLanguages()); } + public function testGetAcceptHeadersReturnString() + { + $request = new Request(); + $request->headers->set('Accept', '123'); + $request->headers->set('Accept-Charset', '123'); + $request->headers->set('Accept-Encoding', '123'); + $request->headers->set('Accept-Language', '123'); + + $this->assertSame(['123'], $request->getAcceptableContentTypes()); + $this->assertSame(['123'], $request->getCharsets()); + $this->assertSame(['123'], $request->getEncodings()); + $this->assertSame(['123'], $request->getLanguages()); + } + public function testGetRequestFormat() { $request = new Request(); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index adb9a94727c5e..c6476b615d29a 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,11 +76,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - public const VERSION = '4.4.45'; - public const VERSION_ID = 40445; + public const VERSION = '4.4.46'; + public const VERSION_ID = 40446; public const MAJOR_VERSION = 4; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 45; + public const RELEASE_VERSION = 46; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2022'; diff --git a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php index d729994c1f0cc..aa494691d1fc1 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php @@ -115,19 +115,7 @@ public function purge() */ public function read($token): ?Profile { - if (!$token || !file_exists($file = $this->getFilename($token))) { - return null; - } - - if (\function_exists('gzcompress')) { - $file = 'compress.zlib://'.$file; - } - - if (!$data = unserialize(file_get_contents($file))) { - return null; - } - - return $this->createProfileFromData($token, $data); + return $this->doRead($token); } /** @@ -169,14 +157,13 @@ public function write(Profile $profile): bool 'status_code' => $profile->getStatusCode(), ]; - $context = stream_context_create(); + $data = serialize($data); - if (\function_exists('gzcompress')) { - $file = 'compress.zlib://'.$file; - stream_context_set_option($context, 'zlib', 'level', 3); + if (\function_exists('gzencode')) { + $data = gzencode($data, 3); } - if (false === file_put_contents($file, serialize($data), 0, $context)) { + if (false === file_put_contents($file, $data, \LOCK_EX)) { return false; } @@ -293,21 +280,34 @@ protected function createProfileFromData($token, $data, $parent = null) } foreach ($data['children'] as $token) { - if (!$token || !file_exists($file = $this->getFilename($token))) { - continue; + if (null !== $childProfile = $this->doRead($token, $profile)) { + $profile->addChild($childProfile); } + } - if (\function_exists('gzcompress')) { - $file = 'compress.zlib://'.$file; - } + return $profile; + } - if (!$childData = unserialize(file_get_contents($file))) { - continue; - } + private function doRead($token, Profile $profile = null): ?Profile + { + if (!$token || !file_exists($file = $this->getFilename($token))) { + return null; + } + + $h = fopen($file, 'r'); + flock($h, \LOCK_SH); + $data = stream_get_contents($h); + flock($h, \LOCK_UN); + fclose($h); - $profile->addChild($this->createProfileFromData($token, $childData, $profile)); + if (\function_exists('gzdecode')) { + $data = @gzdecode($data) ?: $data; } - return $profile; + if (!$data = unserialize($data)) { + return null; + } + + return $this->createProfileFromData($token, $data, $profile); } } diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php index defed4e35a787..9bd9cef35ce35 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php @@ -159,7 +159,11 @@ private function connect() } } - $this->connection = ldap_connect($this->config['connection_string']); + if (false === $connection = ldap_connect($this->config['connection_string'])) { + throw new LdapException('Invalid connection string: '.$this->config['connection_string']); + } else { + $this->connection = $connection; + } foreach ($this->config['options'] as $name => $value) { if (!\in_array(ConnectionOptions::getOption($name), self::PRECONNECT_OPTIONS, true)) { @@ -167,10 +171,6 @@ private function connect() } } - if (false === $this->connection) { - throw new LdapException('Could not connect to Ldap server: '.ldap_error($this->connection)); - } - if ('tls' === $this->config['encryption'] && false === @ldap_start_tls($this->connection)) { throw new LdapException('Could not initiate TLS connection: '.ldap_error($this->connection)); } diff --git a/src/Symfony/Component/Lock/composer.json b/src/Symfony/Component/Lock/composer.json index f7f8f84cc16db..1cd15c22a9e10 100644 --- a/src/Symfony/Component/Lock/composer.json +++ b/src/Symfony/Component/Lock/composer.json @@ -21,11 +21,11 @@ "symfony/polyfill-php80": "^1.16" }, "require-dev": { - "doctrine/dbal": "^2.6|^3.0", + "doctrine/dbal": "^2.7|^3.0", "predis/predis": "~1.0" }, "conflict": { - "doctrine/dbal": "<2.6" + "doctrine/dbal": "<2.7" }, "autoload": { "psr-4": { "Symfony\\Component\\Lock\\": "" }, diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php index 5f8d3ba0d8180..c54b050b92963 100644 --- a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php +++ b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Exception\LogicException; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport; use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream; @@ -133,6 +134,35 @@ public function testWriteEncodedRecipientAndSenderAddresses() $this->assertContains("RCPT TO:\r\n", $stream->getCommands()); $this->assertContains("RCPT TO:\r\n", $stream->getCommands()); } + + public function testAssertResponseCodeNoCodes() + { + $this->expectException(LogicException::class); + $this->invokeAssertResponseCode('response', []); + } + + public function testAssertResponseCodeWithEmptyResponse() + { + $this->expectException(TransportException::class); + $this->expectExceptionMessage('Expected response code "220" but got empty code.'); + $this->invokeAssertResponseCode('', [220]); + } + + public function testAssertResponseCodeWithNotValidCode() + { + $this->expectException(TransportException::class); + $this->expectExceptionMessage('Expected response code "220" but got code "550", with message "550 Access Denied".'); + $this->expectExceptionCode(550); + $this->invokeAssertResponseCode('550 Access Denied', [220]); + } + + private function invokeAssertResponseCode(string $response, array $codes): void + { + $transport = new SmtpTransport($this->getMockForAbstractClass(AbstractStream::class)); + $m = new \ReflectionMethod($transport, 'assertResponseCode'); + $m->setAccessible(true); + $m->invoke($transport, $response, $codes); + } } class DummyStream extends AbstractStream diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php index 25006fbb7d326..da2498aa0dc02 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php @@ -94,7 +94,9 @@ public function addAuthenticator(AuthenticatorInterface $authenticator): void protected function doHeloCommand(): void { - $capabilities = $this->callHeloCommand(); + if (!$capabilities = $this->callHeloCommand()) { + return; + } /** @var SocketStream $stream */ $stream = $this->getStream(); @@ -123,6 +125,8 @@ private function callHeloCommand(): array } catch (TransportExceptionInterface $e) { try { parent::doHeloCommand(); + + return []; } catch (TransportExceptionInterface $ex) { if (!$ex->getCode()) { throw $e; diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php index 3c05e94e376d1..517aa0c31906a 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php @@ -301,7 +301,8 @@ private function assertResponseCode(string $response, array $codes): void if (!$valid || !$response) { $codeStr = $code ? sprintf('code "%s"', $code) : 'empty code'; $responseStr = $response ? sprintf(', with message "%s"', trim($response)) : ''; - throw new TransportException(sprintf('Expected response code "%s" but got ', implode('/', $codes), $codeStr).$codeStr.$responseStr.'.', $code); + + throw new TransportException(sprintf('Expected response code "%s" but got ', implode('/', $codes)).$codeStr.$responseStr.'.', $code ?: 0); } } diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index ab7771b9aedfc..5c27ed9415818 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -230,15 +230,20 @@ private function guessHandledClasses(\ReflectionClass $handlerClass, string $ser if ($type instanceof \ReflectionUnionType) { $types = []; + $invalidTypes = []; foreach ($type->getTypes() as $type) { if (!$type->isBuiltin()) { $types[] = (string) $type; + } else { + $invalidTypes[] = (string) $type; } } if ($types) { return $types; } + + throw new RuntimeException(sprintf('Invalid handler service "%s": type-hint of argument "$%s" in method "%s::__invoke()" must be a class , "%s" given.', $serviceId, $parameters[0]->getName(), $handlerClass->getName(), implode('|', $invalidTypes))); } if ($type->isBuiltin()) { diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index 940c32b374742..82fe774f2469d 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -31,6 +31,7 @@ use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Middleware\MiddlewareInterface; use Symfony\Component\Messenger\Middleware\StackInterface; +use Symfony\Component\Messenger\Tests\Fixtures\ChildDummyMessage; use Symfony\Component\Messenger\Tests\Fixtures\DummyCommand; use Symfony\Component\Messenger\Tests\Fixtures\DummyCommandHandler; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; @@ -39,6 +40,8 @@ use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessage; use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler; use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; +use Symfony\Component\Messenger\Tests\Fixtures\UnionBuiltinTypeArgumentHandler; +use Symfony\Component\Messenger\Tests\Fixtures\UnionTypeArgumentHandler; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; @@ -474,6 +477,43 @@ public function testBuiltinArgumentTypeHandler() (new MessengerPass())->process($container); } + /** + * @requires PHP 8 + */ + public function testUnionTypeArgumentsTypeHandler() + { + $container = $this->getContainerBuilder($busId = 'message_bus'); + $container + ->register(UnionTypeArgumentHandler::class, UnionTypeArgumentHandler::class) + ->addTag('messenger.message_handler') + ; + + (new MessengerPass())->process($container); + + $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); + + $this->assertArrayHasKey(ChildDummyMessage::class, $handlersMapping); + $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); + $this->assertHandlerDescriptor($container, $handlersMapping, ChildDummyMessage::class, [UnionTypeArgumentHandler::class]); + $this->assertHandlerDescriptor($container, $handlersMapping, DummyMessage::class, [UnionTypeArgumentHandler::class]); + } + + /** + * @requires PHP 8 + */ + public function testUnionBuiltinArgumentTypeHandler() + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage(sprintf('Invalid handler service "%s": type-hint of argument "$message" in method "%s::__invoke()" must be a class , "string|int" given.', UnionBuiltinTypeArgumentHandler::class, UnionBuiltinTypeArgumentHandler::class)); + $container = $this->getContainerBuilder(); + $container + ->register(UnionBuiltinTypeArgumentHandler::class, UnionBuiltinTypeArgumentHandler::class) + ->addTag('messenger.message_handler') + ; + + (new MessengerPass())->process($container); + } + public function testNeedsToHandleAtLeastOneMessage() { $this->expectException(RuntimeException::class); diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/UnionBuiltinTypeArgumentHandler.php b/src/Symfony/Component/Messenger/Tests/Fixtures/UnionBuiltinTypeArgumentHandler.php new file mode 100644 index 0000000000000..6061651de187b --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/UnionBuiltinTypeArgumentHandler.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class UnionBuiltinTypeArgumentHandler +{ + public function __invoke(string|int $message): void + { + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/UnionTypeArgumentHandler.php b/src/Symfony/Component/Messenger/Tests/Fixtures/UnionTypeArgumentHandler.php new file mode 100644 index 0000000000000..85be3662ac974 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/UnionTypeArgumentHandler.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Fixtures; + +class UnionTypeArgumentHandler +{ + public function __invoke(ChildDummyMessage|DummyMessage $message): void + { + } +} diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php index 2dba7d9f1045b..6f36a39250b36 100644 --- a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php @@ -126,11 +126,11 @@ public static function fromDsn(string $dsn, array $options = [], AmqpFactory $am ], $options, $parsedQuery); if (isset($parsedUrl['user'])) { - $amqpOptions['login'] = $parsedUrl['user']; + $amqpOptions['login'] = urldecode($parsedUrl['user']); } if (isset($parsedUrl['pass'])) { - $amqpOptions['password'] = $parsedUrl['pass']; + $amqpOptions['password'] = urldecode($parsedUrl['pass']); } if (!isset($amqpOptions['queues'])) { diff --git a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php index 29eb6cb12e0d9..7f9f876dc54f4 100644 --- a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php +++ b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php @@ -93,8 +93,8 @@ public static function fromDsn(string $dsn, array $redisOptions = [], \Redis $re $stream = $pathParts[1] ?? $redisOptions['stream'] ?? null; $group = $pathParts[2] ?? $redisOptions['group'] ?? null; $consumer = $pathParts[3] ?? $redisOptions['consumer'] ?? null; - $pass = '' !== ($parsedUrl['pass'] ?? '') ? $parsedUrl['pass'] : null; - $user = '' !== ($parsedUrl['user'] ?? '') ? $parsedUrl['user'] : null; + $pass = '' !== ($parsedUrl['pass'] ?? '') ? urldecode($parsedUrl['pass']) : null; + $user = '' !== ($parsedUrl['user'] ?? '') ? urldecode($parsedUrl['user']) : null; $connectionCredentials = [ 'host' => $parsedUrl['host'] ?? '127.0.0.1', diff --git a/src/Symfony/Component/Messenger/composer.json b/src/Symfony/Component/Messenger/composer.json index fd8aabb747cb9..1859442a96dab 100644 --- a/src/Symfony/Component/Messenger/composer.json +++ b/src/Symfony/Component/Messenger/composer.json @@ -21,7 +21,7 @@ "symfony/polyfill-php80": "^1.16" }, "require-dev": { - "doctrine/dbal": "^2.6|^3.0", + "doctrine/dbal": "^2.7|^3.0", "doctrine/persistence": "^1.3|^2|^3", "psr/cache": "^1.0|^2.0|^3.0", "symfony/console": "^3.4|^4.0|^5.0", @@ -36,6 +36,7 @@ "symfony/validator": "^3.4|^4.0|^5.0" }, "conflict": { + "doctrine/dbal": "<2.7", "doctrine/persistence": "<1.3", "symfony/event-dispatcher": "<4.3", "symfony/framework-bundle": "<4.4", diff --git a/src/Symfony/Component/Mime/Email.php b/src/Symfony/Component/Mime/Email.php index 9cdde13e533c6..70ea745788e8a 100644 --- a/src/Symfony/Component/Mime/Email.php +++ b/src/Symfony/Component/Mime/Email.php @@ -463,7 +463,7 @@ private function generateBody(): AbstractPart $this->ensureValidity(); - [$htmlPart, $attachmentParts, $inlineParts] = $this->prepareParts(); + [$htmlPart, $otherParts, $relatedParts] = $this->prepareParts(); $part = null === $this->text ? null : new TextPart($this->text, $this->textCharset); if (null !== $htmlPart) { @@ -474,15 +474,15 @@ private function generateBody(): AbstractPart } } - if ($inlineParts) { - $part = new RelatedPart($part, ...$inlineParts); + if ($relatedParts) { + $part = new RelatedPart($part, ...$relatedParts); } - if ($attachmentParts) { + if ($otherParts) { if ($part) { - $part = new MixedPart($part, ...$attachmentParts); + $part = new MixedPart($part, ...$otherParts); } else { - $part = new MixedPart(...$attachmentParts); + $part = new MixedPart(...$otherParts); } } @@ -508,42 +508,44 @@ private function prepareParts(): ?array } // usage of reflection is a temporary workaround for missing getters that will be added in 6.2 - $dispositionRef = new \ReflectionProperty(TextPart::class, 'disposition'); - $dispositionRef->setAccessible(true); $nameRef = new \ReflectionProperty(TextPart::class, 'name'); $nameRef->setAccessible(true); - $attachmentParts = $inlineParts = []; + $otherParts = $relatedParts = []; foreach ($this->attachments as $attachment) { $part = $this->createDataPart($attachment); if (isset($attachment['part'])) { $attachment['name'] = $nameRef->getValue($part); } + $related = false; foreach ($names as $name) { if ($name !== $attachment['name']) { continue; } - if (isset($inlineParts[$name])) { + if (isset($relatedParts[$name])) { continue 2; } $part->setDisposition('inline'); - $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html); + $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html, $count); + if ($count) { + $related = true; + } $part->setName($part->getContentId()); break; } - if ('inline' === $dispositionRef->getValue($part)) { - $inlineParts[$attachment['name']] = $part; + if ($related) { + $relatedParts[$attachment['name']] = $part; } else { - $attachmentParts[] = $part; + $otherParts[] = $part; } } if (null !== $htmlPart) { $htmlPart = new TextPart($html, $this->htmlCharset, 'html'); } - return [$htmlPart, $attachmentParts, array_values($inlineParts)]; + return [$htmlPart, $otherParts, array_values($relatedParts)]; } private function createDataPart(array $attachment): DataPart diff --git a/src/Symfony/Component/Mime/Tests/EmailTest.php b/src/Symfony/Component/Mime/Tests/EmailTest.php index a79a785576361..2d03a8a9aeedd 100644 --- a/src/Symfony/Component/Mime/Tests/EmailTest.php +++ b/src/Symfony/Component/Mime/Tests/EmailTest.php @@ -302,6 +302,17 @@ public function testGenerateBodyWithHtmlContentAndAttachedFile() $this->assertEquals(new MixedPart($html, $filePart), $e->getBody()); } + public function testGenerateBodyWithHtmlContentAndInlineImageNotreferenced() + { + [$text, $html, $filePart, $file, $imagePart, $image] = $this->generateSomeParts(); + $imagePart = new DataPart($image = fopen(__DIR__.'/Fixtures/mimetypes/test.gif', 'r')); + $imagePart->asInline(); + $e = (new Email())->from('me@example.com')->to('you@example.com'); + $e->embed($image); + $e->html('html content'); + $this->assertEquals(new MixedPart($html, $imagePart), $e->getBody()); + } + public function testGenerateBodyWithAttachedFileOnly() { [$text, $html, $filePart, $file, $imagePart, $image] = $this->generateSomeParts(); @@ -310,6 +321,24 @@ public function testGenerateBodyWithAttachedFileOnly() $this->assertEquals(new MixedPart($filePart), $e->getBody()); } + public function testGenerateBodyWithInlineImageOnly() + { + $imagePart = new DataPart($image = fopen(__DIR__.'/Fixtures/mimetypes/test.gif', 'r')); + $imagePart->asInline(); + $e = (new Email())->from('me@example.com')->to('you@example.com'); + $e->embed($image); + $this->assertEquals(new MixedPart($imagePart), $e->getBody()); + } + + public function testGenerateBodyWithEmbeddedImageOnly() + { + $imagePart = new DataPart($image = fopen(__DIR__.'/Fixtures/mimetypes/test.gif', 'r')); + $e = (new Email())->from('me@example.com')->to('you@example.com'); + $e->embed($image); + $imagePart->asInline(); + $this->assertEquals(new MixedPart($imagePart), $e->getBody()); + } + public function testGenerateBodyWithTextAndHtmlContentAndAttachedFile() { [$text, $html, $filePart, $file, $imagePart, $image] = $this->generateSomeParts(); diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf index 2d3a87c793ddf..7e75773798bf3 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.nb.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Ugyldig eller utløpt påloggingskobling. + + Too many failed login attempts, please try again in %minutes% minute. + For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutt. + + + Too many failed login attempts, please try again in %minutes% minutes. + For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutter. + diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf index 2d3a87c793ddf..7e75773798bf3 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.no.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Ugyldig eller utløpt påloggingskobling. + + Too many failed login attempts, please try again in %minutes% minute. + For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutt. + + + Too many failed login attempts, please try again in %minutes% minutes. + For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutter. + diff --git a/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php b/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php index f91478bfc91b2..212ac310bc91a 100644 --- a/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; @@ -34,7 +35,7 @@ abstract protected function getLoginUrl(); /** * Override to change what happens after a bad username/password is submitted. * - * @return RedirectResponse + * @return Response */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { @@ -56,7 +57,7 @@ public function supportsRememberMe() * Override to control what happens when the user hits a secure page * but isn't logged in yet. * - * @return RedirectResponse + * @return Response */ public function start(Request $request, AuthenticationException $authException = null) { diff --git a/src/Symfony/Component/Stopwatch/Tests/StopwatchPeriodTest.php b/src/Symfony/Component/Stopwatch/Tests/StopwatchPeriodTest.php index e01849d474869..81010a79413fd 100644 --- a/src/Symfony/Component/Stopwatch/Tests/StopwatchPeriodTest.php +++ b/src/Symfony/Component/Stopwatch/Tests/StopwatchPeriodTest.php @@ -40,7 +40,7 @@ public function testGetEndTime($end, $useMorePrecision, $expected) public function testGetDuration($start, $end, $useMorePrecision, $duration) { $period = new StopwatchPeriod($start, $end, $useMorePrecision); - $this->assertSame($duration, $period->getDuration()); + $this->assertEqualsWithDelta($duration, $period->getDuration(), \PHP_FLOAT_EPSILON); } public function provideTimeValues() diff --git a/src/Symfony/Component/Stopwatch/composer.json b/src/Symfony/Component/Stopwatch/composer.json index 1babf15aed09f..a11dc7960f185 100644 --- a/src/Symfony/Component/Stopwatch/composer.json +++ b/src/Symfony/Component/Stopwatch/composer.json @@ -19,6 +19,9 @@ "php": ">=7.1.3", "symfony/service-contracts": "^1.0|^2" }, + "require-dev": { + "symfony/polyfill-php72": "~1.5" + }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" }, "exclude-from-classmap": [ diff --git a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php index 1a3ae3784b422..5f2c7a8ef140e 100644 --- a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php @@ -48,7 +48,7 @@ public function validate($value, Constraint $constraint) \DateTime::createFromFormat($constraint->format, $value); - $errors = \DateTime::getLastErrors(); + $errors = \DateTime::getLastErrors() ?: ['error_count' => 0, 'warnings' => []]; if (0 < $errors['error_count']) { $this->context->buildViolation($constraint->message) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf index 93132ec57cdfc..5e1ebc189c350 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nb.xlf @@ -386,6 +386,22 @@ This value is not a valid International Securities Identification Number (ISIN). Denne verdien er ikke et gyldig International Securities Identification Number (ISIN). + + This value should be a valid expression. + Denne verdien skal være et gyldig uttrykk. + + + This value is not a valid CSS color. + Denne verdien er ikke en gyldig CSS-farge. + + + This value is not a valid CIDR notation. + Denne verdien er ikke en gyldig CIDR-notasjon. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Verdien på nettmasken skal 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 93132ec57cdfc..5e1ebc189c350 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.no.xlf @@ -386,6 +386,22 @@ This value is not a valid International Securities Identification Number (ISIN). Denne verdien er ikke et gyldig International Securities Identification Number (ISIN). + + This value should be a valid expression. + Denne verdien skal være et gyldig uttrykk. + + + This value is not a valid CSS color. + Denne verdien er ikke en gyldig CSS-farge. + + + This value is not a valid CIDR notation. + Denne verdien er ikke en gyldig CIDR-notasjon. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Verdien på nettmasken skal være mellom {{ min }} og {{ max }}. + diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php index 921cfda456acf..44036295efb68 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php @@ -39,6 +39,10 @@ public function testDumpForwardsToWrappedDumperWhenServerIsUnavailable() public function testDump() { + if ('True' === getenv('APPVEYOR')) { + $this->markTestSkipped('Skip transient test on AppVeyor'); + } + $wrappedDumper = $this->createMock(DataDumperInterface::class); $wrappedDumper->expects($this->never())->method('dump'); // test wrapped dumper is not used diff --git a/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php b/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php index 15e7b0760d3b6..e15b8d6acffb2 100644 --- a/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php @@ -24,6 +24,10 @@ class ConnectionTest extends TestCase public function testDump() { + if ('True' === getenv('APPVEYOR')) { + $this->markTestSkipped('Skip transient test on AppVeyor'); + } + $cloner = new VarCloner(); $data = $cloner->cloneVar('foo'); $connection = new Connection(self::VAR_DUMPER_SERVER, [