diff --git a/CHANGELOG-4.0.md b/CHANGELOG-4.0.md index cbd0ed892c1db..9abb4a61a6634 100644 --- a/CHANGELOG-4.0.md +++ b/CHANGELOG-4.0.md @@ -7,6 +7,41 @@ in 4.0 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.0.0...v4.0.1 +* 4.0.9 (2018-04-30) + + * bug #27074 [Debug][WebProfilerBundle] Fix setting file link format (lyrixx, nicolas-grekas) + * bug #27088 ResolveBindingsPass: Don't throw error for unused service, missing parent class (weaverryan) + * bug #27086 [PHPUnitBridge] Add an implementation just for php 7.0 (greg0ire) + * bug #26138 [HttpKernel] Catch HttpExceptions when templating is not installed (cilefen) + * bug #27007 [Cache] TagAwareAdapterInterface::invalidateTags() should commit deferred items (nicolas-grekas) + * bug #27067 [HttpFoundation] Fix setting session-related ini settings (e-moe) + * bug #27061 [HttpKernel] Don't clean legacy containers that are still loaded (nicolas-grekas) + * bug #27064 [VarDumper] Fix HtmlDumper classes match (ogizanagi) + * bug #27016 [Security][Guard] GuardAuthenticationProvider::authenticate cannot return null (biomedia-thomas) + * bug #26831 [Bridge/Doctrine] count(): Parameter must be an array or an object that implements Countable (gpenverne) + * bug #27044 [Security] Skip user checks if not implementing UserInterface (chalasr) + * bug #27025 [DI] Add check of internal type to ContainerBuilder::getReflectionClass (upyx) + * bug #26994 [PhpUnitBridge] Add type hints (greg0ire) + * bug #26014 [Security] Fixed being logged out on failed attempt in guard (iltar) + * bug #25348 [HttpFoundation] Send cookies using header() to fix "SameSite" ones (nicolas-grekas, cvilleger) + * bug #26910 Use new PHP7.2 functions in hasColorSupport (johnstevenson) + * bug #26999 [VarDumper] Fix dumping of SplObjectStorage (corphi) + * bug #25841 [DoctrineBridge] Fix bug when indexBy is meta key in PropertyInfo\DoctrineExtractor (insekticid) + * bug #26983 [TwigBridge] [Bootstrap 4] Fix PercentType error rendering. (alexismarquis) + * bug #26980 [TwigBundle] fix formatting arguments in plaintext format (xabbuh) + * bug #26886 Don't assume that file binary exists on *nix OS (teohhanhui) + * bug #26959 [Console] Fix PSR exception context key (scaytrase) + * bug #26899 [Routing] Fix loading multiple class annotations for invokable classes (1ed) + * bug #26643 Fix that ESI/SSI processing can turn a "private" response "public" (mpdude) + * bug #26932 [Form] Fixed trimming choice values (HeahDude) + * bug #26922 [TwigBundle] fix rendering exception stack traces (xabbuh) + * bug #26773 [HttpKernel] Make ServiceValueResolver work if controller namespace starts with a backslash in routing (mathieutu) + * bug #26870 Add d-block to bootstrap 4 alerts (Normunds) + * bug #26857 [HttpKernel] Dont create mock cookie for new sessions in tests (nicolas-grekas) + * bug #26875 [Console] Don't go past exact matches when autocompleting (nicolas-grekas) + * bug #26823 [Validator] Fix LazyLoadingMetadataFactory with PSR6Cache for non classname if tested values isn't existing class (Pascal Montoya, pmontoya) + * bug #26834 [Yaml] Throw parse error on unfinished inline map (nicolas-grekas) + * 4.0.8 (2018-04-06) * bug #26802 [Security] register custom providers on ExpressionLanguage directly (dmaicher) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1cb4373ec2030..bc4ebf6f78139 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -13,21 +13,21 @@ Symfony is the result of the work of many people who made the code better - Jordi Boggiano (seldaek) - Victor Berchet (victor) - Kévin Dunglas (dunglas) + - Robin Chalas (chalas_r) - Johannes S (johannes) - Jakub Zalas (jakubzalas) - - Robin Chalas (chalas_r) - Kris Wallsmith (kriswallsmith) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) - Maxime Steinhausser (ogizanagi) - - Hugo Hamon (hhamon) - Grégoire Pineau (lyrixx) + - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) - Romain Neutron (romain) - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) - - Joseph Bielawski (stloyd) - Roland Franssen (ro0) + - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) @@ -39,32 +39,32 @@ Symfony is the result of the work of many people who made the code better - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) - Sarah Khalil (saro0h) + - Samuel ROZE (sroze) - Jonathan Wage (jwage) - Hamza Amrouche (simperfit) - Diego Saint Esteben (dosten) + - Yonel Ceruto (yonelceruto) - Alexandre Salomé (alexandresalome) + - Iltar van der Berg (kjarli) - William Durand (couac) - ornicar - Francis Besset (francisbesset) - - Iltar van der Berg (kjarli) - stealth35 (stealth35) - Alexander Mols (asm89) - - Yonel Ceruto (yonelceruto) - Bulat Shakirzyanov (avalanche123) - Peter Rehm (rpet) - - Saša Stamenković (umpirsky) - Matthias Pigulla (mpdude) - - Samuel ROZE (sroze) + - Saša Stamenković (umpirsky) + - Pierre du Plessis (pierredup) - Henrik Bjørnskov (henrikbjorn) - Dany Maillard (maidmaid) - Miha Vrhovnik - - Pierre du Plessis (pierredup) - Tobias Nyholm (tobias) - Diego Saint Esteben (dii3g0) - - Konstantin Kudryashov (everzet) - Kevin Bond (kbond) - - Bilal Amarni (bamarni) + - Konstantin Kudryashov (everzet) - Alexander M. Turek (derrabus) + - Bilal Amarni (bamarni) - Jérémy DERUSSÉ (jderusse) - Florin Patan (florinpatan) - Mathieu Piot (mpiot) @@ -81,13 +81,14 @@ Symfony is the result of the work of many people who made the code better - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) - - Douglas Greenshields (shieldo) - Issei Murasawa (issei_m) + - Douglas Greenshields (shieldo) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - Graham Campbell (graham) - Daniel Holmes (dholmes) + - David Maicher (dmaicher) - Dariusz Ruminski - Toni Uebernickel (havvg) - Bart van den Burg (burgov) @@ -95,21 +96,20 @@ Symfony is the result of the work of many people who made the code better - Jérôme Tamarelle (gromnan) - John Wards (johnwards) - Fran Moreno (franmomu) + - Vladimir Reznichenko (kalessil) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - - David Maicher (dmaicher) - - Vladimir Reznichenko (kalessil) - Arnaud Le Blanc (arnaud-lb) - Maxime STEINHAUSSER - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - Brice BERNARD (brikou) - Baptiste Clavié (talus) + - Grégoire Paris (greg0ire) - marc.weistroff - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) - - Grégoire Paris (greg0ire) - Jacob Dreesen (jdreesen) - Florian Voutzinos (florianv) - Colin Frei @@ -122,17 +122,17 @@ Symfony is the result of the work of many people who made the code better - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) - Eric GELOEN (gelo) + - Lars Strojny (lstrojny) - Daniel Wehner (dawehner) - Tugdual Saunier (tucksaun) + - Javier Spagnoletti (phansys) - Théo FIDRY (theofidry) - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) - Sebastiaan Stok (sstok) - Stefano Sala (stefano.sala) - Evgeniy (ewgraf) - - Lars Strojny (lstrojny) - Alex Pott - - Javier Spagnoletti (phansys) - Vincent AUBERT (vincent) - Juti Noppornpitak (shiroyuki) - Tigran Azatyan (tigranazatyan) @@ -148,7 +148,9 @@ Symfony is the result of the work of many people who made the code better - Julien Falque (julienfalque) - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) + - gadelat (gadelat) - jwdeitch + - Teoh Han Hui (teohhanhui) - Mikael Pajunen - Joel Wurtz (brouznouf) - Valentin Udaltsov (vudaltsov) @@ -158,9 +160,7 @@ Symfony is the result of the work of many people who made the code better - Richard van Laak (rvanlaak) - Richard Shank (iampersistent) - Thomas Rabaix (rande) - - gadelat (gadelat) - Rouven Weßling (realityking) - - Teoh Han Hui (teohhanhui) - Clemens Tolboom - Helmer Aaviksoo - Hiromi Hishida (77web) @@ -186,6 +186,7 @@ Symfony is the result of the work of many people who made the code better - Gabriel Ostrolucký - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) + - DQNEO - SpacePossum - Benjamin Dulau (dbenjamin) - Mathieu Lemoine (lemoinem) @@ -223,6 +224,7 @@ Symfony is the result of the work of many people who made the code better - Niels Keurentjes (curry684) - Eugene Wissner - Julien Brochet (mewt) + - Leo Feyer - Tristan Darricau (nicofuma) - Michaël Perrin (michael.perrin) - Marcel Beerta (mazen) @@ -269,7 +271,6 @@ Symfony is the result of the work of many people who made the code better - Ray - Tyson Andre - Nikolay Labinskiy (e-moe) - - Leo Feyer - Chekote - Thomas Adam - Albert Casademont (acasademont) @@ -277,7 +278,6 @@ Symfony is the result of the work of many people who made the code better - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - Andreas Schempp (aschempp) - - DQNEO - jdhoek - Pavel Batanov (scaytrase) - Bob den Otter (bopp) @@ -292,6 +292,7 @@ Symfony is the result of the work of many people who made the code better - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) + - Florent Mata (fmata) - Wouter Van Hecke - Jérôme Parmentier (lctrs) - Michael Babker (mbabker) @@ -299,6 +300,7 @@ Symfony is the result of the work of many people who made the code better - Michael Holm (hollo) - Marc Weistroff (futurecat) - Christian Schmidt + - Maxime Veber (nek-) - Edi Modrić (emodric) - Chad Sikorra (chadsikorra) - Chris Smith (cs278) @@ -326,6 +328,7 @@ Symfony is the result of the work of many people who made the code better - John Bafford (jbafford) - Adrian Rudnik (kreischweide) - Francesc Rosàs (frosas) + - Romain Pierre (romain-pierre) - Massimiliano Arione (garak) - Julien Galenski (ruian) - Bongiraud Dominique @@ -356,8 +359,8 @@ Symfony is the result of the work of many people who made the code better - Damien Alexandre (damienalexandre) - Felix Labrecque - Yaroslav Kiliba - - Maxime Veber (nek-) - Terje Bråten + - Mathieu Lechat - Robbert Klarenbeek (robbertkl) - JhonnyL - David Badura (davidbadura) @@ -371,13 +374,14 @@ Symfony is the result of the work of many people who made the code better - Philipp Kräutli (pkraeutli) - Kirill chEbba Chebunin (chebba) - Greg Thornton (xdissent) - - Florent Mata (fmata) + - Gary PEGEOT (gary-p) - Costin Bereveanu (schniper) - Loïc Chardonnet (gnusat) - Marek Kalnik (marekkalnik) - Vyacheslav Salakhutdinov (megazoll) - Hassan Amouhzi - Tamas Szijarto + - Michele Locati - Pavel Volokitin (pvolok) - Arthur de Moulins (4rthem) - Nicolas Dewez (nicolas_dewez) @@ -420,9 +424,11 @@ Symfony is the result of the work of many people who made the code better - Jeanmonod David (jeanmonod) - Christopher Davis (chrisguitarguy) - Jan Schumann + - Christian Schmidt - Niklas Fiekas - Markus Bachmann (baachi) - lancergr + - Zan Baldwin - Mihai Stancu - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) @@ -435,7 +441,9 @@ Symfony is the result of the work of many people who made the code better - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) - Andreas Braun + - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) + - Mateusz Sip (mateusz_sip) - Seb Koelen - Christoph Mewes (xrstf) - Vitaliy Tverdokhlib (vitaliytv) @@ -456,6 +464,7 @@ Symfony is the result of the work of many people who made the code better - Adam Harvey - Anton Bakai - Alex Bakhturin + - insekticid - Alexander Obuhovich (aik099) - boombatower - Fabrice Bernhard (fabriceb) @@ -467,6 +476,7 @@ Symfony is the result of the work of many people who made the code better - Roman Lapin (memphys) - Yoshio HANAWA - Gladhon + - Haralan Dobrev (hkdobrev) - Sebastian Bergmann - Miroslav Sustek - Sullivan SENECHAL (soullivaneuh) @@ -499,9 +509,7 @@ Symfony is the result of the work of many people who made the code better - Andrew Udvare (audvare) - alexpods - Arjen van der Meijden - - Michele Locati - Dariusz Ruminski - - Mathieu Lechat - Erik Trapman (eriktrapman) - De Cock Xavier (xdecock) - Almog Baku (almogbaku) @@ -550,7 +558,6 @@ Symfony is the result of the work of many people who made the code better - Disquedur - Michiel Boeckaert (milio) - Geoffrey Tran (geoff) - - Romain Pierre (romain-pierre) - David Prévot - Jan Behrens - Mantas Var (mvar) @@ -564,7 +571,6 @@ Symfony is the result of the work of many people who made the code better - aubx - Marvin Butkereit - Ricky Su (ricky) - - Zan Baldwin - Gildas Quéméner (gquemener) - Charles-Henri Bruyand - Max Rath (drak3) @@ -577,6 +583,7 @@ Symfony is the result of the work of many people who made the code better - Andre Rømcke (andrerom) - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) + - Christophe Villeger (seragan) - Stefan Gehrig (sgehrig) - Hany el-Kerdany - Wang Jingyu @@ -588,14 +595,13 @@ Symfony is the result of the work of many people who made the code better - Javier López (loalf) - Reinier Kip - Geoffrey Brier (geoffrey-brier) - - Boris Vujicic (boris.vujicic) + - Vladimir Tsykun - Dustin Dobervich (dustin10) - dantleech - Anne-Sophie Bachelard (annesophie) - Sebastian Marek (proofek) - Erkhembayar Gantulga (erheme318) - Michal Trojanowski - - Mateusz Sip - David Fuhr - Kamil Kokot (pamil) - Aurimas Niekis (gcds) @@ -603,6 +609,7 @@ Symfony is the result of the work of many people who made the code better - mcfedr (mcfedr) - Rostyslav Kinash - Maciej Malarz (malarzm) + - Pascal Luna (skalpa) - Daisuke Ohata - Vincent Simonin - Alex Bogomazov (alebo) @@ -623,6 +630,7 @@ Symfony is the result of the work of many people who made the code better - Denis Brumann (dbrumann) - Quentin de Longraye (quentinus95) - Chris Heng (gigablah) + - Shaun Simmons (simshaun) - Richard Bradley - Ulumuddin Yunus (joenoez) - Johann Saunier (prophet777) @@ -670,7 +678,6 @@ Symfony is the result of the work of many people who made the code better - twifty - Indra Gunawan (guind) - Peter Ward - - insekticid - Julien DIDIER (juliendidier) - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) @@ -765,6 +772,7 @@ Symfony is the result of the work of many people who made the code better - Maksim Kotlyar (makasim) - Neil Ferreira - Nathanael Noblet (gnat) + - Indra Gunawan (indragunawan) - Dmitry Parnas (parnas) - Paul LE CORRE - Emanuele Iannone @@ -876,6 +884,7 @@ Symfony is the result of the work of many people who made the code better - Sander Marechal - Radosław Benkel - jean pasqualini (darkilliant) + - Ross Motley (rossmotley) - ttomor - Mei Gwilym (meigwilym) - Michael H. Arieli (excelwebzone) @@ -895,6 +904,7 @@ Symfony is the result of the work of many people who made the code better - Zachary Tong (polyfractal) - Ashura - Hryhorii Hrebiniuk + - johnstevenson - Dennis Fridrich (dfridrich) - hamza - dantleech @@ -938,6 +948,7 @@ Symfony is the result of the work of many people who made the code better - mlazovla - Max Beutel - Antanas Arvasevicius + - Thomas - Maximilian Berghoff (electricmaxxx) - nacho - Piotr Antosik (antek88) @@ -951,6 +962,7 @@ Symfony is the result of the work of many people who made the code better - Ken Marfilla (marfillaster) - benatespina (benatespina) - Denis Kop + - Jean-Guilhem Rouel (jean-gui) - jfcixmedia - Nikita Konstantinov - Martijn Evers @@ -982,7 +994,6 @@ Symfony is the result of the work of many people who made the code better - Olaf Klischat - orlovv - Peter Smeets (darkspartan) - - Haralan Dobrev (hkdobrev) - Jhonny Lidfors (jhonny) - Julien Bianchi (jubianchi) - Robert Meijers @@ -1001,6 +1012,7 @@ Symfony is the result of the work of many people who made the code better - Jeremy Bush - wizhippo - Viacheslav Sychov + - Helmut Hummel (helhum) - Matt Brunt - Carlos Ortega Huetos - rpg600 @@ -1025,6 +1037,7 @@ Symfony is the result of the work of many people who made the code better - rchoquet - gitlost - Taras Girnyk + - Anthony GRASSIOT (antograssiot) - Eduardo García Sanz (coma) - James Gilliland - fduch (fduch) @@ -1054,6 +1067,7 @@ Symfony is the result of the work of many people who made the code better - Peter Thompson (petert82) - Felicitus - Krzysztof Przybyszewski + - alexpozzi - Paul Matthews - Jakub Kisielewski - Vacheslav Silyutin @@ -1078,6 +1092,7 @@ Symfony is the result of the work of many people who made the code better - Tobias Stöckler - Mario Young - Ilia (aliance) + - Grégoire Penverne (gpenverne) - Mo Di (modi) - Pablo Schläpfer - Jelte Steijaert (jelte) @@ -1098,6 +1113,7 @@ Symfony is the result of the work of many people who made the code better - Lars Ambrosius Wallenborn (larsborn) - Oriol Mangas Abellan (oriolman) - Sebastian Göttschkes (sgoettschkes) + - Sergey (upyx) - Tatsuya Tsuruoka - Ross Tuck - Kévin Gomez (kevin) @@ -1121,6 +1137,7 @@ Symfony is the result of the work of many people who made the code better - Grzegorz Zdanowski (kiler129) - sl_toto (sl_toto) - Walter Dal Mut (wdalmut) + - abluchet - Matthieu - Albin Kerouaton - Sébastien HOUZÉ @@ -1133,9 +1150,11 @@ Symfony is the result of the work of many people who made the code better - Berat Doğan - Guillaume LECERF - Juanmi Rodriguez Cerón + - Sergey Yastrebov - Andy Raines - Anthony Ferrara - Klaas Cuvelier (kcuvelier) + - Mathieu TUDISCO (mathieutu) - markusu49 - Steve Frécinaux - Jules Lamur @@ -1178,6 +1197,7 @@ Symfony is the result of the work of many people who made the code better - Tadcka - Beth Binkovitz - Gonzalo Míguez + - Philipp Cordes - Pierre Rineau - Romain Geissler - Adrien Moiruad @@ -1190,6 +1210,7 @@ Symfony is the result of the work of many people who made the code better - Jay Severson - René Kerner - Nathaniel Catchpole + - - Adrien Samson (adriensamson) - Samuel Gordalina (gordalina) - Max Romanovsky (maxromanovsky) @@ -1226,6 +1247,7 @@ Symfony is the result of the work of many people who made the code better - Ergie Gonzaga - Matthew J Mucklo - AnrDaemon + - Smaine Milianni (ismail1432) - fdgdfg (psampaz) - Stéphane Seng - Maxwell Vandervelde @@ -1237,6 +1259,7 @@ Symfony is the result of the work of many people who made the code better - Simon Neidhold - Valentin VALCIU - Jeremiah VALERIE + - Julien Menth - Kevin Dew - James Cowgill - 1ma (jautenim) @@ -1293,7 +1316,6 @@ Symfony is the result of the work of many people who made the code better - ryunosuke - zenmate - victoria - - Christian Schmidt - Francisco Facioni (fran6co) - Iwan van Staveren (istaveren) - Povilas S. (povilas) @@ -1362,7 +1384,6 @@ Symfony is the result of the work of many people who made the code better - Christopher Parotat - 蝦米 - Grayson Koonce (breerly) - - Indra Gunawan (indragunawan) - Karim Cassam Chenaï (ka) - Michal Kurzeja (mkurzeja) - Nicolas Bastien (nicolas_bastien) @@ -1371,10 +1392,13 @@ Symfony is the result of the work of many people who made the code better - Oleksii Zhurbytskyi - Andy Stanberry - Felix Marezki + - Normunds - Luiz “Felds” Liscia - Thomas Rothe - nietonfir - alefranz + - David Barratt + - Pavel.Batanov - avi123 - alsar - Aarón Nieves Fernández @@ -1446,6 +1470,7 @@ Symfony is the result of the work of many people who made the code better - Edvinas Klovas - Drew Butler - Peter Breuls + - Chansig - Tischoi - J Bruni - Alexey Prilipko @@ -1530,6 +1555,7 @@ Symfony is the result of the work of many people who made the code better - Erik van Wingerden - Valouleloup - Dane Powell + - mweimerskirch - Gerrit Drost - Linnaea Von Lavia - Simon Mönch @@ -1602,6 +1628,7 @@ Symfony is the result of the work of many people who made the code better - Gabriel Moreira - Alexey Popkov - ChS + - Alexis MARQUIS - Joseph Deray - Damian Sromek - Ben @@ -1633,6 +1660,7 @@ Symfony is the result of the work of many people who made the code better - temperatur - Cas - Dusan Kasan + - Karolis - Myke79 - Brian Debuire - Piers Warmers @@ -1643,12 +1671,12 @@ Symfony is the result of the work of many people who made the code better - jc - BenjaminBeck - Aurelijus Rožėnas - - Vladimir Tsykun - Jordan Hoff - znerol - Christian Eikermann - Kai Eichinger - Antonio Angelino + - Pascal Montoya - Matt Fields - Niklas Keller - Vladimir Sazhin @@ -1703,7 +1731,6 @@ Symfony is the result of the work of many people who made the code better - Neophy7e - bokonet - Arrilot - - Shaun Simmons - Markus Staab - Pierre-Louis LAUNAY - djama @@ -1804,6 +1831,7 @@ Symfony is the result of the work of many people who made the code better - Arash Tabriziyan (ghost098) - ibasaw (ibasaw) - Vladislav Krupenkin (ideea) + - Peter Orosz (ill_logical) - Imangazaliev Muhammad (imangazaliev) - j0k (j0k) - joris de wit (jdewit) @@ -1832,6 +1860,7 @@ Symfony is the result of the work of many people who made the code better - emilienbouard (neime) - Nicholas Byfleet (nickbyfleet) - Tomas Norkūnas (norkunas) + - Marco Petersen (ocrampete16) - ollie harridge (ollietb) - Paul Andrieux (paulandrieux) - Paweł Szczepanek (pauluz) @@ -1855,7 +1884,6 @@ Symfony is the result of the work of many people who made the code better - Bruno Ziegler (sfcoder) - Andrea Giuliano (shark) - Schuyler Jager (sjager) - - Pascal Luna (skalpa) - Volker (skydiablo) - Serkan Yildiz (srknyldz) - Julien Sanchez (sumbobyboys) @@ -1886,6 +1914,7 @@ Symfony is the result of the work of many people who made the code better - Ben Scott - Dionysis Arvanitis - Sergey Fedotov + - Konstantin Scheumann - Michael - fh-github@fholzhauer.de - AbdElKader Bouadjadja @@ -1896,6 +1925,7 @@ Symfony is the result of the work of many people who made the code better - Zander Baldwin - Philipp Scheit - max + - Ahmad Mayahi (ahmadmayahi) - Mohamed Karnichi (amiral) - Andrew Carter (andrewcarteruk) - Adam Elsodaney (archfizz) diff --git a/appveyor.yml b/appveyor.yml index a6575052a974b..a901ad79397c2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -45,7 +45,7 @@ install: - copy /Y .composer\* %APPDATA%\Composer\ - php .github/build-packages.php "HEAD^" src\Symfony\Bridge\PhpUnit - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - - php -dmemory_limit=-1 composer.phar update --no-progress --no-suggest --ansi + - php composer.phar update --no-progress --no-suggest --ansi - php phpunit install test_script: diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php index e1e4e4226982c..3f3a5c8ac40f4 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php @@ -95,10 +95,7 @@ public function getIdValue($object) } if (!$this->om->contains($object)) { - throw new RuntimeException( - 'Entities passed to the choice field must be managed. Maybe '. - 'persist them in the entity manager?' - ); + throw new RuntimeException(sprintf('Entity of type "%s" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?', get_class($object))); } $this->om->initializeObject($object); diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index aa1d95ecc6bb7..2a41422e00bd0 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -95,9 +95,19 @@ public function getTypes($class, $property, array $context = array()) if (isset($associationMapping['indexBy'])) { $indexProperty = $associationMapping['indexBy']; + /** @var ClassMetadataInfo $subMetadata */ $subMetadata = $this->classMetadataFactory->getMetadataFor($associationMapping['targetEntity']); $typeOfField = $subMetadata->getTypeOfField($indexProperty); + if (null === $typeOfField) { + $associationMapping = $subMetadata->getAssociationMapping($indexProperty); + + /** @var ClassMetadataInfo $subMetadata */ + $indexProperty = $subMetadata->getSingleAssociationReferencedJoinColumnName($indexProperty); + $subMetadata = $this->classMetadataFactory->getMetadataFor($associationMapping['targetEntity']); + $typeOfField = $subMetadata->getTypeOfField($indexProperty); + } + $collectionKeyType = $this->getPhpType($typeOfField); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index fd6c23279c83d..f212ab25e6b8f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -61,6 +61,7 @@ public function testGetProperties() 'foo', 'bar', 'indexedBar', + 'indexedFoo', ), $this->extractor->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy') ); @@ -141,6 +142,14 @@ public function typesProvider() new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') ))), + array('indexedFoo', array(new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + 'Doctrine\Common\Collections\Collection', + true, + new Type(Type::BUILTIN_TYPE_STRING), + new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') + ))), array('simpleArray', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))), array('customFoo', null), array('notMapped', null), diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php index 793be8f9aae8b..9517f6cc40d31 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; @@ -45,6 +46,11 @@ class DoctrineDummy */ protected $indexedBar; + /** + * @OneToMany(targetEntity="DoctrineRelation", mappedBy="foo", indexBy="foo") + */ + protected $indexedFoo; + /** * @Column(type="guid") */ diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php index 6e94e028faa7d..85660d3d6b66c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\ManyToOne; /** * @Entity @@ -32,4 +33,10 @@ class DoctrineRelation * @Column(type="guid") */ protected $rguid; + + /** + * @Column(type="guid") + * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="indexedFoo") + */ + protected $foo; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 878e19ccb5cd9..efc611859a2b5 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -630,6 +630,34 @@ public function testEntityManagerNullObject() $this->validator->validate($entity, $constraint); } + public function testValidateUniquenessOnNullResult() + { + $repository = $this->createRepositoryMock(); + $repository + ->method('find') + ->will($this->returnValue(null)) + ; + + $this->em = $this->createEntityManagerMock($repository); + $this->registry = $this->createRegistryMock($this->em); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + )); + + $entity = new SingleIntIdEntity(1, null); + + $this->em->persist($entity); + $this->em->flush(); + + $this->validator->validate($entity, $constraint); + $this->assertNoViolation(); + } + public function testValidateInheritanceUniqueness() { $constraint = new UniqueEntity(array( diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index e03ef3c555b37..004de6f67e5a1 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -148,15 +148,23 @@ public function validate($entity, Constraint $constraint) */ if ($result instanceof \Iterator) { $result->rewind(); - } elseif (is_array($result)) { + if ($result instanceof \Countable && 1 < \count($result)) { + $result = array($result->current(), $result->current()); + } else { + $result = $result->current(); + $result = null === $result ? array() : array($result); + } + } elseif (\is_array($result)) { reset($result); + } else { + $result = null === $result ? array() : array($result); } /* If no entity matched the query criteria or a single entity matched, * which is the same as the entity being validated, the criteria is * unique. */ - if (0 === count($result) || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)))) { + if (!$result || (1 === \count($result) && current($result) === $entity)) { return; } diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 22992da68051e..29b1960798b8c 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -297,17 +297,38 @@ public static function collectDeprecations($outputFile) }); } + /** + * Returns true if STDOUT is defined and supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @return bool + */ private static function hasColorSupport() { - if ('\\' === DIRECTORY_SEPARATOR) { - return - defined('STDOUT') && function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(STDOUT) - || '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD + if (!defined('STDOUT')) { + return false; + } + + if (DIRECTORY_SEPARATOR === '\\') { + return (function_exists('sapi_windows_vt100_support') + && sapi_windows_vt100_support(STDOUT)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); + if (function_exists('stream_isatty')) { + return stream_isatty(STDOUT); + } + + if (function_exists('posix_isatty')) { + return posix_isatty(STDOUT); + } + + $stat = fstat(STDOUT); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/Command.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php similarity index 78% rename from src/Symfony/Bridge/PhpUnit/Legacy/Command.php rename to src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php index 0aec8ab67f33e..d4b5ea26d8cd8 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/Command.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php @@ -16,13 +16,13 @@ * * @internal */ -class Command extends \PHPUnit_TextUI_Command +class CommandForV5 extends \PHPUnit_TextUI_Command { /** * {@inheritdoc} */ protected function createRunner() { - return new TestRunner($this->arguments['loader']); + return new TestRunnerForV5($this->arguments['loader']); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php new file mode 100644 index 0000000000000..fc717ef415cb3 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\TextUI\Command as BaseCommand; +use PHPUnit\TextUI\TestRunner as BaseRunner; +use Symfony\Bridge\PhpUnit\TextUI\TestRunner; + +/** + * {@inheritdoc} + * + * @internal + */ +class CommandForV6 extends BaseCommand +{ + /** + * {@inheritdoc} + */ + protected function createRunner(): BaseRunner + { + return new TestRunner($this->arguments['loader']); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunner.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php similarity index 95% rename from src/Symfony/Bridge/PhpUnit/Legacy/TestRunner.php rename to src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php index 8a57416bd241f..7897861cf52f7 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunner.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php @@ -16,7 +16,7 @@ * * @internal */ -class TestRunner extends \PHPUnit_TextUI_TestRunner +class TestRunnerForV5 extends \PHPUnit_TextUI_TestRunner { /** * {@inheritdoc} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php new file mode 100644 index 0000000000000..6da7c65448532 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\TextUI\TestRunner as BaseRunner; +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; + +/** + * {@inheritdoc} + * + * @internal + */ +class TestRunnerForV6 extends BaseRunner +{ + /** + * {@inheritdoc} + */ + protected function handleConfiguration(array &$arguments) + { + $listener = new SymfonyTestsListener(); + + parent::handleConfiguration($arguments); + + $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); + + $registeredLocally = false; + + foreach ($arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListener) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $arguments['listeners'][] = $listener; + } + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php new file mode 100644 index 0000000000000..a175fb65d7f5a --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\TextUI\TestRunner as BaseRunner; +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; + +/** + * {@inheritdoc} + * + * @internal + */ +class TestRunnerForV7 extends BaseRunner +{ + /** + * {@inheritdoc} + */ + protected function handleConfiguration(array &$arguments): void + { + $listener = new SymfonyTestsListener(); + + parent::handleConfiguration($arguments); + + $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); + + $registeredLocally = false; + + foreach ($arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListener) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $arguments['listeners'][] = $listener; + } + } +} diff --git a/src/Symfony/Bridge/PhpUnit/TextUI/Command.php b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php index 869a8a8dbe85d..4a26fc7fad278 100644 --- a/src/Symfony/Bridge/PhpUnit/TextUI/Command.php +++ b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php @@ -11,24 +11,14 @@ namespace Symfony\Bridge\PhpUnit\TextUI; -use PHPUnit\TextUI\Command as BaseCommand; - if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\Command', 'Symfony\Bridge\PhpUnit\TextUI\Command'); + class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV5', 'Symfony\Bridge\PhpUnit\TextUI\Command'); } else { - /** - * {@inheritdoc} - * - * @internal - */ - class Command extends BaseCommand + class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV6', 'Symfony\Bridge\PhpUnit\TextUI\Command'); +} + +if (false) { + class Command { - /** - * {@inheritdoc} - */ - protected function createRunner() - { - return new TestRunner($this->arguments['loader']); - } } } diff --git a/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php b/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php index 4e1fdca4d54be..cda59209790d5 100644 --- a/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php +++ b/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php @@ -11,45 +11,16 @@ namespace Symfony\Bridge\PhpUnit\TextUI; -use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunner', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); + class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV5', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); +} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) { + class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV6', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); } else { - /** - * {@inheritdoc} - * - * @internal - */ - class TestRunner extends BaseRunner - { - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments) - { - $listener = new SymfonyTestsListener(); - - $result = parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListener) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } + class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV7', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); +} - return $result; - } +if (false) { + class TestRunner + { } } diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index a549cd7da4fb7..a824eae8f2ab4 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -72,7 +72,7 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ stream_copy_to_stream($remoteZipStream, fopen("$PHPUNIT_VERSION.zip", 'wb')); } else { @unlink("$PHPUNIT_VERSION.zip"); - passthru("wget https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"); + passthru("wget -q https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"); } if (!class_exists('ZipArchive')) { throw new \Exception('simple-phpunit requires the "zip" PHP extension to be installed and enabled in order to uncompress the downloaded PHPUnit packages.'); diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index bbc5e3a32299b..c0982fab00a24 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -181,7 +181,9 @@ public function formatFile($file, $line, $text = null) } } - $text = "$text at line $line"; + if (0 < $line) { + $text .= ' at line '.$line; + } if (false !== $link = $this->getFileLink($file, $line)) { return sprintf('%s', htmlspecialchars($link, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset), $text); diff --git a/src/Symfony/Bridge/Twig/README.md b/src/Symfony/Bridge/Twig/README.md index eb084147c37f8..602f5a54c3dd6 100644 --- a/src/Symfony/Bridge/Twig/README.md +++ b/src/Symfony/Bridge/Twig/README.md @@ -1,7 +1,7 @@ Twig Bridge =========== -Provides integration for [Twig](http://twig.sensiolabs.org/) with various +Provides integration for [Twig](https://twig.symfony.com/) with various Symfony components. Resources diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index df88a00011883..43c031b933366 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -106,8 +106,7 @@ {%- endblock dateinterval_widget %} {% block percent_widget -%} -
{{ exception.class }}: {% if exception.message is not empty %} {{- exception.message }} @@ -8,5 +7,4 @@ {% for trace in exception.trace %} {{ include('@Twig/Exception/trace.txt.twig', { trace: trace }, with_context = false) }} {% endfor %} -{% endif %} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig index ffe7f31ad5ec4..318a5bbeeb0c7 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig @@ -17,7 +17,13 @@
+ {%- filter escape('html') -%} + {{- include('@Twig/Exception/traces.txt.twig', { exception: exception, format: 'html' }, with_context = false) }} + {% endfilter %} ++ {% endif %}
+ *
+ * @internal
*/
trait ProxyTrait
{
diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php
index ac8b5a5fccda2..b8e05d9e417c9 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -240,7 +240,7 @@ protected function doClear($namespace)
$cursor = null;
do {
$keys = $host instanceof \Predis\Client ? $host->scan($cursor, 'MATCH', $namespace.'*', 'COUNT', 1000) : $host->scan($cursor, $namespace.'*', 1000);
- if (isset($keys[1]) && is_array($keys[1])) {
+ if (isset($keys[1]) && \is_array($keys[1])) {
$cursor = $keys[0];
$keys = $keys[1];
}
diff --git a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php
index 35c87c2207da6..f60323becd88e 100644
--- a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php
@@ -121,7 +121,7 @@ private function getInputOptionData(InputOption $option)
{
return array(
'name' => '--'.$option->getName(),
- 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '',
+ 'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '',
'accept_value' => $option->acceptValue(),
'is_value_required' => $option->isValueRequired(),
'is_multiple' => $option->isArray(),
diff --git a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
index 106bff5114992..d52ba55342fa0 100644
--- a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
+++ b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
@@ -70,7 +70,7 @@ protected function describeInputOption(InputOption $option, array $options = arr
{
$name = '--'.$option->getName();
if ($option->getShortcut()) {
- $name .= '|-'.implode('|-', explode('|', $option->getShortcut())).'';
+ $name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
}
$this->write(
diff --git a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php
index 0d239868c4c99..a2eaca3b18ff2 100644
--- a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php
+++ b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php
@@ -218,7 +218,7 @@ private function getInputOptionDocument(InputOption $option): \DOMDocument
$pos = strpos($option->getShortcut(), '|');
if (false !== $pos) {
$objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
- $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut())));
+ $objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut()));
} else {
$objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
}
diff --git a/src/Symfony/Component/Console/EventListener/ErrorListener.php b/src/Symfony/Component/Console/EventListener/ErrorListener.php
index 3774f9e6666d4..909d6ea3a1ddb 100644
--- a/src/Symfony/Component/Console/EventListener/ErrorListener.php
+++ b/src/Symfony/Component/Console/EventListener/ErrorListener.php
@@ -40,10 +40,10 @@ public function onConsoleError(ConsoleErrorEvent $event)
$error = $event->getError();
if (!$inputString = $this->getInputString($event)) {
- return $this->logger->error('An error occurred while using the console. Message: "{message}"', array('error' => $error, 'message' => $error->getMessage()));
+ return $this->logger->error('An error occurred while using the console. Message: "{message}"', array('exception' => $error, 'message' => $error->getMessage()));
}
- $this->logger->error('Error thrown while running command "{command}". Message: "{message}"', array('error' => $error, 'command' => $inputString, 'message' => $error->getMessage()));
+ $this->logger->error('Error thrown while running command "{command}". Message: "{message}"', array('exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()));
}
public function onConsoleTerminate(ConsoleTerminateEvent $event)
diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php
index dc088a0565ae8..46e1383e786d9 100644
--- a/src/Symfony/Component/Console/Helper/QuestionHelper.php
+++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php
@@ -261,7 +261,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
foreach ($autocomplete as $value) {
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
- if (0 === strpos($value, $ret) && $i !== strlen($value)) {
+ if (0 === strpos($value, $ret)) {
$matches[$numMatches++] = $value;
}
}
diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php
index e4c855cae1706..184e369b86a52 100644
--- a/src/Symfony/Component/Console/Input/InputOption.php
+++ b/src/Symfony/Component/Console/Input/InputOption.php
@@ -195,7 +195,7 @@ public function getDescription()
*
* @return bool
*/
- public function equals(InputOption $option)
+ public function equals(self $option)
{
return $option->getName() === $this->getName()
&& $option->getShortcut() === $this->getShortcut()
diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php
index 0f3d2aa06a172..476b2fa0dc05c 100644
--- a/src/Symfony/Component/Console/Output/StreamOutput.php
+++ b/src/Symfony/Component/Console/Output/StreamOutput.php
@@ -83,31 +83,34 @@ protected function doWrite($message, $newline)
*
* Colorization is disabled if not supported by the stream:
*
- * - the stream is redirected (eg php file.php >log)
- * - Windows without VT100 support, Ansicon, ConEmu, Mintty
- * - non tty consoles
+ * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
+ * terminals via named pipes, so we can only check the environment.
+ *
+ * Reference: Composer\XdebugHandler\Process::supportsColor
+ * https://github.com/composer/xdebug-handler
*
* @return bool true if the stream supports colorization, false otherwise
*/
protected function hasColorSupport()
{
- if (function_exists('stream_isatty') && !@stream_isatty($this->stream)) {
- return false;
- }
if (DIRECTORY_SEPARATOR === '\\') {
- if (function_exists('sapi_windows_vt100_support')) {
- $vt100Enabled = @sapi_windows_vt100_support($this->stream);
- } else {
- $vt100Enabled = '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD;
- }
-
- return
- $vt100Enabled
+ return (function_exists('sapi_windows_vt100_support')
+ && @sapi_windows_vt100_support($this->stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}
- return function_exists('posix_isatty') && @posix_isatty($this->stream);
+ if (function_exists('stream_isatty')) {
+ return @stream_isatty($this->stream);
+ }
+
+ if (function_exists('posix_isatty')) {
+ return @posix_isatty($this->stream);
+ }
+
+ $stat = @fstat($this->stream);
+ // Check if formatted mode is S_IFCHR
+ return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
}
}
diff --git a/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php b/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php
index 17eaae09084c8..3794a2660ab5f 100644
--- a/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php
+++ b/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php
@@ -34,7 +34,7 @@ public function testOnConsoleError()
$logger
->expects($this->once())
->method('error')
- ->with('Error thrown while running command "{command}". Message: "{message}"', array('error' => $error, 'command' => 'test:run --foo=baz buzz', 'message' => 'An error occurred'))
+ ->with('Error thrown while running command "{command}". Message: "{message}"', array('exception' => $error, 'command' => 'test:run --foo=baz buzz', 'message' => 'An error occurred'))
;
$listener = new ErrorListener($logger);
@@ -49,7 +49,7 @@ public function testOnConsoleErrorWithNoCommandAndNoInputString()
$logger
->expects($this->once())
->method('error')
- ->with('An error occurred while using the console. Message: "{message}"', array('error' => $error, 'message' => 'An error occurred'))
+ ->with('An error occurred while using the console. Message: "{message}"', array('exception' => $error, 'message' => 'An error occurred'))
;
$listener = new ErrorListener($logger);
diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
index 3b8c07d39379b..ea5268cae3d65 100644
--- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
@@ -157,6 +157,29 @@ public function testAskWithAutocompleteWithNonSequentialKeys()
$this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
+ public function testAskWithAutocompleteWithExactMatch()
+ {
+ if (!$this->hasSttyAvailable()) {
+ $this->markTestSkipped('`stty` is required to test autocomplete functionality');
+ }
+
+ $inputStream = $this->getInputStream("b\n");
+
+ $possibleChoices = array(
+ 'a' => 'berlin',
+ 'b' => 'copenhagen',
+ 'c' => 'amsterdam',
+ );
+
+ $dialog = new QuestionHelper();
+ $dialog->setHelperSet(new HelperSet(array(new FormatterHelper())));
+
+ $question = new ChoiceQuestion('Please select a city', $possibleChoices);
+ $question->setMaxAttempts(1);
+
+ $this->assertSame('b', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ }
+
public function testAutocompleteWithTrailingBackslash()
{
if (!$this->hasSttyAvailable()) {
diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json
index b11991944435f..371818fc5fd17 100644
--- a/src/Symfony/Component/Console/composer.json
+++ b/src/Symfony/Component/Console/composer.json
@@ -31,7 +31,7 @@
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": "",
- "psr/log": "For using the console logger"
+ "psr/log-implementation": "For using the console logger"
},
"conflict": {
"symfony/dependency-injection": "<3.4",
diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php
index a6ae71f8f1091..a67637ea6e655 100644
--- a/src/Symfony/Component/Debug/ExceptionHandler.php
+++ b/src/Symfony/Component/Debug/ExceptionHandler.php
@@ -40,7 +40,7 @@ public function __construct(bool $debug = true, string $charset = null, $fileLin
{
$this->debug = $debug;
$this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8';
- $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
+ $this->fileLinkFormat = $fileLinkFormat;
}
/**
@@ -355,13 +355,29 @@ private function formatClass($class)
private function formatPath($path, $line)
{
$file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path);
- $fmt = $this->fileLinkFormat;
+ $fmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
+
+ if (!$fmt) {
+ return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : '');
+ }
+
+ if (\is_string($fmt)) {
+ $i = strpos($f = $fmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: strlen($f);
+ $fmt = array(substr($f, 0, $i)) + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ for ($i = 1; isset($fmt[$i]); ++$i) {
+ if (0 === strpos($path, $k = $fmt[$i++])) {
+ $path = substr_replace($path, $fmt[$i], 0, strlen($k));
+ break;
+ }
+ }
- if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $path, '%l' => $line)) : $fmt->format($path, $line)) {
- return sprintf('in %s (line %d)', $this->escapeHtml($link), $file, $line);
+ $link = strtr($fmt[0], array('%f' => $path, '%l' => $line));
+ } else {
+ $link = $fmt->format($path, $line);
}
- return sprintf('in %s (line %d)', $this->escapeHtml($path), $file, $line);
+ return sprintf('in %s%s', $this->escapeHtml($link), $file, 0 < $line ? ' line '.$line : '');
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
index a798482656ca6..605d36079b192 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
@@ -179,7 +179,10 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
if ($parameter->isOptional()) {
continue;
}
- throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" must have a type-hint or be given a value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
+ $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false);
+ $type = $type ? sprintf('is type-hinted "%s"', $type) : 'has no type-hint';
+
+ throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type));
}
// specifically pass the default value
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
index 73ca29d35f424..c82a974360674 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
@@ -15,6 +15,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\DependencyInjection\Reference;
@@ -88,8 +89,14 @@ protected function processValue($value, $isRoot = false)
$calls = $value->getMethodCalls();
- if ($constructor = $this->getConstructor($value, false)) {
- $calls[] = array($constructor, $value->getArguments());
+ try {
+ if ($constructor = $this->getConstructor($value, false)) {
+ $calls[] = array($constructor, $value->getArguments());
+ }
+ } catch (RuntimeException $e) {
+ $this->container->getDefinition($this->currentId)->addError($e->getMessage());
+
+ return parent::processValue($value, $isRoot);
}
foreach ($calls as $i => $call) {
diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
index f28726c11a55f..d9657ce479d31 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
@@ -123,6 +123,20 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
private $removedIds = array();
private $alreadyLoading = array();
+ private static $internalTypes = array(
+ 'int' => true,
+ 'float' => true,
+ 'string' => true,
+ 'bool' => true,
+ 'resource' => true,
+ 'object' => true,
+ 'array' => true,
+ 'null' => true,
+ 'callable' => true,
+ 'iterable' => true,
+ 'mixed' => true,
+ );
+
public function __construct(ParameterBagInterface $parameterBag = null)
{
parent::__construct($parameterBag);
@@ -321,6 +335,11 @@ public function getReflectionClass(?string $class, bool $throw = true): ?\Reflec
if (!$class = $this->getParameterBag()->resolveValue($class)) {
return null;
}
+
+ if (isset(self::$internalTypes[$class])) {
+ return null;
+ }
+
$resource = null;
try {
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
index 84fab467cee43..9e51b65eed64d 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
@@ -373,6 +373,7 @@ public function testSomeSpecificArgumentsAreSet()
// args are: A, Foo, Dunglas
->setArguments(array(
1 => new Reference('foo'),
+ 3 => array('bar'),
));
(new ResolveClassPass())->process($container);
@@ -384,6 +385,7 @@ public function testSomeSpecificArgumentsAreSet()
new TypedReference(A::class, A::class, MultipleArguments::class),
new Reference('foo'),
new TypedReference(Dunglas::class, Dunglas::class, MultipleArguments::class),
+ array('bar'),
),
$definition->getArguments()
);
@@ -391,12 +393,30 @@ public function testSomeSpecificArgumentsAreSet()
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
- * @expectedExceptionMessage Cannot autowire service "arg_no_type_hint": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\MultipleArguments::__construct()" must have a type-hint or be given a value explicitly.
+ * @expectedExceptionMessage Cannot autowire service "arg_no_type_hint": argument "$bar" of method "Symfony\Component\DependencyInjection\Tests\Compiler\MultipleArguments::__construct()" is type-hinted "array", you should configure its value explicitly.
*/
public function testScalarArgsCannotBeAutowired()
{
$container = new ContainerBuilder();
+ $container->register(A::class);
+ $container->register(Dunglas::class);
+ $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
+ ->setArguments(array(1 => 'foo'))
+ ->setAutowired(true);
+
+ (new ResolveClassPass())->process($container);
+ (new AutowirePass())->process($container);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
+ * @expectedExceptionMessage Cannot autowire service "arg_no_type_hint": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\MultipleArguments::__construct()" has no type-hint, you should configure its value explicitly.
+ */
+ public function testNoTypeArgsCannotBeAutowired()
+ {
+ $container = new ContainerBuilder();
+
$container->register(A::class);
$container->register(Dunglas::class);
$container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index 1b719938a9d0f..1f808d1ca9a4b 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -880,6 +880,23 @@ public function testGetReflectionClass()
$this->assertSame('BarMissingClass', (string) end($resources));
}
+ public function testGetReflectionClassOnInternalTypes()
+ {
+ $container = new ContainerBuilder();
+
+ $this->assertNull($container->getReflectionClass('int'));
+ $this->assertNull($container->getReflectionClass('float'));
+ $this->assertNull($container->getReflectionClass('string'));
+ $this->assertNull($container->getReflectionClass('bool'));
+ $this->assertNull($container->getReflectionClass('resource'));
+ $this->assertNull($container->getReflectionClass('object'));
+ $this->assertNull($container->getReflectionClass('array'));
+ $this->assertNull($container->getReflectionClass('null'));
+ $this->assertNull($container->getReflectionClass('callable'));
+ $this->assertNull($container->getReflectionClass('iterable'));
+ $this->assertNull($container->getReflectionClass('mixed'));
+ }
+
public function testCompilesClassDefinitionsOfLazyServices()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
index 04c17ee188fa7..77f8f61337b1b 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
@@ -181,7 +181,7 @@ public function __construct(A $k)
}
class MultipleArguments
{
- public function __construct(A $k, $foo, Dunglas $dunglas)
+ public function __construct(A $k, $foo, Dunglas $dunglas, array $bar)
{
}
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
index e536efac37ce3..f4590b29a4bcc 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
@@ -319,6 +319,7 @@ public function configureOptions(OptionsResolver $resolver)
// See https://github.com/symfony/symfony/pull/5582
'data_class' => null,
'choice_translation_domain' => true,
+ 'trim' => false,
));
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php
index c906c6549ad2f..016792e0edaf6 100644
--- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php
@@ -32,7 +32,7 @@ public function testRow()
[
./label[@for="name"]
[
- ./span[@class="alert alert-danger"]
+ ./span[@class="alert alert-danger d-block"]
[./span[@class="mb-0 d-block"]
[./span[.="[trans]Error[/trans]"]]
[./span[.="[trans]Error![/trans]"]]
diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php
index beabaa21cdb1a..2cadba0ba9e67 100644
--- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php
@@ -32,7 +32,7 @@ public function testRow()
[
./label[@for="name"]
[
- ./span[@class="alert alert-danger"]
+ ./span[@class="alert alert-danger d-block"]
[./span[@class="mb-0 d-block"]
[./span[.="[trans]Error[/trans]"]]
[./span[.="[trans]Error![/trans]"]]
@@ -161,7 +161,7 @@ public function testErrors()
$this->assertMatchesXpath($html,
'/span
- [@class="alert alert-danger"]
+ [@class="alert alert-danger d-block"]
[
./span[@class="mb-0 d-block"]
[./span[.="[trans]Error[/trans]"]]
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
index 5ae3c53689ea6..9cbe941581418 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
@@ -1880,4 +1880,176 @@ public function invalidNestedValueTestMatrix()
'multiple, expanded' => array(true, true, array(array())),
);
}
+
+ public function testInheritTranslationDomainFromParent()
+ {
+ $view = $this->factory
+ ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, array(
+ 'translation_domain' => 'domain',
+ ))
+ ->add('child', static::TESTED_TYPE)
+ ->getForm()
+ ->createView();
+
+ $this->assertEquals('domain', $view['child']->vars['translation_domain']);
+ }
+
+ public function testPassTranslationDomainToView()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, array(
+ 'translation_domain' => 'domain',
+ ))
+ ->createView();
+
+ $this->assertSame('domain', $view->vars['translation_domain']);
+ }
+
+ public function testPreferOwnTranslationDomain()
+ {
+ $view = $this->factory
+ ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, array(
+ 'translation_domain' => 'parent_domain',
+ ))
+ ->add('child', static::TESTED_TYPE, array(
+ 'translation_domain' => 'domain',
+ ))
+ ->getForm()
+ ->createView();
+
+ $this->assertEquals('domain', $view['child']->vars['translation_domain']);
+ }
+
+ public function testDefaultTranslationDomain()
+ {
+ $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE)
+ ->add('child', static::TESTED_TYPE)
+ ->getForm()
+ ->createView();
+
+ $this->assertNull($view['child']->vars['translation_domain']);
+ }
+
+ public function testPassMultipartFalseToView()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null)
+ ->createView();
+
+ $this->assertFalse($view->vars['multipart']);
+ }
+
+ public function testPassLabelToView()
+ {
+ $view = $this->factory->createNamed('__test___field', static::TESTED_TYPE, null, array(
+ 'label' => 'My label',
+ ))
+ ->createView();
+
+ $this->assertSame('My label', $view->vars['label']);
+ }
+
+ public function testPassIdAndNameToViewWithGrandParent()
+ {
+ $builder = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE)
+ ->add('child', FormTypeTest::TESTED_TYPE);
+ $builder->get('child')->add('grand_child', static::TESTED_TYPE);
+ $view = $builder->getForm()->createView();
+
+ $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->vars['id']);
+ $this->assertEquals('grand_child', $view['child']['grand_child']->vars['name']);
+ $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->vars['full_name']);
+ }
+
+ public function testPassIdAndNameToViewWithParent()
+ {
+ $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE)
+ ->add('child', static::TESTED_TYPE)
+ ->getForm()
+ ->createView();
+
+ $this->assertEquals('parent_child', $view['child']->vars['id']);
+ $this->assertEquals('child', $view['child']->vars['name']);
+ $this->assertEquals('parent[child]', $view['child']->vars['full_name']);
+ }
+
+ public function testPassDisabledAsOption()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, array(
+ 'disabled' => true,
+ ));
+
+ $this->assertTrue($form->isDisabled());
+ }
+
+ public function testPassIdAndNameToView()
+ {
+ $view = $this->factory->createNamed('name', static::TESTED_TYPE, null)
+ ->createView();
+
+ $this->assertEquals('name', $view->vars['id']);
+ $this->assertEquals('name', $view->vars['name']);
+ $this->assertEquals('name', $view->vars['full_name']);
+ }
+
+ public function testStripLeadingUnderscoresAndDigitsFromId()
+ {
+ $view = $this->factory->createNamed('_09name', static::TESTED_TYPE, null)
+ ->createView();
+
+ $this->assertEquals('name', $view->vars['id']);
+ $this->assertEquals('_09name', $view->vars['name']);
+ $this->assertEquals('_09name', $view->vars['full_name']);
+ }
+
+ /**
+ * @dataProvider provideTrimCases
+ */
+ public function testTrimIsDisabled($multiple, $expanded)
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, array(
+ 'multiple' => $multiple,
+ 'expanded' => $expanded,
+ 'choices' => array(
+ 'a' => '1',
+ ),
+ ));
+
+ $submittedData = ' 1';
+
+ $form->submit($multiple ? (array) $submittedData : $submittedData);
+
+ // When the choice does not exist the transformation fails
+ $this->assertFalse($form->isSynchronized());
+ $this->assertNull($form->getData());
+ }
+
+ /**
+ * @dataProvider provideTrimCases
+ */
+ public function testSubmitValueWithWhiteSpace($multiple, $expanded)
+ {
+ $valueWhitWhiteSpace = '1 ';
+
+ $form = $this->factory->create(static::TESTED_TYPE, null, array(
+ 'multiple' => $multiple,
+ 'expanded' => $expanded,
+ 'choices' => array(
+ 'a' => $valueWhitWhiteSpace,
+ ),
+ ));
+
+ $form->submit($multiple ? (array) $valueWhitWhiteSpace : $valueWhitWhiteSpace);
+
+ $this->assertTrue($form->isSynchronized());
+ $this->assertSame($multiple ? (array) $valueWhitWhiteSpace : $valueWhitWhiteSpace, $form->getData());
+ }
+
+ public function provideTrimCases()
+ {
+ return array(
+ 'Simple' => array(false, false),
+ 'Multiple' => array(true, false),
+ 'Simple expanded' => array(false, true),
+ 'Multiple expanded' => array(true, true),
+ );
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json
index acbb82aa8fe49..481a1333d1982 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json
@@ -21,7 +21,8 @@
"compound",
"data_class",
"empty_data",
- "error_bubbling"
+ "error_bubbling",
+ "trim"
]
},
"parent": {
@@ -43,7 +44,6 @@
"property_path",
"required",
"translation_domain",
- "trim",
"upload_max_size_message"
]
},
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
index 6d698a6171f15..d8f6d47f54cb2 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
@@ -11,7 +11,7 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
choice_name data_class attr csrf_message
choice_translation_domain empty_data auto_initialize csrf_protection
choice_value error_bubbling block_name csrf_token_id
- choices by_reference csrf_token_manager
+ choices trim by_reference csrf_token_manager
expanded data
group_by disabled
multiple inherit_data
@@ -24,7 +24,6 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
property_path
required
translation_domain
- trim
upload_max_size_message
--------------------------- -------------------- ------------------------- -----------------------
diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php
index 78b67bb3d5d44..cc9de5adf7fcd 100644
--- a/src/Symfony/Component/HttpFoundation/Cookie.php
+++ b/src/Symfony/Component/HttpFoundation/Cookie.php
@@ -145,12 +145,12 @@ public function __toString()
$str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
if ('' === (string) $this->getValue()) {
- $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001';
+ $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';
} else {
$str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue());
if (0 !== $this->getExpiresTime()) {
- $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; max-age='.$this->getMaxAge();
+ $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; Max-Age='.$this->getMaxAge();
}
}
@@ -224,7 +224,9 @@ public function getExpiresTime()
*/
public function getMaxAge()
{
- return 0 !== $this->expire ? $this->expire - time() : 0;
+ $maxAge = $this->expire - time();
+
+ return 0 >= $maxAge ? 0 : $maxAge;
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
index b00f58c4e8a27..83be44af67d2d 100644
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
+++ b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
@@ -43,7 +43,21 @@ public function __construct(string $cmd = 'file -b --mime %s 2>/dev/null')
*/
public static function isSupported()
{
- return '\\' !== DIRECTORY_SEPARATOR && function_exists('passthru') && function_exists('escapeshellarg');
+ static $supported = null;
+
+ if (null !== $supported) {
+ return $supported;
+ }
+
+ if ('\\' === DIRECTORY_SEPARATOR || !function_exists('passthru') || !function_exists('escapeshellarg')) {
+ return $supported = false;
+ }
+
+ ob_start();
+ passthru('command -v file', $exitStatus);
+ $binPath = trim(ob_get_clean());
+
+ return $supported = 0 === $exitStatus && '' !== $binPath;
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php
index c733c5e1dd69e..7cc9d91ab40a5 100644
--- a/src/Symfony/Component/HttpFoundation/Response.php
+++ b/src/Symfony/Component/HttpFoundation/Response.php
@@ -21,6 +21,7 @@ class Response
const HTTP_CONTINUE = 100;
const HTTP_SWITCHING_PROTOCOLS = 101;
const HTTP_PROCESSING = 102; // RFC2518
+ const HTTP_EARLY_HINTS = 103; // RFC8297
const HTTP_OK = 200;
const HTTP_CREATED = 201;
const HTTP_ACCEPTED = 202;
@@ -323,7 +324,7 @@ public function sendHeaders()
}
// headers
- foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
+ foreach ($this->headers->allPreserveCase() as $name => $values) {
foreach ($values as $value) {
header($name.': '.$value, false, $this->statusCode);
}
@@ -332,15 +333,6 @@ public function sendHeaders()
// status
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
- // cookies
- foreach ($this->headers->getCookies() as $cookie) {
- if ($cookie->isRaw()) {
- setrawcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
- } else {
- setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
- }
- }
-
return $this;
}
@@ -502,13 +494,19 @@ public function getCharset(): ?string
}
/**
- * Returns true if the response is worth caching under any circumstance.
+ * Returns true if the response may safely be kept in a shared (surrogate) cache.
*
* Responses marked "private" with an explicit Cache-Control directive are
* considered uncacheable.
*
* Responses with neither a freshness lifetime (Expires, max-age) nor cache
- * validator (Last-Modified, ETag) are considered uncacheable.
+ * validator (Last-Modified, ETag) are considered uncacheable because there is
+ * no way to tell when or how to remove them from the cache.
+ *
+ * Note that RFC 7231 and RFC 7234 possibly allow for a more permissive implementation,
+ * for example "status codes that are defined as cacheable by default [...]
+ * can be reused by a cache with heuristic expiration unless otherwise indicated"
+ * (https://tools.ietf.org/html/rfc7231#section-6.1)
*
* @final
*/
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
index 6a1dd634ad7c4..12e32df36afd5 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
@@ -346,20 +346,20 @@ public function setOptions(array $options)
}
$validOptions = array_flip(array(
- 'cache_limiter', 'cache_expire', 'cookie_domain', 'cookie_httponly',
+ 'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
'cookie_lifetime', 'cookie_path', 'cookie_secure',
'gc_divisor', 'gc_maxlifetime', 'gc_probability',
'lazy_write', 'name', 'referer_check',
'serialize_handler', 'use_strict_mode', 'use_cookies',
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
- 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
+ 'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags',
'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
));
foreach ($options as $key => $value) {
if (isset($validOptions[$key])) {
- ini_set('session.'.$key, $value);
+ ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
}
}
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php
index 264fafa097596..5abb5814080f2 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php
@@ -159,13 +159,13 @@ public function testCookieIsCleared()
public function testToString()
{
$cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
- $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; max-age='.($expire - time()).'; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
+ $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
$cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
- $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; max-age='.($expire - time()).'; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
+ $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
$cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com');
- $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; max-age='.($expire - time()).'; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
+ $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
$cookie = new Cookie('foo', 'bar', 0, '/', '');
$this->assertEquals('foo=bar; path=/; httponly', (string) $cookie);
@@ -191,7 +191,7 @@ public function testGetMaxAge()
$this->assertEquals($expire - time(), $cookie->getMaxAge());
$cookie = new Cookie('foo', 'bar', $expire = time() - 100);
- $this->assertEquals($expire - time(), $cookie->getMaxAge());
+ $this->assertEquals(0, $cookie->getMaxAge());
}
public function testFromString()
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc
new file mode 100644
index 0000000000000..ba101d357852d
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc
@@ -0,0 +1,39 @@
+headers->set('Date', 'Sat, 12 Nov 1955 20:04:00 GMT');
+
+return $r;
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected
new file mode 100644
index 0000000000000..bdb9d023f8f3d
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected
@@ -0,0 +1,11 @@
+
+Warning: Expiry date cannot have a year greater than 9999 in %scookie_max_age.php on line 10
+
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: no-cache, private
+ [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+ [3] => Set-Cookie: foo=bar; expires=Sat, 01-Jan-10000 02:46:40 GMT; Max-Age=%d; path=/
+)
+shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.php
new file mode 100644
index 0000000000000..8775a5cceeb88
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.php
@@ -0,0 +1,10 @@
+headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false));
+$r->sendHeaders();
+
+setcookie('foo2', 'bar', 253402310800, '/');
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected
new file mode 100644
index 0000000000000..0c097972e78e2
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected
@@ -0,0 +1,10 @@
+
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: no-cache, private
+ [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+ [3] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/
+ [4] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/
+)
+shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php
new file mode 100644
index 0000000000000..2ca5b59f1a3e5
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php
@@ -0,0 +1,12 @@
+headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true));
+$r->sendHeaders();
+
+setrawcookie($str, $str, 0, '/', null, false, false);
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected
new file mode 100644
index 0000000000000..cbde2cbfe13f3
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected
@@ -0,0 +1,9 @@
+
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: no-cache, private
+ [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+ [3] => Set-Cookie: CookieSamesiteLaxTest=LaxValue; path=/; httponly; samesite=lax
+)
+shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php
new file mode 100644
index 0000000000000..9a476f1d23fab
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php
@@ -0,0 +1,8 @@
+headers->setCookie(new Cookie('CookieSamesiteLaxTest', 'LaxValue', 0, '/', null, false, true, false, Cookie::SAMESITE_LAX));
+$r->sendHeaders();
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected
new file mode 100644
index 0000000000000..adc491fd2bc51
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected
@@ -0,0 +1,9 @@
+
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: no-cache, private
+ [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+ [3] => Set-Cookie: CookieSamesiteStrictTest=StrictValue; path=/; httponly; samesite=strict
+)
+shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php
new file mode 100644
index 0000000000000..3bcb41f8f059e
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php
@@ -0,0 +1,8 @@
+headers->setCookie(new Cookie('CookieSamesiteStrictTest', 'StrictValue', 0, '/', null, false, true, false, Cookie::SAMESITE_STRICT));
+$r->sendHeaders();
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected
new file mode 100644
index 0000000000000..4e9c4c075f5ed
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected
@@ -0,0 +1,10 @@
+
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: no-cache, private
+ [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+ [3] => Set-Cookie: %3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
+ [4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
+)
+shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.php
new file mode 100644
index 0000000000000..05b9af30d58f2
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.php
@@ -0,0 +1,12 @@
+headers->setCookie(new Cookie($str, $str, 0, '', null, false, false));
+$r->sendHeaders();
+
+setcookie($str, $str, 0, '/');
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected
new file mode 100644
index 0000000000000..2b560f0bd5689
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected
@@ -0,0 +1,6 @@
+The cookie name "Hello + world" contains invalid characters.
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+)
+shutdown
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php
new file mode 100644
index 0000000000000..3fe1571845628
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php
@@ -0,0 +1,11 @@
+headers->setCookie(new Cookie('Hello + world', 'hodor'));
+} catch (\InvalidArgumentException $e) {
+ echo $e->getMessage();
+}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php
new file mode 100644
index 0000000000000..22f25e978e5a2
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @requires PHP 7.0
+ */
+class ResponseFunctionalTest extends TestCase
+{
+ private static $server;
+
+ public static function setUpBeforeClass()
+ {
+ $spec = array(
+ 1 => array('file', '/dev/null', 'w'),
+ 2 => array('file', '/dev/null', 'w'),
+ );
+ if (!self::$server = @proc_open('exec php -S localhost:8054', $spec, $pipes, __DIR__.'/Fixtures/response-functional')) {
+ self::markTestSkipped('PHP server unable to start.');
+ }
+ sleep(1);
+ }
+
+ public static function tearDownAfterClass()
+ {
+ if (self::$server) {
+ proc_terminate(self::$server);
+ proc_close(self::$server);
+ }
+ }
+
+ /**
+ * @dataProvider provideCookie
+ */
+ public function testCookie($fixture)
+ {
+ $result = file_get_contents(sprintf('http://localhost:8054/%s.php', $fixture));
+ $this->assertStringMatchesFormatFile(__DIR__.sprintf('/Fixtures/response-functional/%s.expected', $fixture), $result);
+ }
+
+ public function provideCookie()
+ {
+ foreach (glob(__DIR__.'/Fixtures/response-functional/*.php') as $file) {
+ yield array(pathinfo($file, PATHINFO_FILENAME));
+ }
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php
index ce8553590dcdb..7b5e720afdb61 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php
@@ -116,7 +116,7 @@ public function testToStringIncludesCookieHeaders()
$bag->clearCookie('foo');
- $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001; path=/; httponly', $bag);
+ $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; httponly', $bag);
}
public function testClearCookieSecureNotHttpOnly()
@@ -125,7 +125,7 @@ public function testClearCookieSecureNotHttpOnly()
$bag->clearCookie('foo', '/', null, true, false);
- $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001; path=/; secure', $bag);
+ $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag);
}
public function testReplace()
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
index 8fb8b4222835b..382707b0c399e 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -182,6 +182,23 @@ public function testCookieOptions()
$this->assertEquals($options, $gco);
}
+ public function testSessionOptions()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM is not handled in this test case.');
+ }
+
+ $options = array(
+ 'url_rewriter.tags' => 'a=href',
+ 'cache_expire' => '200',
+ );
+
+ $this->getStorage($options);
+
+ $this->assertSame('a=href', ini_get('url_rewriter.tags'));
+ $this->assertSame('200', ini_get('session.cache_expire'));
+ }
+
/**
* @expectedException \InvalidArgumentException
*/
diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php
index c55564c0467ef..7bc195f233114 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php
@@ -39,9 +39,15 @@ public function supports(Request $request, ArgumentMetadata $argument)
if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) {
$controller = $controller[0].'::'.$controller[1];
+ } elseif (!\is_string($controller) || '' === $controller) {
+ return false;
}
- return \is_string($controller) && $this->container->has($controller) && $this->container->get($controller)->has($argument->getName());
+ if ('\\' === $controller[0]) {
+ $controller = ltrim($controller, '\\');
+ }
+
+ return $this->container->has($controller) && $this->container->get($controller)->has($argument->getName());
}
/**
@@ -53,6 +59,10 @@ public function resolve(Request $request, ArgumentMetadata $argument)
$controller = $controller[0].'::'.$controller[1];
}
+ if ('\\' === $controller[0]) {
+ $controller = ltrim($controller, '\\');
+ }
+
yield $this->container->get($controller)->get($argument->getName());
}
}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
index 0a153dd943297..82061fd6ea0fc 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
@@ -69,7 +69,7 @@ public function onKernelResponse(FilterResponseEvent $event)
$session->save();
}
- if ($session instanceof Session ? !$session->isEmpty() || $session->getId() !== $this->sessionId : $wasStarted) {
+ if ($session instanceof Session ? !$session->isEmpty() || (null !== $this->sessionId && $session->getId() !== $this->sessionId) : $wasStarted) {
$params = session_get_cookie_params();
$event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']));
$this->sessionId = $session->getId();
diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
index f18e42c7d3693..3dfa4cd8ea79a 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php
@@ -12,9 +12,11 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Psr\Log\LoggerInterface;
+use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
@@ -33,12 +35,14 @@ class ExceptionListener implements EventSubscriberInterface
protected $controller;
protected $logger;
protected $debug;
+ private $charset;
- public function __construct($controller, LoggerInterface $logger = null, $debug = false)
+ public function __construct($controller, LoggerInterface $logger = null, $debug = false, $charset = null)
{
$this->controller = $controller;
$this->logger = $logger;
$this->debug = $debug;
+ $this->charset = $charset;
}
public function onKernelException(GetResponseForExceptionEvent $event)
@@ -117,8 +121,12 @@ protected function logException(\Exception $exception, $message)
protected function duplicateRequest(\Exception $exception, Request $request)
{
$attributes = array(
- '_controller' => $this->controller,
- 'exception' => FlattenException::create($exception),
+ 'exception' => $exception = FlattenException::create($exception),
+ '_controller' => $this->controller ?: function () use ($exception) {
+ $handler = new ExceptionHandler($this->debug, $this->charset);
+
+ return new Response($handler->getHtml($exception), $exception->getStatusCode(), $exception->getHeaders());
+ },
'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null,
);
$request = $request->duplicate(null, null, $attributes);
diff --git a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php
index 027b2b1761334..672cc893feb6d 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php
@@ -72,7 +72,7 @@ public function update(Response $response)
$response->setLastModified(null);
}
- if (!$response->isFresh()) {
+ if (!$response->isFresh() || !$response->isCacheable()) {
$this->cacheable = false;
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index db80323c206d9..24cce648ef443 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -63,11 +63,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private $requestStackSize = 0;
private $resetServices = false;
- const VERSION = '4.0.8';
- const VERSION_ID = 40008;
+ const VERSION = '4.0.9';
+ const VERSION_ID = 40009;
const MAJOR_VERSION = 4;
const MINOR_VERSION = 0;
- const RELEASE_VERSION = 8;
+ const RELEASE_VERSION = 9;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '07/2018';
@@ -540,9 +540,11 @@ protected function initializeContainer()
// Because concurrent requests might still be using them,
// old container files are not removed immediately,
// but on a next dump of the container.
+ static $legacyContainers = array();
$oldContainerDir = dirname($oldContainer->getFileName());
- foreach (glob(dirname($oldContainerDir).'/*.legacy') as $legacyContainer) {
- if ($oldContainerDir.'.legacy' !== $legacyContainer && @unlink($legacyContainer)) {
+ $legacyContainers[$oldContainerDir.'.legacy'] = true;
+ foreach (glob(dirname($oldContainerDir).DIRECTORY_SEPARATOR.'*.legacy') as $legacyContainer) {
+ if (!isset($legacyContainers[$legacyContainer]) && @unlink($legacyContainer)) {
(new Filesystem())->remove(substr($legacyContainer, 0, -7));
}
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/ClientTest.php b/src/Symfony/Component/HttpKernel/Tests/ClientTest.php
index 051d5d47c0265..b774d8ec7ae1c 100644
--- a/src/Symfony/Component/HttpKernel/Tests/ClientTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/ClientTest.php
@@ -60,22 +60,17 @@ public function testFilterResponseConvertsCookies()
$m = $r->getMethod('filterResponse');
$m->setAccessible(true);
- $expected = array(
- 'foo=bar; expires=Sun, 15-Feb-2009 20:00:00 GMT; max-age='.(strtotime('Sun, 15-Feb-2009 20:00:00 GMT') - time()).'; path=/foo; domain=http://example.com; secure; httponly',
- 'foo1=bar1; expires=Sun, 15-Feb-2009 20:00:00 GMT; max-age='.(strtotime('Sun, 15-Feb-2009 20:00:00 GMT') - time()).'; path=/foo; domain=http://example.com; secure; httponly',
- );
-
$response = new Response();
- $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true));
+ $response->headers->setCookie($cookie1 = new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true));
$domResponse = $m->invoke($client, $response);
- $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie'));
+ $this->assertSame((string) $cookie1, $domResponse->getHeader('Set-Cookie'));
$response = new Response();
- $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true));
- $response->headers->setCookie(new Cookie('foo1', 'bar1', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true));
+ $response->headers->setCookie($cookie1 = new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true));
+ $response->headers->setCookie($cookie2 = new Cookie('foo1', 'bar1', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true));
$domResponse = $m->invoke($client, $response);
- $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie'));
- $this->assertEquals($expected, $domResponse->getHeader('Set-Cookie', false));
+ $this->assertSame((string) $cookie1, $domResponse->getHeader('Set-Cookie'));
+ $this->assertSame(array((string) $cookie1, (string) $cookie2), $domResponse->getHeader('Set-Cookie', false));
}
public function testFilterResponseSupportsStreamedResponses()
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php
index b05828f5bf6d2..7d34172ce3d8f 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php
@@ -47,6 +47,25 @@ public function testExistingController()
$this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument));
}
+ public function testExistingControllerWithATrailingBackSlash()
+ {
+ $resolver = new ServiceValueResolver(new ServiceLocator(array(
+ 'App\\Controller\\Mine::method' => function () {
+ return new ServiceLocator(array(
+ 'dummy' => function () {
+ return new DummyService();
+ },
+ ));
+ },
+ )));
+
+ $request = $this->requestWithAttributes(array('_controller' => '\\App\\Controller\\Mine::method'));
+ $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null);
+
+ $this->assertTrue($resolver->supports($request, $argument));
+ $this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument));
+ }
+
public function testControllerNameIsAnArray()
{
$resolver = new ServiceValueResolver(new ServiceLocator(array(
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php
index 2e8007349a971..d809331cb0998 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php
@@ -67,7 +67,7 @@ public function testCollectDefault()
ob_start();
$collector->collect(new Request(), new Response());
- $output = ob_get_clean();
+ $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean());
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output);
$this->assertSame(1, $collector->getDumpsCount());
@@ -111,7 +111,8 @@ public function testFlush()
ob_start();
$collector->__destruct();
- $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean());
+ $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean());
+ $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output);
}
public function testFlushNothingWhenDataDumperIsProvided()
@@ -123,10 +124,11 @@ public function testFlushNothingWhenDataDumperIsProvided()
ob_start();
$collector->dump($data);
$line = __LINE__ - 1;
+ $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean());
if (\PHP_VERSION_ID >= 50400) {
- $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean());
+ $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output);
} else {
- $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", ob_get_clean());
+ $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", $output);
}
ob_start();
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php
index 3cb0b298bb07a..b607bf900ae91 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php
@@ -151,6 +151,23 @@ public function testCSPHeaderIsRemoved()
$this->assertFalse($response->headers->has('content-security-policy'), 'CSP header has been removed');
$this->assertFalse($dispatcher->hasListeners(KernelEvents::RESPONSE), 'CSP removal listener has been removed');
}
+
+ public function testNullController()
+ {
+ $listener = new ExceptionListener(null);
+ $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
+ $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) {
+ $controller = $request->attributes->get('_controller');
+
+ return $controller();
+ }));
+ $request = Request::create('/');
+ $event = new GetResponseForExceptionEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, new \Exception('foo'));
+
+ $listener->onKernelException($event);
+
+ $this->assertContains('Whoops, looks like something went wrong.', $event->getResponse()->getContent());
+ }
}
class TestLogger extends Logger implements DebugLoggerInterface
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php
index 0a2263d5a8e78..22a2b71239874 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php
@@ -13,7 +13,6 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
-use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -46,6 +45,9 @@ protected function setUp()
{
$this->listener = $this->getMockForAbstractClass('Symfony\Component\HttpKernel\EventListener\AbstractTestSessionListener');
$this->session = $this->getSession();
+ $this->listener->expects($this->any())
+ ->method('getSession')
+ ->will($this->returnValue($this->session));
}
public function testShouldSaveMasterRequestSession()
@@ -95,7 +97,7 @@ public function testEmptySessionWithNewSessionIdDoesSendCookie()
$this->fixSessionId('456');
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
- $request = Request::create('/', 'GET', array(), array(new Cookie('MOCKSESSID', '123')));
+ $request = Request::create('/', 'GET', array(), array('MOCKSESSID' => '123'));
$event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
$this->listener->onKernelRequest($event);
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
index 5e4c322223eb3..6d67a177398c2 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php
@@ -175,8 +175,26 @@ public function testEmbeddingPrivateResponseMakesMainResponsePrivate()
$cacheStrategy->update($masterResponse);
$this->assertTrue($masterResponse->headers->hasCacheControlDirective('private'));
- // Not sure if we should pass "max-age: 60" in this case, as long as the response is private and
- // that's the more conservative of both the master and embedded response...?
+ $this->assertFalse($masterResponse->headers->hasCacheControlDirective('public'));
+ }
+
+ public function testEmbeddingPublicResponseDoesNotMakeMainResponsePublic()
+ {
+ $cacheStrategy = new ResponseCacheStrategy();
+
+ $masterResponse = new Response();
+ $masterResponse->setPrivate(); // this is the default, but let's be explicit
+ $masterResponse->setMaxAge(100);
+
+ $embeddedResponse = new Response();
+ $embeddedResponse->setPublic();
+ $embeddedResponse->setSharedMaxAge(100);
+
+ $cacheStrategy->add($embeddedResponse);
+ $cacheStrategy->update($masterResponse);
+
+ $this->assertTrue($masterResponse->headers->hasCacheControlDirective('private'));
+ $this->assertFalse($masterResponse->headers->hasCacheControlDirective('public'));
}
public function testResponseIsExiprableWhenEmbeddedResponseCombinesExpiryAndValidation()
diff --git a/src/Symfony/Component/Lock/Store/FlockStore.php b/src/Symfony/Component/Lock/Store/FlockStore.php
index bc03b859d00f3..64438fd461b2e 100644
--- a/src/Symfony/Component/Lock/Store/FlockStore.php
+++ b/src/Symfony/Component/Lock/Store/FlockStore.php
@@ -34,7 +34,7 @@ class FlockStore implements StoreInterface
/**
* @param string|null $lockPath the directory to store the lock, defaults to the system's temporary directory
*
- * @throws LockStorageException If the lock directory could not be created or is not writable
+ * @throws LockStorageException If the lock directory doesn’t exist or is not writable
*/
public function __construct(string $lockPath = null)
{
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
index ae90a1e5e2fe1..f11569f162f66 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
@@ -144,7 +144,7 @@ public function getTypes($class, $property, array $context = array())
return;
}
- if (!in_array($prefix, $this->arrayMutatorPrefixes)) {
+ if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
return $types;
}
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index 74f9303cbc990..ca26c69c9c2e0 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -155,7 +155,7 @@ private function extractFromMutator(string $class, string $property): ?array
}
$type = $this->extractFromReflectionType($reflectionType);
- if (in_array($prefix, $this->arrayMutatorPrefixes)) {
+ if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
$type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type);
}
@@ -178,7 +178,7 @@ private function extractFromAccessor(string $class, string $property): ?array
return array($this->extractFromReflectionType($reflectionType));
}
- if (in_array($prefix, array('is', 'can'))) {
+ if (\in_array($prefix, array('is', 'can'))) {
return array(new Type(Type::BUILTIN_TYPE_BOOL));
}
diff --git a/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php
index a2e98d0febb29..3f400a8fa53ea 100644
--- a/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php
+++ b/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\PropertyInfo;
/**
- * Description extractor Interface.
+ * Guesses the property's human readable description.
*
* @author Kévin Dunglas