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 Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages. Alternative Proxies:Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.