diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 415464c40b9a8..0bef18db33536 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -25,3 +25,5 @@ /src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php @lyrixx /src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php @lyrixx /src/Symfony/Component/Workflow/* @lyrixx +# Yaml +/src/Symfony/Component/Yaml/* @xabbuh diff --git a/.travis.yml b/.travis.yml index c23169a9b4d9e..d4c5156a6d2a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -149,7 +149,7 @@ before_install: tfold ext.apcu tpecl apcu-5.1.6 apcu.so $INI tfold ext.libsodium tpecl libsodium sodium.so $INI - tfold ext.mongodb tpecl mongodb-1.4.0RC1 mongodb.so $INI + tfold ext.mongodb tpecl mongodb-1.5.0 mongodb.so $INI tfold ext.amqp tpecl amqp-1.9.3 amqp.so $INI fi diff --git a/CHANGELOG-4.0.md b/CHANGELOG-4.0.md index 7131036ba4fa8..72d24bf39e62d 100644 --- a/CHANGELOG-4.0.md +++ b/CHANGELOG-4.0.md @@ -7,6 +7,46 @@ 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.12 (2018-06-25) + + * bug #27626 [TwigBundle][DX] Only add the Twig WebLinkExtension if the WebLink component is enabled (thewilkybarkid) + * bug #27701 [SecurityBundle] Dont throw if "security.http_utils" is not found (nicolas-grekas) + * bug #27690 [DI] Resolve env placeholder in logs (ro0NL) + * bug #26534 allow_extra_attributes does not throw an exception as documented (deviantintegral) + * bug #27668 [Lock] use 'r+' for fopen (fixes issue on Solaris) (fritzmg) + * bug #27669 [Filesystem] fix file lock on SunOS (fritzmg) + * bug #27662 [HttpKernel] fix handling of nested Error instances (xabbuh) + * bug #26845 [Config] Fixing GlobResource when inside phar archive (vworldat) + * bug #27382 [Form] Fix error when rendering a DateIntervalType form with exactly 0 weeks (krixon) + * bug #27309 Fix surrogate not using original request (Toflar) + * bug #27467 [HttpKernel] fix session tracking in surrogate master requests (nicolas-grekas) + * bug #27630 [Validator][Form] Remove BOM in some xlf files (gautierderuette) + * bug #27596 [Framework][Workflow] Added support for interfaces (vudaltsov) + * bug #27593 [ProxyManagerBridge] Fixed support of private services (nicolas-grekas) + * bug #27591 [VarDumper] Fix dumping ArrayObject and ArrayIterator instances (nicolas-grekas) + * bug #27581 Fix bad method call with guard authentication + session migration (weaverryan) + * bug #27576 [Cache] Fix expiry comparisons in array-based pools (nicolas-grekas) + * bug #27556 Avoiding session migration for stateless firewall UsernamePasswordJsonAuthenticationListener (weaverryan) + * bug #27452 Avoid migration on stateless firewalls (weaverryan) + * bug #27568 [DI] Deduplicate generated proxy classes (nicolas-grekas) + * bug #27326 [Serializer] deserialize from xml: Fix a collection that contains the only one element (webnet-fr) + * bug #27567 [PhpUnitBridge] Fix error on some Windows OS (Nsbx) + * bug #27357 [Lock] Remove released semaphore (jderusse) + * bug #27416 TagAwareAdapter over non-binary memcached connections corrupts memcache (Aleksey Prilipko) + * bug #27514 [Debug] Pass previous exception to FatalErrorException (pmontoya) + * bug #27516 Revert "bug #26138 [HttpKernel] Catch HttpExceptions when templating is not installed (cilefen)" (nicolas-grekas) + * bug #27318 [Cache] memcache connect should not add duplicate entries on sequential calls (Aleksey Prilipko) + * bug #27389 [Serializer] Fix serializer tries to denormalize null values on nullable properties (ogizanagi) + * bug #27272 [FrameworkBundle] Change priority of AddConsoleCommandPass to TYPE_BEFORE_REMOVING (upyx) + * bug #27396 [HttpKernel] fix registering IDE links (nicolas-grekas) + * bug #26973 [HttpKernel] Set first trusted proxy as REMOTE_ADDR in InlineFragmentRenderer. (kmadejski) + * bug #27303 [Process] Consider "executable" suffixes first on Windows (sanmai) + * bug #27297 Triggering RememberMe's loginFail() when token cannot be created (weaverryan) + * bug #27344 [HttpKernel] reset kernel start time on reboot (kiler129) + * bug #27365 [Serializer] Check the value of enable_max_depth if defined (dunglas) + * bug #27358 [PhpUnitBridge] silence some stderr outputs (ostrolucky) + * bug #27366 [DI] never inline lazy services (nicolas-grekas) + * 4.0.11 (2018-05-25) * bug #27364 [DI] Fix bad exception on uninitialized references to non-shared services (nicolas-grekas) diff --git a/CHANGELOG-4.1.md b/CHANGELOG-4.1.md index b09b994c5cd63..e7126dcf6d28c 100644 --- a/CHANGELOG-4.1.md +++ b/CHANGELOG-4.1.md @@ -7,6 +7,53 @@ in 4.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.1.0...v4.1.1 +* 4.1.2 (2018-07-23) + + * bug #28005 [HttpKernel] Fixed templateExists on parse error of the template name (yceruto) + * bug #28013 [Messenger] Add missing typehint on chain sender (sroze) + * bug #27997 Serbo-Croatian has Serbian plural rule (kylekatarnls) + * bug #26193 Fix false-positive deprecation notices for TranslationLoader and WriteCheckSessionHandler (iquito) + * bug #27827 [Serializer] Supports nested abstract items (sroze) + * bug #27958 [Form] Remaining changes for bootstrap 4 file fields (apfelbox) + * bug #27919 [Form] Improve rendering of `file` field in bootstrap 4 (apfelbox) + * bug #27941 [WebProfilerBundle] Fixed icon alignment issue using Bootstrap 4.1.2 (jmsche) + * bug #27937 [HttpFoundation] reset callback on StreamedResponse when setNotModified() is called (rubencm) + * bug #27927 [HttpFoundation] Suppress side effects in 'get' and 'has' methods of NamespacedAttributeBag (webnet-fr) + * bug #27913 [EventDispatcher] Clear orphaned events on reset (acasademont) + * bug #27923 [Form/Profiler] Massively reducing memory footprint of form profiling pages... (VincentChalnot) + * bug #27918 [Console] correctly return parameter's default value on "--" (seschwar) + * bug #27826 [Serializer] Fix serialization of items with groups across entities and discrimination map (sroze) + * bug #27904 [Filesystem] fix lock file permissions (fritzmg) + * bug #27903 [Lock] fix lock file permissions (fritzmg) + * bug #27889 [Form] Replace .initialism with .text-uppercase. (vudaltsov) + * bug #27902 Fix the detection of the Process new argument (stof) + * bug #27885 [HttpFoundation] don't encode cookie name for BC (nicolas-grekas) + * bug #27782 [DI] Fix dumping ignore-on-uninitialized references to synthetic services (nicolas-grekas) + * bug #27435 [OptionResolver] resolve arrays (Doctrs) + * bug #27728 [TwigBridge] Fix missing path and separators in loader paths list on debug:twig output (yceruto) + * bug #27837 [PropertyInfo] Fix dock block lookup fallback loop (DerManoMann) + * bug #27848 [Workflow] Fixed BC break (lyrixx) + * bug #27758 [WebProfilerBundle] Prevent toolbar links color override by css (alcalyn) + * bug #27847 [Security] Fix accepting null as $uidKey in LdapUserProvider (louhde) + * bug #27820 [Messenger] Fix a bug when having more than one named handler per message subscriber (sroze) + * bug #27834 [DI] Don't show internal service id on binding errors (nicolas-grekas) + * bug #27831 Check for Hyper terminal on all operating systems. (azjezz) + * bug #27794 Add color support for Hyper terminal . (azjezz) + * bug #27809 [HttpFoundation] Fix tests: new message for status 425 (dunglas) + * bug #27618 [PropertyInfo] added handling of nullable types in PhpDoc (oxan) + * bug #27659 [HttpKernel] Make AbstractTestSessionListener compatible with CookieClearingLogoutHandler (thewilkybarkid) + * bug #27752 [Cache] provider does not respect option maxIdLength with versioning enabled (Constantine Shtompel) + * bug #27773 [Serializer] Class discriminator and serialization groups (sroze) + * bug #27710 [DependencyInjection] fix handling of empty DI extension configs (xabbuh) + * bug #27776 [ProxyManagerBridge] Fix support of private services (bis) (nicolas-grekas) + * bug #27714 [HttpFoundation] fix session tracking counter (nicolas-grekas, dmaicher) + * bug #27727 [Routing] Disallow object usage inside Route (paxal) + * bug #27736 [Routing] fix too much greediness in host-matching regex (nicolas-grekas) + * bug #27747 [HttpFoundation] fix registration of session proxies (nicolas-grekas) + * bug #27754 [HttpFoundation] missing namespace for RedisProxy (Bonfante) + * bug #27722 Redesign the Debug error page in prod (javiereguiluz) + * bug #27716 [DI] fix dumping deprecated service in yaml (nicolas-grekas) + * 4.1.1 (2018-06-25) * bug #27626 [TwigBundle][DX] Only add the Twig WebLinkExtension if the WebLink component is enabled (thewilkybarkid) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 672246e7f9da8..b6eef0b63198c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -16,8 +16,8 @@ Symfony is the result of the work of many people who made the code better - Robin Chalas (chalas_r) - Johannes S (johannes) - Jakub Zalas (jakubzalas) - - Kris Wallsmith (kriswallsmith) - Maxime Steinhausser (ogizanagi) + - Kris Wallsmith (kriswallsmith) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) - Grégoire Pineau (lyrixx) @@ -25,8 +25,8 @@ Symfony is the result of the work of many people who made the code better - Abdellatif Ait boudad (aitboudad) - Romain Neutron (romain) - Pascal Borreli (pborreli) - - Wouter De Jong (wouterj) - Roland Franssen (ro0) + - Wouter De Jong (wouterj) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) @@ -39,8 +39,8 @@ Symfony is the result of the work of many people who made the code better - Jules Pietri (heah) - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) - - Sarah Khalil (saro0h) - Yonel Ceruto (yonelceruto) + - Sarah Khalil (saro0h) - Jonathan Wage (jwage) - Hamza Amrouche (simperfit) - Diego Saint Esteben (dosten) @@ -54,36 +54,37 @@ Symfony is the result of the work of many people who made the code better - Bulat Shakirzyanov (avalanche123) - Peter Rehm (rpet) - Matthias Pigulla (mpdude) + - Dany Maillard (maidmaid) - Saša Stamenković (umpirsky) + - Kevin Bond (kbond) + - Tobias Nyholm (tobias) - Pierre du Plessis (pierredup) - Henrik Bjørnskov (henrikbjorn) - - Dany Maillard (maidmaid) - Miha Vrhovnik - - Kevin Bond (kbond) - - Tobias Nyholm (tobias) - Diego Saint Esteben (dii3g0) - - Konstantin Kudryashov (everzet) - Alexander M. Turek (derrabus) + - Konstantin Kudryashov (everzet) - Bilal Amarni (bamarni) - Jérémy DERUSSÉ (jderusse) - Florin Patan (florinpatan) - Mathieu Piot (mpiot) - Gábor Egyed (1ed) - Michel Weimerskirch (mweimerskirch) + - Titouan Galopin (tgalopin) - Andrej Hudec (pulzarraider) - Eric Clemmons (ericclemmons) - Jáchym Toušek (enumag) - Charles Sarrazin (csarrazi) - - Titouan Galopin (tgalopin) - Konstantin Myakshin (koc) - Christian Raue + - David Maicher (dmaicher) - Arnout Boks (aboks) - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) - Issei Murasawa (issei_m) - Douglas Greenshields (shieldo) - - David Maicher (dmaicher) + - Vladimir Reznichenko (kalessil) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) @@ -96,7 +97,6 @@ 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) - Arnaud Le Blanc (arnaud-lb) @@ -105,18 +105,20 @@ Symfony is the result of the work of many people who made the code better - Tim Nagel (merk) - Grégoire Paris (greg0ire) - Brice BERNARD (brikou) + - Valentin Udaltsov (vudaltsov) + - gadelat (gadelat) - Baptiste Clavié (talus) - marc.weistroff - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) + - Peter Kokot (maastermedia) - Jacob Dreesen (jdreesen) - Florian Voutzinos (florianv) - Colin Frei - Adrien Brault (adrienbrault) - Tomáš Votruba (tomas_votruba) - Joshua Thijssen - - Peter Kokot (maastermedia) - David Buchmann (dbu) - excelwebzone - Fabien Pennequin (fabienpennequin) @@ -131,6 +133,7 @@ Symfony is the result of the work of many people who made the code better - Florian Lonqueu-Brochard (florianlb) - Sebastiaan Stok (sstok) - Stefano Sala (stefano.sala) + - Jérôme Vasseur (jvasseur) - Evgeniy (ewgraf) - Alex Pott - Vincent AUBERT (vincent) @@ -139,9 +142,8 @@ Symfony is the result of the work of many people who made the code better - Sebastian Hörl (blogsh) - Daniel Gomes (danielcsgomes) - Hidenori Goto (hidenorigoto) - - Jérôme Vasseur (jvasseur) - - Valentin Udaltsov (vudaltsov) - - gadelat (gadelat) + - Chris Wilkinson (thewilkybarkid) + - Arnaud Kleinpeter (nanocom) - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) @@ -149,12 +151,10 @@ Symfony is the result of the work of many people who made the code better - Philipp Wahala (hifi) - Julien Falque (julienfalque) - Rafael Dohms (rdohms) - - Arnaud Kleinpeter (nanocom) - jwdeitch - Teoh Han Hui (teohhanhui) - Mikael Pajunen - Joel Wurtz (brouznouf) - - Chris Wilkinson (thewilkybarkid) - Oleg Voronkovich - Vyacheslav Pavlov - Richard van Laak (rvanlaak) @@ -170,6 +170,7 @@ Symfony is the result of the work of many people who made the code better - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) - Artur Kotyrba + - Jannik Zschiesche (apfelbox) - GDIBass - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) @@ -178,6 +179,7 @@ Symfony is the result of the work of many people who made the code better - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) + - Niels Keurentjes (curry684) - Daniel Espendiller - Possum - Dorian Villet (gnutix) @@ -203,7 +205,6 @@ Symfony is the result of the work of many people who made the code better - Matthieu Bontemps (mbontemps) - apetitpa - Pierre Minnieur (pminnieur) - - Jannik Zschiesche (apfelbox) - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) @@ -219,22 +220,26 @@ Symfony is the result of the work of many people who made the code better - Michele Orselli (orso) - Tom Van Looy (tvlooy) - Sven Paulus (subsven) + - Yanick Witschi (toflar) - Thomas Calvet (fancyweb) - Rui Marinho (ruimarinho) - - Niels Keurentjes (curry684) + - Alessandro Chitolina - Eugene Wissner + - Pascal Montoya - Julien Brochet (mewt) - Leo Feyer - Tristan Darricau (nicofuma) - Nikolay Labinskiy (e-moe) - Michaël Perrin (michael.perrin) - Marcel Beerta (mazen) + - Albert Casademont (acasademont) - Loïc Faugeron - Hidde Wieringa (hiddewie) - Marco Pivetta (ocramius) - Rob Frawley 2nd (robfrawley) - julien pauli (jpauli) - Lorenz Schori + - Oskar Stark (oskarstark) - Sébastien Lavoie (lavoiesl) - Gregor Harlan (gharlan) - Dariusz @@ -255,14 +260,13 @@ Symfony is the result of the work of many people who made the code better - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA - Patrick McDougle (patrick-mcdougle) - - Yanick Witschi (toflar) - Alif Rachmawadi - - Alessandro Chitolina - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) - Jordan Samouh (jordansamouh) - Baptiste Lafontaine (magnetik) - Jakub Kucharovic (jkucharovic) + - Edi Modrić (emodric) - Uwe Jäger (uwej711) - Eugene Leonovich (rybakit) - Filippo Tessarotto @@ -274,7 +278,6 @@ Symfony is the result of the work of many people who made the code better - Tyson Andre - Chekote - Thomas Adam - - Albert Casademont (acasademont) - Viktor Bocharskyi (bocharsky_bw) - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) @@ -284,7 +287,6 @@ Symfony is the result of the work of many people who made the code better - Bob den Otter (bopp) - Nikita Konstantinov - Wodor Wodorski - - Oskar Stark (oskarstark) - Thomas Lallement (raziel057) - Giorgio Premi - Christian Schmidt @@ -303,7 +305,7 @@ Symfony is the result of the work of many people who made the code better - Marc Weistroff (futurecat) - Christian Schmidt - Maxime Veber (nek-) - - Edi Modrić (emodric) + - MatTheCat - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) @@ -347,6 +349,7 @@ Symfony is the result of the work of many people who made the code better - Artur Melo (restless) - Matthew Lewinski (lewinski) - Magnus Nordlander (magnusnordlander) + - Thomas Royer (cydonia7) - alquerci - Francesco Levorato - Vitaliy Zakharov (zakharovvi) @@ -358,11 +361,11 @@ Symfony is the result of the work of many people who made the code better - Artur Eshenbrener - François-Xavier de Guillebon (de-gui_f) - Damien Alexandre (damienalexandre) + - Thomas Perez (scullwm) - Felix Labrecque - Yaroslav Kiliba - Terje Bråten - Mathieu Lechat - - MatTheCat - Robbert Klarenbeek (robbertkl) - JhonnyL - David Badura (davidbadura) @@ -374,6 +377,7 @@ Symfony is the result of the work of many people who made the code better - ShinDarth - Stéphane PY (steph_py) - Philipp Kräutli (pkraeutli) + - Grzegorz (Greg) Zdanowski (kiler129) - Kirill chEbba Chebunin (chebba) - Greg Thornton (xdissent) - Gary PEGEOT (gary-p) @@ -427,13 +431,13 @@ Symfony is the result of the work of many people who made the code better - Christopher Davis (chrisguitarguy) - Jan Schumann - Niklas Fiekas + - Colin O'Dell (colinodell) - Markus Bachmann (baachi) - lancergr - Zan Baldwin - Mihai Stancu - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) - - Thomas Royer (cydonia7) - Arturs Vonda - Josip Kruslin - Asmir Mustafic (goetas) @@ -453,7 +457,6 @@ Symfony is the result of the work of many people who made the code better - Dirk Pahl (dirkaholic) - cedric lombardot (cedriclombardot) - Jonas Flodén (flojon) - - Thomas Perez (scullwm) - Marcin Sikoń (marphi) - Dominik Zogg (dominik.zogg) - Marek Pietrzak @@ -511,6 +514,7 @@ Symfony is the result of the work of many people who made the code better - Andrew Udvare (audvare) - alexpods - Arjen van der Meijden + - Adam Szaraniec (mimol) - Dariusz Ruminski - Erik Trapman (eriktrapman) - De Cock Xavier (xdecock) @@ -550,6 +554,7 @@ Symfony is the result of the work of many people who made the code better - Ned Schwartz - Ziumin - Jeremy Benoist + - fritzmg - Lenar Lõhmus - Sander Toonen (xatoo) - Benjamin Laugueux (yzalis) @@ -588,9 +593,11 @@ Symfony is the result of the work of many people who made the code better - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) - Christophe Villeger (seragan) + - Bob van de Vijver (bobvandevijver) - Stefan Gehrig (sgehrig) - Hany el-Kerdany - Wang Jingyu + - Webnet team (webnet) - Åsmund Garfors - Gunnstein Lye (glye) - Maxime Douailin @@ -609,6 +616,7 @@ Symfony is the result of the work of many people who made the code better - David Fuhr - Kamil Kokot (pamil) - Max Grigorian (maxakawizard) + - DerManoMann - mcfedr (mcfedr) - Rostyslav Kinash - Maciej Malarz (malarzm) @@ -637,6 +645,7 @@ Symfony is the result of the work of many people who made the code better - Richard Bradley - Ulumuddin Yunus (joenoez) - Johann Saunier (prophet777) + - Sergey (upyx) - Michael Devery (mickadoo) - Antoine Corcy - Sascha Grossenbacher @@ -666,31 +675,40 @@ Symfony is the result of the work of many people who made the code better - Thomas Ploch - Benjamin Grandfond (benjamin) - Tiago Brito (blackmx) + - - Richard van den Brand (ricbra) - develop + - flip111 - Greg Anderson - VJ - Delf Tonder (leberknecht) - Mark Sonnabaum + - Massimiliano Braglia (massimilianobraglia) - Richard Quadling - jochenvdv - Arturas Smorgun (asarturas) - Alexander Volochnev (exelenz) - Michael Piecko - yclian + - Aleksey Prilipko - twifty - Indra Gunawan (guind) - Peter Ward + - Davide Borsatto (davide.borsatto) + - Rhodri Pugh (rodnaph) - Julien DIDIER (juliendidier) - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) - Jeroen van den Enden (stoefke) - Pascal Helfenstein + - Anthony GRASSIOT (antograssiot) - Baldur Rensch (brensch) + - Pierre Rineau - Vladyslav Petrovych - Alex Xandra Albert Sim - Craig Duncan (duncan3dc) - Carson Full + - Sergey Yastrebov - Trent Steel (trsteel88) - Yuen-Chi Lian - Besnik Br @@ -717,11 +735,13 @@ Symfony is the result of the work of many people who made the code better - Joschi Kuphal - John Bohn (jbohn) - Marc Morera (mmoreram) + - Saif Eddin Gmati (azjezz) + - Smaine Milianni (ismail1432) + - Michael Moravec - Andrew Hilobok (hilobok) - Noah Heck (myesain) - Christian Soronellas (theunic) - Johann Pardanaud - - Adam Szaraniec (mimol) - Yosmany Garcia (yosmanyga) - Wouter de Wild - Antoine M (amakdessi) @@ -734,6 +754,7 @@ Symfony is the result of the work of many people who made the code better - Xavier Lacot (xavier) - possum - Denis Zunke (donalberto) + - Ahmadou Waly Ndiaye (waly) - Philipp Cordes - Ahmed TAILOULOUTE (ahmedtai) - Olivier Maisonneuve (olineuve) @@ -770,6 +791,7 @@ Symfony is the result of the work of many people who made the code better - Abhoryo - Fabian Vogler (fabian) - Korvin Szanto + - Stéphan Kochen - Arjan Keeman - Alaattin Kahramanlar (alaattin) - Sergey Zolotov (enleur) @@ -777,6 +799,7 @@ Symfony is the result of the work of many people who made the code better - Neil Ferreira - Nathanael Noblet (gnat) - Indra Gunawan (indragunawan) + - Julie Hourcade (juliehde) - Dmitry Parnas (parnas) - Paul LE CORRE - Emanuele Iannone @@ -853,6 +876,7 @@ Symfony is the result of the work of many people who made the code better - LOUARDI Abdeltif (ouardisoft) - Robert Gruendler (pulse00) - Simon Terrien (sterrien) + - Tarmo Leppänen (tarlepp) - Benoît Merlet (trompette) - Koen Kuipers - datibbaw @@ -863,6 +887,7 @@ Symfony is the result of the work of many people who made the code better - Sebastien Morel (plopix) - Patrick Kaufmann - Piotr Stankowski + - Anton Dyshkant - Reece Fowell (reecefowell) - Mátyás Somfai (smatyas) - stefan.r @@ -880,17 +905,18 @@ Symfony is the result of the work of many people who made the code better - Sam Malone - Phan Thanh Ha (haphan) - Chris Jones (leek) - - Colin O'Dell (colinodell) - xaav - Mahmoud Mostafa (mahmoud) - Pieter - Michael Tibben - Billie Thompson - Sander Marechal + - Oleg Golovakhin (doc_tr) - Icode4Food (icode4food) - Radosław Benkel - jean pasqualini (darkilliant) - Ross Motley (rossmotley) + - Julien Fredon - ttomor - Mei Gwilym (meigwilym) - Michael H. Arieli (excelwebzone) @@ -916,6 +942,7 @@ Symfony is the result of the work of many people who made the code better - dantleech - Bastien DURAND (deamon) - Xavier Leune + - Rudy Onfroy - Tero Alén (tero) - DerManoMann - Guillaume Royer @@ -934,7 +961,6 @@ Symfony is the result of the work of many people who made the code better - Máximo Cuadros (mcuadros) - tamirvs - julien.galenski - - Bob van de Vijver - Christian Neff - Oliver Hoff - Ole Rößner (basster) @@ -952,8 +978,10 @@ Symfony is the result of the work of many people who made the code better - ilyes kooli - gr1ev0us - mlazovla + - Behnoush norouzali (behnoush) - Max Beutel - Antanas Arvasevicius + - Pierre Dudoret - Thomas - Maximilian Berghoff (electricmaxxx) - nacho @@ -962,6 +990,7 @@ Symfony is the result of the work of many people who made the code better - Sergey Novikov (s12v) - Marcos Quesada (marcos_quesada) - Matthew Vickery (mattvick) + - Viktor Novikov (panzer_commander) - Paul Mitchum (paul-m) - Angel Koilov (po_taka) - Dan Finnie @@ -996,7 +1025,6 @@ Symfony is the result of the work of many people who made the code better - Thanos Polymeneas (thanos) - Benoit Garret - Jakub Sacha - - DerManoMann - Olaf Klischat - orlovv - Jonathan Hedstrom @@ -1017,13 +1045,13 @@ Symfony is the result of the work of many people who made the code better - Alex Bowers - Jeremy Bush - wizhippo + - Thomason, James - Viacheslav Sychov - Helmut Hummel (helhum) - Matt Brunt - Carlos Ortega Huetos - rpg600 - Péter Buri (burci) - - Davide Borsatto (davide.borsatto) - kaiwa - RJ Garcia - Charles Sanquer (csanquer) @@ -1033,7 +1061,6 @@ Symfony is the result of the work of many people who made the code better - Will Donohoe - peter - Jaroslav Kuba - - flip111 - Jérémy Jourdin (jjk801) - BRAMILLE Sébastien (oktapodia) - Artem Kolesnikov (tyomo4ka) @@ -1043,11 +1070,9 @@ 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) - - Rhodri Pugh (rodnaph) - David de Boer (ddeboer) - Ryan Rogers - Klaus Purer @@ -1061,6 +1086,7 @@ Symfony is the result of the work of many people who made the code better - Roger Webb - Dmitriy Simushev - Pawel Smolinski + - Oxan van Leeuwen - pkowalczyk - Max Voloshin (maxvoloshin) - Nicolas Fabre (nfabre) @@ -1111,6 +1137,7 @@ Symfony is the result of the work of many people who made the code better - ConneXNL - Aharon Perkel - matze + - Rubén Calvo (rubencm) - Abdul.Mohsen B. A. A - Benoît Burnichon - pthompson @@ -1121,10 +1148,10 @@ 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) + - Andrei Igna - azine - Dawid Sajdak - Ludek Stepan @@ -1134,6 +1161,7 @@ Symfony is the result of the work of many people who made the code better - Erika Heidi Reinaldo (erikaheidi) - Pierre Tachoire (krichprollsch) - Marc J. Schmidt (marcjs) + - Sebastian Schwarz - Marco Jantke - Saem Ghani - Clément LEFEBVRE @@ -1142,7 +1170,6 @@ Symfony is the result of the work of many people who made the code better - Adrien Gallou (agallou) - Maks Rafalko (bornfree) - Karol Sójko (karolsojko) - - Grzegorz Zdanowski (kiler129) - sl_toto (sl_toto) - Walter Dal Mut (wdalmut) - abluchet @@ -1155,16 +1182,17 @@ Symfony is the result of the work of many people who made the code better - Keri Henare (kerihenare) - Cédric Lahouste (rapotor) - Samuel Vogel (samuelvogel) + - Alexey Kopytko (sanmai) - 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 + - Constantine Shtompel - Jules Lamur - Renato Mendes Figueiredo - ShiraNai7 @@ -1187,6 +1215,7 @@ Symfony is the result of the work of many people who made the code better - Lance McNearney - Gonzalo Vilaseca (gonzalovilaseca) - Giorgio Premi + - Andrew Berry - ncou - Ian Carroll - caponica @@ -1205,10 +1234,10 @@ Symfony is the result of the work of many people who made the code better - Tadcka - Beth Binkovitz - Gonzalo Míguez - - Pierre Rineau - Romain Geissler - Adrien Moiruad - Tomaz Ahlin + - Philip Ardery - Marcus Stöhr (dafish) - Emmanuel Vella (emmanuel.vella) - Jonathan Johnson (jrjohnson) @@ -1217,7 +1246,6 @@ 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) @@ -1254,7 +1282,6 @@ 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 @@ -1275,18 +1302,22 @@ Symfony is the result of the work of many people who made the code better - Jonathan Gough - Benjamin Bender - Jared Farrish + - karl.rixon - Konrad Mohrfeldt - Lance Chen + - Ciaran McNulty (ciaranmcnulty) - Andrew (drew) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) - Flavian (2much) + - Gautier Deuette - mike - Kirk Madera - Keith Maika - Mephistofeles - Hoffmann András - Olivier + - Cyril PASCAL - pscheit - Wybren Koelmans - Zdeněk Drahoš @@ -1313,6 +1344,7 @@ Symfony is the result of the work of many people who made the code better - Artiom - Jakub Simon - Bouke Haarsma + - Evert Harmeling - Martin Eckhardt - natechicago - Jonathan Poston @@ -1339,6 +1371,7 @@ Symfony is the result of the work of many people who made the code better - Harold Iedema - Arnau González (arnaugm) - Simon Bouland (bouland) + - Ivan Nikolaev (destillat) - Matthew Foster (mfoster) - Paul Seiffert (seiffert) - Vasily Khayrulin (sirian) @@ -1372,10 +1405,12 @@ Symfony is the result of the work of many people who made the code better - Manatsawin Hanmongkolchai - Gunther Konig - Maciej Schmidt + - Greg ORIOL - Dennis Væversted - nuncanada - flack - František Bereň + - Kamil Madejski - Jeremiah VALERIE - Mike Francis - Christoph Nissle (derstoffel) @@ -1468,7 +1503,6 @@ Symfony is the result of the work of many people who made the code better - Sam Ward - Walther Lalk - Adam - - Stéphan Kochen - devel - taiiiraaa - Trevor Suarez @@ -1482,6 +1516,7 @@ Symfony is the result of the work of many people who made the code better - Chansig - Tischoi - J Bruni + - Fritz Michael Gschwantner - Alexey Prilipko - Dmitriy Fedorenko - vlakoff @@ -1509,10 +1544,12 @@ Symfony is the result of the work of many people who made the code better - Joel Marcey - David Christmann - root + - Vincent Chalnot - James Hudson - Tom Maguire - Richard Quadling - David Zuelke + - Adrian - Oleg Andreyev - Pierre Rineau - Maxim Lovchikov @@ -1535,6 +1572,7 @@ Symfony is the result of the work of many people who made the code better - Hein Zaw Htet™ - Ruben Kruiswijk - Cosmin-Romeo TANASE + - Julien Maulny - Michael J - Joseph Maarek - Alexander Menk @@ -1580,6 +1618,7 @@ Symfony is the result of the work of many people who made the code better - Kuba Werłos - Tomas Liubinas - Alex + - Jan Hort - Klaas Naaijkens - Daniel González Cerviño - Rafał @@ -1686,7 +1725,6 @@ Symfony is the result of the work of many people who made the code better - Christian Eikermann - Kai Eichinger - Antonio Angelino - - Pascal Montoya - Matt Fields - Niklas Keller - Vladimir Sazhin @@ -1753,6 +1791,7 @@ Symfony is the result of the work of many people who made the code better - Adam Klvač - Yevgen Kovalienia - Lebnik + - nsbx - Shude - Ondřej Führer - Sema @@ -1765,11 +1804,13 @@ Symfony is the result of the work of many people who made the code better - Norman Soetbeer - zorn - Yuriy Potemkin + - Emilie Lorenzo - Benjamin Long - Matt Janssen - Ben Miller - Peter Gribanov - kwiateusz + - jspee - David Soria Parra - Sergiy Sokolenko - dinitrol @@ -1827,6 +1868,7 @@ Symfony is the result of the work of many people who made the code better - Damon Jones (damon__jones) - Łukasz Giza (destroyer) - Daniel Londero (dlondero) + - Samuele Lilli (doncallisto) - Sebastian Landwehr (dword123) - Adel ELHAIBA (eadel) - Damián Nohales (eagleoneraptor) @@ -1889,6 +1931,7 @@ Symfony is the result of the work of many people who made the code better - scourgen hung (scourgen) - Sébastien Alfaiate (seb33300) - Sebastian Busch (sebu) + - Sepehr Lajevardi (sepehr) - André Filipe Gonçalves Neves (seven) - Bruno Ziegler (sfcoder) - Andrea Giuliano (shark) @@ -1898,12 +1941,13 @@ Symfony is the result of the work of many people who made the code better - Julien Sanchez (sumbobyboys) - Guillermo Gisinger (t3chn0r) - Markus Tacker (tacker) - - Tarmo Leppänen (tarlepp) + - Andrew Clark (tqt_andrew_clark) - Tyler Stroud (tystr) - Moritz Kraft (userfriendly) - Víctor Mateo (victormateo) - Vincent (vincent1870) - Vincent CHALAMON (vincentchalamon) + - David Herrmann (vworldat) - Eugene Babushkin (warl) - Wouter Sioen (wouter_sioen) - Xavier Amado (xamado) @@ -1929,6 +1973,7 @@ Symfony is the result of the work of many people who made the code better - fh-github@fholzhauer.de - AbdElKader Bouadjadja - DSeemiller + - Kyle - Jan Emrich - Mark Topper - Xavier REN @@ -1939,10 +1984,12 @@ Symfony is the result of the work of many people who made the code better - Mohamed Karnichi (amiral) - Andrew Carter (andrewcarteruk) - Adam Elsodaney (archfizz) + - Gregório Bonfante Borba (bonfante) - Daniel Kolvik (dkvk) - Marc Lemay (flug) - Henne Van Och (hennevo) - Jeroen De Dauw (jeroendedauw) + - Jonathan Scheiber (jmsche) - Daniel Alejandro Castro Arellano (lexcast) - Maxime COLIN (maximecolin) - Muharrem Demirci (mdemirci) diff --git a/composer.json b/composer.json index bf5bc53c4cc47..67065a8d919ad 100644 --- a/composer.json +++ b/composer.json @@ -90,7 +90,7 @@ "doctrine/cache": "~1.6", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.4", - "doctrine/orm": "~2.4,>=2.4.5", + "doctrine/orm": "~2.4,>=2.4.5,<=2.7.0", "doctrine/doctrine-bundle": "~1.4", "monolog/monolog": "~1.11", "ocramius/proxy-manager": "~0.4|~1.0|~2.0", @@ -102,7 +102,7 @@ }, "conflict": { "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", - "phpdocumentor/type-resolver": "<0.2.1", + "phpdocumentor/type-resolver": "<0.3.0", "phpunit/phpunit": "<5.4.3" }, "provide": { diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index c4b8202e744c5..d856b2c8db79d 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -68,7 +68,7 @@ private function addTaggedSubscribers(ContainerBuilder $container) $connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections); foreach ($connections as $con) { if (!isset($this->connections[$con])) { - throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $taggedSubscriber, implode(', ', array_keys($this->connections)))); + throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections)))); } $this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', array(new Reference($id))); diff --git a/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php b/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php index 056496ae1325a..966a7baf83079 100644 --- a/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php @@ -38,9 +38,10 @@ public function onKernelResponse(FilterResponseEvent $event) return; } - if (!preg_match('{\bFirePHP/\d+\.\d+\b}', $event->getRequest()->headers->get('User-Agent')) - && !$event->getRequest()->headers->has('X-FirePHP-Version')) { - $this->sendHeaders = false; + $request = $event->getRequest(); + if (!preg_match('{\bFirePHP/\d+\.\d+\b}', $request->headers->get('User-Agent')) + && !$request->headers->has('X-FirePHP-Version')) { + self::$sendHeaders = false; $this->headers = array(); return; @@ -58,7 +59,7 @@ public function onKernelResponse(FilterResponseEvent $event) */ protected function sendHeader($header, $content) { - if (!$this->sendHeaders) { + if (!self::$sendHeaders) { return; } diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 29b1960798b8c..a2528db303fbb 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -311,6 +311,10 @@ private static function hasColorSupport() return false; } + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + if (DIRECTORY_SEPARATOR === '\\') { return (function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(STDOUT)) diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index cabae2517b691..0871c4ce9d70d 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -54,7 +54,7 @@ public function getProxyFactoryCode(Definition $definition, $id, $factoryCode = $instantiation = 'return'; if ($definition->isShared()) { - $instantiation .= sprintf(' $this->%s[\'%s\'] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', $id); + $instantiation .= sprintf(' $this->%s[\'%s\'] =', \method_exists(ContainerBuilder::class, 'addClassResource') || ($definition->isPublic() && !$definition->isPrivate()) ? 'services' : 'privates', $id); } if (null === $factoryCode) { diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index cd346dfe580eb..e12b25fcb02f8 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; /** @@ -98,7 +99,7 @@ public function getPrivatePublicDefinitions() array( (new Definition(__CLASS__)) ->setPublic(false), - 'privates', + \method_exists(ContainerBuilder::class, 'addClassResource') ? 'services' : 'privates', ), array( (new Definition(__CLASS__)) diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 5720096c46534..0d77fed339bcc 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -108,19 +108,27 @@ protected function execute(InputInterface $input, OutputInterface $output) } $rows = array(); + $firstNamespace = true; + $prevHasSeparator = false; foreach ($this->getLoaderPaths() as $namespace => $paths) { - if (count($paths) > 1) { + if (!$firstNamespace && !$prevHasSeparator && count($paths) > 1) { $rows[] = array('', ''); } + $firstNamespace = false; foreach ($paths as $path) { - $rows[] = array($namespace, '- '.$path); + $rows[] = array($namespace, $path.DIRECTORY_SEPARATOR); $namespace = ''; } if (count($paths) > 1) { $rows[] = array('', ''); + $prevHasSeparator = true; + } else { + $prevHasSeparator = false; } } - array_pop($rows); + if ($prevHasSeparator) { + array_pop($rows); + } $io->section('Loader Paths'); $io->table(array('Namespace', 'Paths'), $rows); 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 dc4858e2f3871..8f790fdd19450 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 @@ -114,6 +114,20 @@ {%- endblock percent_widget %} +{% block file_widget -%} +
+ <{{ element|default('div') }} class="custom-file"> + {%- set type = type|default('file') -%} + {{- block('form_widget_simple') -}} + + +
+{% endblock %} + {% block form_widget_simple -%} {% if type is not defined or type != 'hidden' %} {%- set attr = attr|merge({class: (attr.class|default('') ~ (type|default('') == 'file' ? ' custom-file-input' : ' form-control'))|trim}) -%} @@ -186,10 +200,8 @@ {%- if compound is defined and compound -%} {%- set element = 'legend' -%} {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-label')|trim}) -%} - {% elseif type is defined and type == 'file' %} - {%- set label_attr = label_attr|merge({for: id, class: (label_attr.class|default('') ~ ' custom-file-label')|trim}) -%} {%- else -%} - {%- set label_attr = label_attr|merge({for: id, class: (label_attr.class|default('') ~ ' form-control-label')|trim}) -%} + {%- set label_attr = label_attr|merge({for: id}) -%} {%- endif -%} {% if required -%} {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %} @@ -269,23 +281,14 @@ {%- endblock form_row %} -{% block file_row -%} -
- <{{ element|default('div') }} class="custom-file"> - {{- form_widget(form) -}} - {{- form_label(form) -}} - -
-{% endblock %} - {# Errors #} {% block form_errors -%} {%- if errors|length > 0 -%} {%- for error in errors -%} - - {{ 'Error'|trans({}, 'validators') }} {{ error.message }} + + {{ 'Error'|trans({}, 'validators') }} {{ error.message }} {%- endfor -%} diff --git a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php new file mode 100644 index 0000000000000..77d522f5230aa --- /dev/null +++ b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Twig\Command\DebugCommand; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Twig\Loader\FilesystemLoader; +use Twig\Environment; + +class DebugCommandTest extends TestCase +{ + public function testDebugCommand() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array(), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Functions', trim($tester->getDisplay())); + } + + public function testLineSeparatorInLoaderPaths() + { + // these paths aren't realistic, + // they're configured to force the line separator + $tester = $this->createCommandTester(array( + 'Acme' => array('extractor', 'extractor'), + '!Acme' => array('extractor', 'extractor'), + FilesystemLoader::MAIN_NAMESPACE => array('extractor', 'extractor'), + )); + $ret = $tester->execute(array(), array('decorated' => false)); + $ds = DIRECTORY_SEPARATOR; + $loaderPaths = <<assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains($loaderPaths, trim($tester->getDisplay(true))); + } + + private function createCommandTester(array $paths = array()) + { + $filesystemLoader = new FilesystemLoader(array(), dirname(__DIR__).'/Fixtures'); + foreach ($paths as $namespace => $relDirs) { + foreach ($relDirs as $relDir) { + $filesystemLoader->addPath($relDir, $namespace); + } + } + $command = new DebugCommand(new Environment($filesystemLoader)); + + $application = new Application(); + $application->add($command); + $command = $application->find('debug:twig'); + + return new CommandTester($command); + } +} diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTest.php similarity index 99% rename from src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTest.php index 04b5b5836c650..97311b38a93f9 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Form\Tests; +namespace Symfony\Bridge\Twig\Tests\Extension; abstract class AbstractBootstrap3HorizontalLayoutTest extends AbstractBootstrap3LayoutTest { diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php similarity index 99% rename from src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php index e9cc2c026191a..ff1c221b79ff7 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php @@ -9,9 +9,10 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Form\Tests; +namespace Symfony\Bridge\Twig\Tests\Extension; use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Tests\AbstractLayoutTest; abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest { diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTest.php similarity index 94% rename from src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTest.php index 016792e0edaf6..9d216e38194f2 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Form\Tests; +namespace Symfony\Bridge\Twig\Tests\Extension; use Symfony\Component\Form\FormError; @@ -33,7 +33,7 @@ public function testRow() ./label[@for="name"] [ ./span[@class="alert alert-danger d-block"] - [./span[@class="mb-0 d-block"] + [./span[@class="d-block"] [./span[.="[trans]Error[/trans]"]] [./span[.="[trans]Error![/trans]"]] ] @@ -72,7 +72,7 @@ public function testLabelDoesNotRenderFieldAttributes() $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="col-form-label col-sm-2 form-control-label required"] + [@class="col-form-label col-sm-2 required"] ' ); } @@ -89,7 +89,7 @@ public function testLabelWithCustomAttributesPassedDirectly() $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="my&class col-form-label col-sm-2 form-control-label required"] + [@class="my&class col-form-label col-sm-2 required"] ' ); } @@ -106,7 +106,7 @@ public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="my&class col-form-label col-sm-2 form-control-label required"] + [@class="my&class col-form-label col-sm-2 required"] [.="[trans]Custom label[/trans]"] ' ); @@ -126,7 +126,7 @@ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="my&class col-form-label col-sm-2 form-control-label required"] + [@class="my&class col-form-label col-sm-2 required"] [.="[trans]Custom label[/trans]"] ' ); diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php similarity index 95% rename from src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php index 8cc1df5f52f27..1336405bdd30e 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Form\Tests; +namespace Symfony\Bridge\Twig\Tests\Extension; use Symfony\Component\Form\Extension\Core\Type\ButtonType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; @@ -42,7 +42,7 @@ public function testRow() ./label[@for="name"] [ ./span[@class="alert alert-danger d-block"] - [./span[@class="mb-0 d-block"] + [./span[@class="d-block"] [./span[.="[trans]Error[/trans]"]] [./span[.="[trans]Error![/trans]"]] ] @@ -81,7 +81,7 @@ public function testLabelDoesNotRenderFieldAttributes() $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="form-control-label required"] + [@class="required"] ' ); } @@ -98,7 +98,7 @@ public function testLabelWithCustomAttributesPassedDirectly() $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="my&class form-control-label required"] + [@class="my&class required"] ' ); } @@ -115,7 +115,7 @@ public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="my&class form-control-label required"] + [@class="my&class required"] [.="[trans]Custom label[/trans]"] ' ); @@ -135,7 +135,7 @@ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly $this->assertMatchesXpath($html, '/label [@for="name"] - [@class="my&class form-control-label required"] + [@class="my&class required"] [.="[trans]Custom label[/trans]"] ' ); @@ -189,11 +189,11 @@ public function testErrors() '/span [@class="alert alert-danger d-block"] [ - ./span[@class="mb-0 d-block"] + ./span[@class="d-block"] [./span[.="[trans]Error[/trans]"]] [./span[.="[trans]Error 1[/trans]"]] - /following-sibling::span[@class="mb-0 d-block"] + /following-sibling::span[@class="d-block"] [./span[.="[trans]Error[/trans]"]] [./span[.="[trans]Error 2[/trans]"]] ] @@ -940,9 +940,42 @@ public function testFile() { $form = $this->factory->createNamed('name', FileType::class); - $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class form-control-file')), -'/input - [@type="file"] + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'n/a', 'attr' => array('class' => 'my&class form-control-file')), +'/div + [@class="form-group"] + [ + ./div + [@class="custom-file"] + [ + ./input + [@type="file"] + [@name="name"] + /following-sibling::label + [@for="name"] + ] + ] +' + ); + } + + public function testFileWithPlaceholder() + { + $form = $this->factory->createNamed('name', FileType::class); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'n/a', 'attr' => array('class' => 'my&class form-control-file', 'placeholder' => 'Custom Placeholder')), +'/div + [@class="form-group"] + [ + ./div + [@class="custom-file"] + [ + ./input + [@type="file"] + [@name="name"] + /following-sibling::label + [@for="name" and text() = "[trans]Custom Placeholder[/trans]"] + ] + ] ' ); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php index 9c9ea12ab2b97..440bcf2a5e2e5 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -18,7 +18,6 @@ use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; use Symfony\Component\Form\FormRenderer; use Symfony\Component\Form\FormView; -use Symfony\Component\Form\Tests\AbstractBootstrap3HorizontalLayoutTest; use Twig\Environment; class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTest diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php index 64559e6c5414c..bd19f5edb7fba 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php @@ -18,7 +18,6 @@ use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; use Symfony\Component\Form\FormRenderer; use Symfony\Component\Form\FormView; -use Symfony\Component\Form\Tests\AbstractBootstrap3LayoutTest; use Twig\Environment; class FormExtensionBootstrap3LayoutTest extends AbstractBootstrap3LayoutTest diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php index de9f82beb054b..371101ff161f1 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php @@ -18,7 +18,6 @@ use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; use Symfony\Component\Form\FormRenderer; use Symfony\Component\Form\FormView; -use Symfony\Component\Form\Tests\AbstractBootstrap4HorizontalLayoutTest; use Twig\Environment; /** diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php index 19f4b506816a4..540c67c4a1693 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php @@ -18,7 +18,6 @@ use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; use Symfony\Component\Form\FormRenderer; use Symfony\Component\Form\FormView; -use Symfony\Component\Form\Tests\AbstractBootstrap4LayoutTest; use Twig\Environment; /** diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 14c80b21a33e2..839636282c78a 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -23,7 +23,7 @@ "symfony/asset": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", - "symfony/form": "^4.1", + "symfony/form": "^4.1.2", "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", @@ -41,7 +41,7 @@ "symfony/workflow": "~3.4|~4.0" }, "conflict": { - "symfony/form": "<4.1", + "symfony/form": "<4.1.2", "symfony/console": "<3.4" }, "suggest": { diff --git a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php index ab6163e3ef4d7..5130071bcdec1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php @@ -28,7 +28,7 @@ trait MicroKernelTrait * Add or import routes into your application. * * $routes->import('config/routing.yml'); - * $routes->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); + * $routes->add('/admin', 'App\Controller\AdminController::dashboard', 'admin_dashboard'); * * @param RouteCollectionBuilder $routes */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 2ae8411c22909..b1570333eb2eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -268,7 +268,7 @@ public function testWorkflows() $this->assertInstanceOf(Reference::class, $markingStoreRef); $this->assertEquals('workflow_service', (string) $markingStoreRef); - $this->assertTrue($container->hasDefinition('workflow.registry', 'Workflow registry is registered as a service')); + $this->assertTrue($container->hasDefinition('workflow.registry'), 'Workflow registry is registered as a service'); $registryDefinition = $container->getDefinition('workflow.registry'); $this->assertGreaterThan(0, count($registryDefinition->getMethodCalls())); } @@ -313,8 +313,8 @@ public function testWorkflowMultipleTransitionsWithSameName() { $container = $this->createContainerFromFile('workflow_with_multiple_transitions_with_same_name'); - $this->assertTrue($container->hasDefinition('workflow.article', 'Workflow is registered as a service')); - $this->assertTrue($container->hasDefinition('workflow.article.definition', 'Workflow definition is registered as a service')); + $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is registered as a service'); + $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); $workflowDefinition = $container->getDefinition('workflow.article.definition'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php index a1d91e7929d65..0c82d8cc73eb2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php @@ -43,6 +43,14 @@ public function welcomeAction(Request $request, $name = null) return new Response(sprintf('Welcome back %s, nice to meet you.', $name)); } + public function cacheableAction() + { + $response = new Response('all good'); + $response->setSharedMaxAge(100); + + return $response; + } + public function logoutAction(Request $request) { $request->getSession()->invalidate(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml index 0730b723282cc..3cbdf944af3bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml @@ -2,6 +2,10 @@ session_welcome: path: /session defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SessionController::welcomeAction } +session_cacheable: + path: /cacheable + defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SessionController::cacheableAction } + session_welcome_name: path: /session/{name} defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SessionController::welcomeAction } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php index 166d8a33919df..2e1634220c3a5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php @@ -126,6 +126,22 @@ public function testTwoClients($config, $insulate) $this->assertContains('Welcome back client2, nice to meet you.', $crawler2->text()); } + /** + * @dataProvider getConfigs + */ + public function testCorrectCacheControlHeadersForCacheableAction($config, $insulate) + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client->insulate(); + } + + $client->request('GET', '/cacheable'); + + $response = $client->getResponse(); + $this->assertSame('public, s-maxage=100', $response->headers->get('cache-control')); + } + public function getConfigs() { return array( diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 0c2a3dec226d8..de0fe10f91224 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -71,6 +71,7 @@ "symfony/serializer": "<4.1", "symfony/stopwatch": "<3.4", "symfony/translation": "<3.4", + "symfony/twig-bridge": "<4.1.1", "symfony/validator": "<4.1", "symfony/workflow": "<4.1" }, diff --git a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php index 84227cf47e72e..0b36054fd0679 100644 --- a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php +++ b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php @@ -70,7 +70,7 @@ protected function configure() security: encoders: Symfony\Component\Security\Core\User\User: plaintext - AppBundle\Entity\User: bcrypt + App\Entity\User: bcrypt If you execute the command non-interactively, the first available configured @@ -82,16 +82,16 @@ protected function configure() Pass the full user class path as the second argument to encode passwords for your own entities: - php %command.full_name% --no-interaction [password] AppBundle\Entity\User + php %command.full_name% --no-interaction [password] App\Entity\User Executing the command interactively allows you to generate a random salt for encoding the password: - php %command.full_name% [password] AppBundle\Entity\User + php %command.full_name% [password] App\Entity\User In case your encoder doesn't require a salt, add the empty-salt option: - php %command.full_name% --empty-salt [password] AppBundle\Entity\User + php %command.full_name% --empty-salt [password] App\Entity\User EOF ) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index a2feb22c8e170..b0d7353834690 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -363,8 +363,8 @@ private function addEncodersSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('encoders') ->example(array( - 'AppBundle\Entity\User1' => 'bcrypt', - 'AppBundle\Entity\User2' => array( + 'App\Entity\User1' => 'bcrypt', + 'App\Entity\User2' => array( 'algorithm' => 'bcrypt', 'cost' => 13, ), diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 2a2b3793fd845..09056e177093b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -70,6 +70,7 @@ .sf-toolbarreset svg, .sf-toolbarreset img { height: 20px; + width: 20px; display: inline-block; } @@ -160,11 +161,11 @@ margin-bottom: 0; } -.sf-toolbar-block .sf-toolbar-info-piece a { +div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece a { color: #99CDD8; text-decoration: underline; } -.sf-toolbar-block .sf-toolbar-info-piece a:hover { +div.sf-toolbar .sf-toolbar-block a:hover { text-decoration: none; } @@ -292,6 +293,7 @@ border-width: 0; position: relative; top: 8px; + vertical-align: baseline; } .sf-toolbar-block .sf-toolbar-icon img + span, diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php index cf2384c5f37e6..266e1f850c953 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php @@ -34,6 +34,34 @@ public function testLongKey() $cache->hasItem(str_repeat('-', 39)); } + public function testLongKeyVersioning() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs(array(str_repeat('-', 26))) + ->getMock(); + + $reflectionClass = new \ReflectionClass(AbstractAdapter::class); + + $reflectionMethod = $reflectionClass->getMethod('getId'); + $reflectionMethod->setAccessible(true); + + // No versioning enabled + $this->assertEquals('--------------------------:------------', $reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12)))); + $this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12))))); + $this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 23))))); + $this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 40))))); + + $reflectionProperty = $reflectionClass->getProperty('versioningIsEnabled'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($cache, true); + + // Versioning enabled + $this->assertEquals('--------------------------:1:------------', $reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12)))); + $this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12))))); + $this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 23))))); + $this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 40))))); + } + /** * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException * @expectedExceptionMessage Namespace must be 26 chars max, 40 given ("----------------------------------------") diff --git a/src/Symfony/Component/Cache/Traits/AbstractTrait.php b/src/Symfony/Component/Cache/Traits/AbstractTrait.php index 7953ae6e8beee..ec60fe79c4a6b 100644 --- a/src/Symfony/Component/Cache/Traits/AbstractTrait.php +++ b/src/Symfony/Component/Cache/Traits/AbstractTrait.php @@ -255,7 +255,7 @@ private function getId($key) return $this->namespace.$this->namespaceVersion.$key; } if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) { - $id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), ':', -22); + $id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), ':', -(\strlen($this->namespaceVersion) + 22)); } return $id; diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index b64d9af361e55..62c3a834f6a2d 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -220,7 +220,7 @@ public function run(InputInterface $input, OutputInterface $output) if (function_exists('cli_set_process_title')) { if (!@cli_set_process_title($this->processTitle)) { if ('Darwin' === PHP_OS) { - $output->writeln('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.'); + $output->writeln('Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', OutputInterface::VERBOSITY_VERY_VERBOSE); } else { cli_set_process_title($this->processTitle); } diff --git a/src/Symfony/Component/Console/Helper/TableStyle.php b/src/Symfony/Component/Console/Helper/TableStyle.php index a0d0b5b770737..3339debe5dd02 100644 --- a/src/Symfony/Component/Console/Helper/TableStyle.php +++ b/src/Symfony/Component/Console/Helper/TableStyle.php @@ -110,7 +110,7 @@ public function setHorizontalBorderChars(string $outside, string $inside = null) */ public function setHorizontalBorderChar($horizontalBorderChar) { - @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->setHorizontalBorderChars($horizontalBorderChar, $horizontalBorderChar); } @@ -124,7 +124,7 @@ public function setHorizontalBorderChar($horizontalBorderChar) */ public function getHorizontalBorderChar() { - @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->horizontalOutsideBorderChar; } @@ -166,7 +166,7 @@ public function setVerticalBorderChars(string $outside, string $inside = null): */ public function setVerticalBorderChar($verticalBorderChar) { - @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->setVerticalBorderChars($verticalBorderChar, $verticalBorderChar); } @@ -180,7 +180,7 @@ public function setVerticalBorderChar($verticalBorderChar) */ public function getVerticalBorderChar() { - @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->verticalOutsideBorderChar; } @@ -268,7 +268,7 @@ public function setDefaultCrossingChar(string $char): self */ public function setCrossingChar($crossingChar) { - @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1. Use setDefaultCrossingChar() instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use setDefaultCrossingChar() instead.', __METHOD__), E_USER_DEPRECATED); return $this->setDefaultCrossingChar($crossingChar); } diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index edeabdbf257bc..14276ba31711b 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -301,7 +301,7 @@ public function getParameterOption($values, $default = false, $onlyParams = fals while (0 < count($tokens)) { $token = array_shift($tokens); if ($onlyParams && '--' === $token) { - return false; + return $default; } foreach ($values as $value) { diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php index 4d9797ba1af0c..595a8a0e8cbc1 100644 --- a/src/Symfony/Component/Console/Input/ArrayInput.php +++ b/src/Symfony/Component/Console/Input/ArrayInput.php @@ -81,7 +81,7 @@ public function getParameterOption($values, $default = false, $onlyParams = fals foreach ($this->parameters as $k => $v) { if ($onlyParams && ('--' === $k || (is_int($k) && '--' === $v))) { - return false; + return $default; } if (is_int($k)) { diff --git a/src/Symfony/Component/Console/Output/OutputInterface.php b/src/Symfony/Component/Console/Output/OutputInterface.php index 5742e553a9310..0dfd12b98749c 100644 --- a/src/Symfony/Component/Console/Output/OutputInterface.php +++ b/src/Symfony/Component/Console/Output/OutputInterface.php @@ -33,7 +33,7 @@ interface OutputInterface /** * Writes a message to the output. * - * @param string|iterable $messages The message as an iterable of lines or a single string + * @param string|iterable $messages The message as an iterable of strings or a single string * @param bool $newline Whether to add a newline * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL */ @@ -42,7 +42,7 @@ public function write($messages, $newline = false, $options = 0); /** * Writes a message to the output and adds a newline at the end. * - * @param string|iterable $messages The message as an iterable of lines of a single string + * @param string|iterable $messages The message as an iterable of strings or a single string * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL */ public function writeln($messages, $options = 0); diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index 476b2fa0dc05c..d012a0a5c6ebd 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -93,6 +93,10 @@ protected function doWrite($message, $newline) */ protected function hasColorSupport() { + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + if (DIRECTORY_SEPARATOR === '\\') { return (function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support($this->stream)) diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 08f86225c1b3c..26018e2338648 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -269,7 +269,7 @@ public function createProgressBar($max = 0) { $progressBar = parent::createProgressBar($max); - if ('\\' !== DIRECTORY_SEPARATOR) { + if ('\\' !== DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) { $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 $progressBar->setProgressCharacter(''); $progressBar->setBarCharacter('▓'); // dark shade character \u2593 diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php index 61d1723e0842e..4cd7ab478f3a4 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -395,25 +395,26 @@ public function testToString() /** * @dataProvider provideGetParameterOptionValues */ - public function testGetParameterOptionEqualSign($argv, $key, $onlyParams, $expected) + public function testGetParameterOptionEqualSign($argv, $key, $default, $onlyParams, $expected) { $input = new ArgvInput($argv); - $this->assertEquals($expected, $input->getParameterOption($key, false, $onlyParams), '->getParameterOption() returns the expected value'); + $this->assertEquals($expected, $input->getParameterOption($key, $default, $onlyParams), '->getParameterOption() returns the expected value'); } public function provideGetParameterOptionValues() { return array( - array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', false, 'dev'), - array(array('app/console', 'foo:bar', '--env=dev'), '--env', false, 'dev'), - array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), false, 'dev'), - array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), false, 'dev'), - array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), false, '1'), - array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), false, '1'), - array(array('app/console', 'foo:bar', '--env', 'val'), '--env', false, 'val'), - array(array('app/console', 'foo:bar', '--env', 'val', '--dummy'), '--env', false, 'val'), - array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', false, 'dev'), - array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', true, false), + array(array('app/console', 'foo:bar'), '-e', 'default', false, 'default'), + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', 'default', false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', 'default', false, 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'default', false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'default', false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), 'default', false, '1'), + array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), 'default', false, '1'), + array(array('app/console', 'foo:bar', '--env', 'val'), '--env', 'default', false, 'val'), + array(array('app/console', 'foo:bar', '--env', 'val', '--dummy'), '--env', 'default', false, 'val'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', 'default', false, 'dev'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', 'default', true, 'default'), ); } diff --git a/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php index 6b443e0b2abae..34b67ac7f5235 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php @@ -47,14 +47,14 @@ public function testGetParameterOption() { $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); - $this->assertFalse($input->getParameterOption('--bar'), '->getParameterOption() returns the default if an option is not present in the passed parameters'); + $this->assertEquals('default', $input->getParameterOption('--bar', 'default'), '->getParameterOption() returns the default value if an option is not present in the passed parameters'); $input = new ArrayInput(array('Fabien', '--foo' => 'bar')); $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); $input = new ArrayInput(array('--foo', '--', '--bar' => 'woop')); $this->assertEquals('woop', $input->getParameterOption('--bar'), '->getParameterOption() returns the correct value if an option is present in the passed parameters'); - $this->assertFalse($input->getParameterOption('--bar', false, true), '->getParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); + $this->assertEquals('default', $input->getParameterOption('--bar', 'default', true), '->getParameterOption() returns the default value if an option is present in the passed parameters after an end of options signal'); } public function testParseArguments() diff --git a/src/Symfony/Component/Debug/Exception/FlattenException.php b/src/Symfony/Component/Debug/Exception/FlattenException.php index f70085160ba3a..2afffa21b6e82 100644 --- a/src/Symfony/Component/Debug/Exception/FlattenException.php +++ b/src/Symfony/Component/Debug/Exception/FlattenException.php @@ -186,7 +186,7 @@ public function getTrace() */ public function setTraceFromException(\Exception $exception) { - @trigger_error(sprintf('"%s" is deprecated since Symfony 4.1, use "setTraceFromThrowable()" instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use "setTraceFromThrowable()" instead.', __METHOD__), E_USER_DEPRECATED); $this->setTraceFromThrowable($exception); } diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index a67637ea6e655..0e3ec00717958 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -208,48 +208,54 @@ public function getContent(FlattenException $exception) $title = 'Whoops, looks like something went wrong.'; } + if (!$this->debug) { + return << +

$title

+ +EOF; + } + $content = ''; - if ($this->debug) { - try { - $count = count($exception->getAllPrevious()); - $total = $count + 1; - foreach ($exception->toArray() as $position => $e) { - $ind = $count - $position + 1; - $class = $this->formatClass($e['class']); - $message = nl2br($this->escapeHtml($e['message'])); - $content .= sprintf(<<<'EOF' -
- - - + try { + $count = count($exception->getAllPrevious()); + $total = $count + 1; + foreach ($exception->toArray() as $position => $e) { + $ind = $count - $position + 1; + $class = $this->formatClass($e['class']); + $message = nl2br($this->escapeHtml($e['message'])); + $content .= sprintf(<<<'EOF' +
+
-

- (%d/%d) - %s -

-

%s

-
+ + EOF - , $ind, $total, $class, $message); - foreach ($e['trace'] as $trace) { - $content .= '\n"; + , $ind, $total, $class, $message); + foreach ($e['trace'] as $trace) { + $content .= '\n
+

+ (%d/%d) + %s +

+

%s

+
'; - if ($trace['function']) { - $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); - } - if (isset($trace['file']) && isset($trace['line'])) { - $content .= $this->formatPath($trace['file'], $trace['line']); - } - $content .= "
'; + if ($trace['function']) { + $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); } - - $content .= "
\n
\n"; - } - } catch (\Exception $e) { - // something nasty happened and we cannot throw an exception anymore - if ($this->debug) { - $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage())); - } else { - $title = 'Whoops, looks like something went wrong.'; + if (isset($trace['file']) && isset($trace['line'])) { + $content .= $this->formatPath($trace['file'], $trace['line']); + } + $content .= "\n"; } + + $content .= "\n\n\n"; + } + } catch (\Exception $e) { + // something nasty happened and we cannot throw an exception anymore + if ($this->debug) { + $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage())); + } else { + $title = 'Whoops, looks like something went wrong.'; } } @@ -278,6 +284,14 @@ public function getContent(FlattenException $exception) */ public function getStylesheet(FlattenException $exception) { + if (!$this->debug) { + return <<<'EOF' + body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; } + .container { margin: 30px; max-width: 600px; } + h1 { color: #dc3545; font-size: 24px; } +EOF; + } + return <<<'EOF' body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index 500de64bc2f0f..63309b3a17e77 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -123,10 +123,11 @@ private function processDefinition(ContainerBuilder $container, $id, Definition } $definition->setMethodCalls(array_merge($instanceofCalls, $definition->getMethodCalls())); + $definition->setBindings($bindings); // reset fields with "merge" behavior $abstract - ->setBindings($bindings) + ->setBindings(array()) ->setArguments(array()) ->setMethodCalls(array()) ->setDecoratedService(null) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php index 402aca6378b63..06b655328b43d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ValidateEnvPlaceholdersPass.php @@ -65,7 +65,7 @@ public function process(ContainerBuilder $container) $processor = new Processor(); foreach ($extensions as $name => $extension) { - if (!$extension instanceof ConfigurationExtensionInterface || !$config = $container->getExtensionConfig($name)) { + if (!$extension instanceof ConfigurationExtensionInterface || !$config = array_filter($container->getExtensionConfig($name))) { // this extension has no semantic configuration or was not called continue; } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 1c2c239869131..2a8dd0c4f1246 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -606,10 +606,10 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_ * the parameters passed to the container constructor to have precedence * over the loaded ones. * - * $container = new ContainerBuilder(array('foo' => 'bar')); + * $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar'))); * $loader = new LoaderXXX($container); * $loader->load('resource_name'); - * $container->register('foo', new stdClass()); + * $container->register('foo', 'stdClass'); * * In the above example, even if the loaded resource defines a foo * parameter, the value will still be 'bar' as defined in the ContainerBuilder diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index d790aff7d66d2..660f1f4defad6 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1662,8 +1662,10 @@ private function getServiceCall(string $id, Reference $reference = null): string return '$this'; } - if ($this->container->hasDefinition($id) && ($definition = $this->container->getDefinition($id)) && !$definition->isSynthetic()) { - if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { + if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) { + if ($definition->isSynthetic()) { + $code = sprintf('$this->get(\'%s\'%s)', $id, null !== $reference ? ', '.$reference->getInvalidBehavior() : ''); + } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { $code = 'null'; if (!$definition->isShared()) { return $code; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 8fa341bcadac4..73f29a6145e6d 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -94,7 +94,7 @@ private function addService(string $id, Definition $definition): string } if ($definition->isDeprecated()) { - $code .= sprintf(" deprecated: %s\n", $definition->getDeprecationMessage('%service_id%')); + $code .= sprintf(" deprecated: %s\n", $this->dumper->dump($definition->getDeprecationMessage('%service_id%'))); } if ($definition->isAutowired()) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php index 38164fc10589d..cc96c5a517849 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; @@ -268,4 +269,18 @@ public function testMergeReset() $this->assertEmpty($abstract->getTags()); $this->assertTrue($abstract->isAbstract()); } + + public function testBindings() + { + $container = new ContainerBuilder(); + $def = $container->register('foo', self::class)->setBindings(array('$toto' => 123)); + $def->setInstanceofConditionals(array(parent::class => new ChildDefinition(''))); + + (new ResolveInstanceofConditionalsPass())->process($container); + + $bindings = $container->getDefinition('foo')->getBindings(); + $this->assertSame(array('$toto'), array_keys($bindings)); + $this->assertInstanceOf(BoundArgument::class, $bindings['$toto']); + $this->assertSame(123, $bindings['$toto']->getValues()[0]); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php index b5841f343a4e8..cbec6bf778a49 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php @@ -234,6 +234,18 @@ public function testConfigurationWithoutRootNode(): void $this->addToAssertionCount(1); } + public function testEmptyConfigFromMoreThanOneSource() + { + $container = new ContainerBuilder(); + $container->registerExtension(new EnvExtension(new ConfigurationWithArrayNodeRequiringOneElement())); + $container->loadFromExtension('env_extension', array()); + $container->loadFromExtension('env_extension', array()); + + $this->doProcess($container); + + $this->addToAssertionCount(1); + } + public function testDiscardedEnvInConfig(): void { $container = new ContainerBuilder(); @@ -313,6 +325,24 @@ public function getConfigTreeBuilder() } } +class ConfigurationWithArrayNodeRequiringOneElement implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $treeBuilder->root('env_extension') + ->children() + ->arrayNode('nodes') + ->isRequired() + ->requiresAtLeastOneElement() + ->scalarPrototype()->end() + ->end() + ->end(); + + return $treeBuilder; + } +} + class EnvExtension extends Extension { private $configuration; @@ -335,6 +365,10 @@ public function getConfiguration(array $config, ContainerBuilder $container) public function load(array $configs, ContainerBuilder $container) { + if (!array_filter($configs)) { + return; + } + try { $this->config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs); } catch (TreeWithoutRootNodeException $e) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index a8402569905a3..7fde6ec969408 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1399,6 +1399,21 @@ public function testArgumentsHaveHigherPriorityThanBindings() $this->assertSame('via-bindings', $container->get('foo')->class2->identifier); } + public function testUninitializedSyntheticReference() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true)->setSynthetic(true); + $container->register('bar', 'stdClass')->setPublic(true)->setShared(false) + ->setProperty('foo', new Reference('foo', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)); + + $container->compile(); + + $this->assertEquals((object) array('foo' => null), $container->get('bar')); + + $container->set('foo', (object) array(123)); + $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar')); + } + public function testIdCanBeAnObjectAsLongAsItCanBeCastToString() { $id = new Reference('another_service'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 52e7ff18fab44..29cbf1f6a410a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -953,6 +953,29 @@ public function testDumpHandlesObjectClassNames() $this->assertInstanceOf('stdClass', $container->get('bar')); } + public function testUninitializedSyntheticReference() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true)->setSynthetic(true); + $container->register('bar', 'stdClass')->setPublic(true)->setShared(false) + ->setProperty('foo', new Reference('foo', ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE)); + + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array( + 'class' => 'Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference', + 'inline_class_loader_parameter' => 'inline_requires', + ))); + + $container = new \Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference(); + + $this->assertEquals((object) array('foo' => null), $container->get('bar')); + + $container->set('foo', (object) array(123)); + $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar')); + } + /** * This test checks the trigger of a deprecation note and should not be removed in major releases. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml index 5394535caf1d8..ebfe087d779cf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.expected.yml @@ -10,7 +10,7 @@ services: tags: - { name: foo } - { name: baz } - deprecated: %service_id% + deprecated: '%service_id%' arguments: [1] factory: f Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar: @@ -19,7 +19,7 @@ services: tags: - { name: foo } - { name: baz } - deprecated: %service_id% + deprecated: '%service_id%' lazy: true arguments: [1] factory: f diff --git a/src/Symfony/Component/DependencyInjection/TypedReference.php b/src/Symfony/Component/DependencyInjection/TypedReference.php index 9d488ddbb478c..2bd25ab46d299 100644 --- a/src/Symfony/Component/DependencyInjection/TypedReference.php +++ b/src/Symfony/Component/DependencyInjection/TypedReference.php @@ -29,7 +29,7 @@ class TypedReference extends Reference public function __construct(string $id, string $type, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { if (\is_string($invalidBehavior) || 3 < \func_num_args()) { - @trigger_error(sprintf('The $requiringClass argument of "%s" is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The $requiringClass argument of "%s()" is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); $this->requiringClass = $invalidBehavior; $invalidBehavior = 3 < \func_num_args() ? \func_get_arg(3) : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; @@ -48,7 +48,7 @@ public function getType() */ public function getRequiringClass() { - @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); return $this->requiringClass ?? ''; } @@ -58,7 +58,7 @@ public function getRequiringClass() */ public function canBeAutoregistered() { - @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); return $this->requiringClass && (false !== $i = strpos($this->type, '\\')) && 0 === strncasecmp($this->type, $this->requiringClass, 1 + $i); } diff --git a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php index 4d3aebfba531f..b24bbd879b5ad 100644 --- a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php +++ b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -75,7 +75,7 @@ public function isDisabled() /** * Sets the value of the field. * - * @param string $value The value of the field + * @param string|array $value The value of the field */ public function select($value) { diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 3b6e7cc393144..8d851623fb67a 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -217,6 +217,7 @@ public function getOrphanedEvents(): array public function reset() { $this->called = array(); + $this->orphanedEvents = array(); } /** diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php index 2521f741ea1fc..ab3b33e3779a4 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -271,6 +271,17 @@ public function testListenerCanRemoveItselfWhenExecuted() $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); } + + public function testClearOrphanedEvents() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->dispatch('foo'); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertCount(1, $events); + $tdispatcher->reset(); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertCount(0, $events); + } } class EventSubscriber implements EventSubscriberInterface diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php index e206936dd32e8..e7c41e0cec46f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php @@ -77,7 +77,7 @@ public function getBlockPrefix() */ public function loadChoiceList($value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); if (null !== $this->choiceList) { return $this->choiceList; @@ -93,7 +93,7 @@ public function loadChoiceList($value = null) */ public function loadChoicesForValues(array $values, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $values = array_filter($values); @@ -116,7 +116,7 @@ public function loadChoicesForValues(array $values, $value = null) */ public function loadValuesForChoices(array $choices, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $choices = array_filter($choices); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php index 79d874ca400b6..c6759ea3da912 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php @@ -77,7 +77,7 @@ public function getBlockPrefix() */ public function loadChoiceList($value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); if (null !== $this->choiceList) { return $this->choiceList; @@ -93,7 +93,7 @@ public function loadChoiceList($value = null) */ public function loadChoicesForValues(array $values, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $values = array_filter($values); @@ -116,7 +116,7 @@ public function loadChoicesForValues(array $values, $value = null) */ public function loadValuesForChoices(array $choices, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $choices = array_filter($choices); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php index 6d35c2392140f..c9bc9e6042c35 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php @@ -77,7 +77,7 @@ public function getBlockPrefix() */ public function loadChoiceList($value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); if (null !== $this->choiceList) { return $this->choiceList; @@ -93,7 +93,7 @@ public function loadChoiceList($value = null) */ public function loadChoicesForValues(array $values, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $values = array_filter($values); @@ -116,7 +116,7 @@ public function loadChoicesForValues(array $values, $value = null) */ public function loadValuesForChoices(array $choices, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $choices = array_filter($choices); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php index 4c0c467729ff1..c0de1c933bd17 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php @@ -77,7 +77,7 @@ public function getBlockPrefix() */ public function loadChoiceList($value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); if (null !== $this->choiceList) { return $this->choiceList; @@ -93,7 +93,7 @@ public function loadChoiceList($value = null) */ public function loadChoicesForValues(array $values, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $values = array_filter($values); @@ -116,7 +116,7 @@ public function loadChoicesForValues(array $values, $value = null) */ public function loadValuesForChoices(array $choices, $value = null) { - @trigger_error(sprintf('Method "%s" is deprecated since Symfony 4.1, use "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use the "choice_loader" option instead.', __METHOD__), E_USER_DEPRECATED); // Optimize $choices = array_filter($choices); diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 3fac07591db98..a3b7765e12475 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -19,6 +19,7 @@ use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Caster\StubCaster; use Symfony\Component\VarDumper\Cloner\Stub; /** @@ -261,6 +262,7 @@ protected function getCasters() Caster::PREFIX_VIRTUAL.'type_class' => new ClassStub(get_class($f->getConfig()->getType()->getInnerType())), ); }, + FormView::class => array(StubCaster::class, 'cutInternals'), ConstraintViolationInterface::class => function (ConstraintViolationInterface $v, array $a) { return array( Caster::PREFIX_VIRTUAL.'root' => $v->getRoot(), diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php index 7ac98b1cf2e0b..e3660e19adbc0 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -138,7 +138,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface private $data; /** - * @var string + * @var string|null */ private $dataClass; @@ -181,7 +181,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface * Creates an empty form configuration. * * @param string|int $name The form name - * @param string $dataClass The class of the form's data + * @param string|null $dataClass The class of the form's data * @param EventDispatcherInterface $dispatcher The event dispatcher * @param array $options The form options * diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt index c622057cdbdfd..391c3e694d974 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt @@ -15,9 +15,9 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_translation_domain ---------------- --------------------%s Allowed values - %s ---------------- --------------------%s - Normalizer Closure { %s + Normalizer Closure%s{%w parameters: 2 %s - file: "%s%eExtension%eCore%eType%eChoiceType.php" + file: "%s%eExtension%eCore%eType%eChoiceType.php"%w line: "%s to %s" %s } %s ---------------- --------------------%s diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt index 231d1319bbeff..846d6f384684c 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt @@ -8,14 +8,14 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (empty_data) Default Value: null %s %s Closure(s): [ %s - Closure { %s + Closure%s{%w parameters: 1 %s - file: "%s%eExtension%eCore%eType%eFormType.php" + file: "%s%eExtension%eCore%eType%eFormType.php"%w line: "%s to %s" %s }, %s - Closure { %s + Closure%s{%w parameters: 2 %s - file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php" + file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"%w line: "%s to %s" %s } %s ] %s diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt index da3e6c82d8b1a..8cc88a550ab70 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt @@ -16,9 +16,9 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (foo) "baz" %s ] %s ---------------- --------------------%s - Normalizer Closure { %s + Normalizer Closure%s{%w parameters: 2 %s - file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php" + file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"%w line: "%s to %s" %s } %s ---------------- --------------------%s diff --git a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php index 252f46ad8e59b..594decb067281 100644 --- a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php +++ b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -158,7 +158,7 @@ public function guessClientExtension() */ public function getClientSize() { - @trigger_error(sprintf('"%s" is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED); return $this->getSize(); } diff --git a/src/Symfony/Component/HttpFoundation/HeaderBag.php b/src/Symfony/Component/HttpFoundation/HeaderBag.php index 7026732fe7721..d11887fb61b2f 100644 --- a/src/Symfony/Component/HttpFoundation/HeaderBag.php +++ b/src/Symfony/Component/HttpFoundation/HeaderBag.php @@ -101,11 +101,11 @@ public function add(array $headers) /** * Returns a header value by name. * - * @param string $key The header name - * @param string|string[] $default The default value - * @param bool $first Whether to return the first value or all header values + * @param string $key The header name + * @param string|string[]|null $default The default value + * @param bool $first Whether to return the first value or all header values * - * @return string|string[] The first header value or default value if $first is true, an array of values otherwise + * @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise */ public function get($key, $default = null, $first = true) { diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 7cc9d91ab40a5..b7433173c7d9b 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -64,7 +64,12 @@ class Response const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918 const HTTP_LOCKED = 423; // RFC4918 const HTTP_FAILED_DEPENDENCY = 424; // RFC4918 + + /** + * @deprecated + */ const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817 + const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04 const HTTP_UPGRADE_REQUIRED = 426; // RFC2817 const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585 const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585 @@ -169,7 +174,7 @@ class Response 422 => 'Unprocessable Entity', // RFC4918 423 => 'Locked', // RFC4918 424 => 'Failed Dependency', // RFC4918 - 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817 + 425 => 'Too Early', // RFC-ietf-httpbis-replay-04 426 => 'Upgrade Required', // RFC2817 428 => 'Precondition Required', // RFC6585 429 => 'Too Many Requests', // RFC6585 @@ -324,12 +329,17 @@ public function sendHeaders() } // headers - foreach ($this->headers->allPreserveCase() as $name => $values) { + foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) { foreach ($values as $value) { header($name.': '.$value, false, $this->statusCode); } } + // cookies + foreach ($this->headers->getCookies() as $cookie) { + header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode); + } + // status header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php index 19b57762a69fd..dd7a7fad822f7 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php @@ -124,7 +124,13 @@ protected function &resolveAttributePath($name, $writeContext = false) foreach ($parts as $part) { if (null !== $array && !array_key_exists($part, $array)) { - $array[$part] = $writeContext ? array() : null; + if (!$writeContext) { + $null = null; + + return $null; + } + + $array[$part] = array(); } $array = &$array[$part]; diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php index 80e97f17cdff3..f53c9dae6c0aa 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php +++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php @@ -24,7 +24,7 @@ interface FlashBagInterface extends SessionBagInterface * Adds a flash message for type. * * @param string $type - * @param string $message + * @param mixed $message */ public function add($type, $message); diff --git a/src/Symfony/Component/HttpFoundation/Session/Session.php b/src/Symfony/Component/HttpFoundation/Session/Session.php index f0379c1697b82..c0978d552f197 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Session.php +++ b/src/Symfony/Component/HttpFoundation/Session/Session.php @@ -54,8 +54,6 @@ public function __construct(SessionStorageInterface $storage = null, AttributeBa */ public function start() { - ++$this->usageIndex; - return $this->storage->start(); } @@ -160,7 +158,9 @@ public function getUsageIndex() */ public function isEmpty() { - ++$this->usageIndex; + if ($this->isStarted()) { + ++$this->usageIndex; + } foreach ($this->data as &$data) { if (!empty($data)) { return false; @@ -185,8 +185,6 @@ public function invalidate($lifetime = null) */ public function migrate($destroy = false, $lifetime = null) { - ++$this->usageIndex; - return $this->storage->regenerate($destroy, $lifetime); } @@ -195,8 +193,6 @@ public function migrate($destroy = false, $lifetime = null) */ public function save() { - ++$this->usageIndex; - $this->storage->save(); } diff --git a/src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php b/src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php index 88005ee092e2a..3504bdfe7b4a6 100644 --- a/src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php +++ b/src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php @@ -44,6 +44,9 @@ public function getBag() */ public function isEmpty() { + if (!isset($this->data[$this->bag->getStorageKey()])) { + return true; + } ++$this->usageIndex; return empty($this->data[$this->bag->getStorageKey()]); @@ -81,8 +84,6 @@ public function getStorageKey() */ public function clear() { - ++$this->usageIndex; - return $this->bag->clear(); } } diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php index 59242ef387f81..bad59332a9c62 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/RedisSessionHandler.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; use Predis\Response\ErrorInterface; +use Symfony\Component\Cache\Traits\RedisProxy; /** * Redis based session storage handler based on the Redis class diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php index 12e32df36afd5..bfe13f05ed2e5 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -407,8 +407,6 @@ public function setSaveHandler($saveHandler = null) } if ($this->saveHandler instanceof SessionHandlerProxy) { - session_set_save_handler($this->saveHandler->getHandler(), false); - } elseif ($this->saveHandler instanceof \SessionHandlerInterface) { session_set_save_handler($this->saveHandler, false); } } diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php index 53c1209a1c07d..b11cc397a0973 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php @@ -14,7 +14,7 @@ /** * @author Drak */ -class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface +class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface { protected $handler; @@ -82,4 +82,20 @@ public function gc($maxlifetime) { return (bool) $this->handler->gc($maxlifetime); } + + /** + * {@inheritdoc} + */ + public function validateId($sessionId) + { + return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId); + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data); + } } diff --git a/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/src/Symfony/Component/HttpFoundation/StreamedResponse.php index a7c2150252eb6..2519f874cceca 100644 --- a/src/Symfony/Component/HttpFoundation/StreamedResponse.php +++ b/src/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -141,4 +141,16 @@ public function getContent() { return false; } + + /** + * {@inheritdoc} + * + * @return $this + */ + public function setNotModified() + { + $this->setCallback(function () {}); + + return parent::setNotModified(); + } } 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 index 4e9c4c075f5ed..14e44a398af66 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected @@ -4,7 +4,7 @@ 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=/ + [3] => Set-Cookie: ?*():@&+$/%#[]=%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/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index b4af82ddf68de..a51b954fa0bd0 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -126,7 +126,7 @@ public function testMustRevalidateWithProxyRevalidateCacheControlHeader() public function testSetNotModified() { - $response = new Response(); + $response = new Response('foo'); $modified = $response->setNotModified(); $this->assertObjectHasAttribute('headers', $modified); $this->assertObjectHasAttribute('content', $modified); @@ -135,6 +135,11 @@ public function testSetNotModified() $this->assertObjectHasAttribute('statusText', $modified); $this->assertObjectHasAttribute('charset', $modified); $this->assertEquals(304, $modified->getStatusCode()); + + ob_start(); + $modified->sendContent(); + $string = ob_get_clean(); + $this->assertEmpty($string); } public function testIsSuccessful() diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php index f074ce1b26261..ec4cd5ad1a146 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php @@ -82,6 +82,17 @@ public function testHas($key, $value, $exists) $this->assertEquals($exists, $this->bag->has($key)); } + /** + * @dataProvider attributesProvider + */ + public function testHasNoSideEffect($key, $value, $expected) + { + $expected = json_encode($this->bag->all()); + $this->bag->has($key); + + $this->assertEquals($expected, json_encode($this->bag->all())); + } + /** * @dataProvider attributesProvider */ @@ -96,6 +107,17 @@ public function testGetDefaults() $this->assertEquals('default', $this->bag->get('user2.login', 'default')); } + /** + * @dataProvider attributesProvider + */ + public function testGetNoSideEffect($key, $value, $expected) + { + $expected = json_encode($this->bag->all()); + $this->bag->get($key); + + $this->assertEquals($expected, json_encode($this->bag->all())); + } + /** * @dataProvider attributesProvider */ diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.php index c4e75b1b18b40..905a1f7517306 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.php @@ -74,6 +74,18 @@ public function testPeek() $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); } + public function testAdd() + { + $tab = array('bar' => 'baz'); + $this->bag->add('string_message', 'lorem'); + $this->bag->add('object_message', new \stdClass()); + $this->bag->add('array_message', $tab); + + $this->assertEquals(array('lorem'), $this->bag->get('string_message')); + $this->assertEquals(array(new \stdClass()), $this->bag->get('object_message')); + $this->assertEquals(array($tab), $this->bag->get('array_message')); + } + public function testGet() { $this->assertEquals(array(), $this->bag->get('non_existing')); @@ -112,6 +124,19 @@ public function testKeys() $this->assertEquals(array('notice'), $this->bag->keys()); } + public function testSetAll() + { + $this->bag->add('one_flash', 'Foo'); + $this->bag->add('another_flash', 'Bar'); + $this->assertTrue($this->bag->has('one_flash')); + $this->assertTrue($this->bag->has('another_flash')); + $this->bag->setAll(array('unique_flash' => 'FooBar')); + $this->assertFalse($this->bag->has('one_flash')); + $this->assertFalse($this->bag->has('another_flash')); + $this->assertSame(array('unique_flash' => 'FooBar'), $this->bag->all()); + $this->assertSame(array(), $this->bag->all()); + } + public function testPeekAll() { $this->bag->set('notice', 'Foo'); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php index 682825356a724..0b48250e01010 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php @@ -121,4 +121,37 @@ public function testGc() $this->proxy->gc(86400); } + + /** + * @requires PHPUnit 5.1 + */ + public function testValidateId() + { + $mock = $this->getMockBuilder(array('SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'))->getMock(); + $mock->expects($this->once()) + ->method('validateId'); + + $proxy = new SessionHandlerProxy($mock); + $proxy->validateId('id'); + + $this->assertTrue($this->proxy->validateId('id')); + } + + /** + * @requires PHPUnit 5.1 + */ + public function testUpdateTimestamp() + { + $mock = $this->getMockBuilder(array('SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'))->getMock(); + $mock->expects($this->once()) + ->method('updateTimestamp'); + + $proxy = new SessionHandlerProxy($mock); + $proxy->updateTimestamp('id', 'data'); + + $this->mock->expects($this->once()) + ->method('write'); + + $this->proxy->updateTimestamp('id', 'data'); + } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php index c2ded996fab4f..699222e37916e 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php @@ -123,4 +123,22 @@ public function testReturnThis() $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); } + + public function testSetNotModified() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $modified = $response->setNotModified(); + $this->assertObjectHasAttribute('headers', $modified); + $this->assertObjectHasAttribute('content', $modified); + $this->assertObjectHasAttribute('version', $modified); + $this->assertObjectHasAttribute('statusCode', $modified); + $this->assertObjectHasAttribute('statusText', $modified); + $this->assertObjectHasAttribute('charset', $modified); + $this->assertEquals(304, $modified->getStatusCode()); + + ob_start(); + $modified->sendContent(); + $string = ob_get_clean(); + $this->assertEmpty($string); + } } diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php index f7745ea3dc160..c25a0f1cf1a1a 100644 --- a/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php @@ -29,7 +29,7 @@ class GetResponseEvent extends KernelEvent /** * Returns the response object. * - * @return Response + * @return Response|null */ public function getResponse() { diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index 2636c65ff6951..bc02fe0a830f7 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -62,7 +62,7 @@ public function onKernelRequest(GetResponseEvent $event) } $session = $session ?? ($this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : null); - $this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : null; + $this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : 0; } public function onKernelResponse(FilterResponseEvent $event) diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php index f07647f8a67dc..51e92e2ce3773 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php @@ -73,6 +73,13 @@ public function onKernelResponse(FilterResponseEvent $event) if ($session instanceof Session ? !$session->isEmpty() || (null !== $this->sessionId && $session->getId() !== $this->sessionId) : $wasStarted) { $params = session_get_cookie_params(); + + foreach ($event->getResponse()->headers->getCookies() as $cookie) { + if ($session->getName() === $cookie->getName() && $params['path'] === $cookie->getPath() && $params['domain'] == $cookie->getDomain()) { + return; + } + } + $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/Fragment/HIncludeFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php index e737a141e93b7..d65d373ee30c7 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php @@ -126,7 +126,7 @@ private function templateExists(string $template): bool if ($this->templating instanceof EngineInterface) { try { return $this->templating->exists($template); - } catch (\InvalidArgumentException $e) { + } catch (\Exception $e) { return false; } } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 1d5d2227579d8..f536bd0b30399 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.1.1'; - const VERSION_ID = 40101; + const VERSION = '4.1.2'; + const VERSION_ID = 40102; const MAJOR_VERSION = 4; const MINOR_VERSION = 1; - const RELEASE_VERSION = 1; + const RELEASE_VERSION = 2; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2019'; diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php index 84ca04a680d61..2182a1d3d60c0 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php @@ -106,6 +106,36 @@ public function testEmptySessionWithNewSessionIdDoesSendCookie() $this->assertNotEmpty($response->headers->getCookies()); } + /** + * @dataProvider anotherCookieProvider + */ + public function testSessionWithNewSessionIdAndNewCookieDoesNotSendAnotherCookie($existing, array $expected) + { + $this->sessionHasBeenStarted(); + $this->sessionIsEmpty(); + $this->fixSessionId('456'); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('/', 'GET', array(), array('MOCKSESSID' => '123')); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + + $response = new Response('', 200, array('Set-Cookie' => $existing)); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + + $this->assertSame($expected, $response->headers->get('Set-Cookie', null, false)); + } + + public function anotherCookieProvider() + { + return array( + 'same' => array('MOCKSESSID=789; path=/', array('MOCKSESSID=789; path=/')), + 'different domain' => array('MOCKSESSID=789; path=/; domain=example.com', array('MOCKSESSID=789; path=/; domain=example.com', 'MOCKSESSID=456; path=/')), + 'different path' => array('MOCKSESSID=789; path=/foo', array('MOCKSESSID=789; path=/foo', 'MOCKSESSID=456; path=/')), + ); + } + public function testUnstartedSessionIsNotSave() { $this->sessionHasNotBeenStarted(); @@ -133,10 +163,10 @@ public function testDoesNotThrowIfRequestDoesNotHaveASession() $this->assertTrue(true); } - private function filterResponse(Request $request, $type = HttpKernelInterface::MASTER_REQUEST) + private function filterResponse(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, Response $response = null) { $request->setSession($this->session); - $response = new Response(); + $response = $response ?: new Response(); $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); $event = new FilterResponseEvent($kernel, $request, $type, $response); diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php index 1be052e5e62fd..7171c71bc3af4 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php @@ -86,4 +86,17 @@ public function testRenderWithDefaultText() $strategy = new HIncludeFragmentRenderer($engine); $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent()); } + + public function testRenderWithEngineAndDefaultText() + { + $engine = $this->getMockBuilder('Symfony\\Component\\Templating\\EngineInterface')->getMock(); + $engine->expects($this->once()) + ->method('exists') + ->with('loading...') + ->will($this->throwException(new \RuntimeException())); + + // only default + $strategy = new HIncludeFragmentRenderer($engine); + $this->assertEquals('loading...', $strategy->render('/foo', Request::create('/'), array('default' => 'loading...'))->getContent()); + } } diff --git a/src/Symfony/Component/Lock/Store/FlockStore.php b/src/Symfony/Component/Lock/Store/FlockStore.php index 1ef9ec8472ec7..5b2732d30ac6f 100644 --- a/src/Symfony/Component/Lock/Store/FlockStore.php +++ b/src/Symfony/Component/Lock/Store/FlockStore.php @@ -81,7 +81,7 @@ private function lock(Key $key, $blocking) set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); if (!$handle = fopen($fileName, 'r+') ?: fopen($fileName, 'r')) { if ($handle = fopen($fileName, 'x')) { - chmod($fileName, 0444); + chmod($fileName, 0666); } elseif (!$handle = fopen($fileName, 'r+') ?: fopen($fileName, 'r')) { usleep(100); // Give some time for chmod() to complete $handle = fopen($fileName, 'r+') ?: fopen($fileName, 'r'); diff --git a/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php index 97c93c786c971..6c265b98bdb42 100644 --- a/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php @@ -46,13 +46,22 @@ public function testResourceRemoval() private function getOpenedSemaphores() { - $lines = explode(PHP_EOL, trim(`ipcs -su`)); + if ('Darwin' === PHP_OS) { + $lines = explode(PHP_EOL, trim(`ipcs -s`)); + if (-1 === $start = array_search('Semaphores:', $lines)) { + throw new \Exception('Failed to extract list of opened semaphores. Expected a Semaphore list, got '.implode(PHP_EOL, $lines)); + } + + return \count(\array_slice($lines, ++$start)); + } + + $lines = explode(PHP_EOL, trim(`LC_ALL=C ipcs -su`)); if ('------ Semaphore Status --------' !== $lines[0]) { - throw new \Exception('Failed to extract list of opend semaphores. Expect a Semaphore status, got '.implode(PHP_EOL, $lines)); + throw new \Exception('Failed to extract list of opened semaphores. Expected a Semaphore status, got '.implode(PHP_EOL, $lines)); } list($key, $value) = explode(' = ', $lines[1]); if ('used arrays' !== $key) { - throw new \Exception('Failed to extract list of opend semaphores. Expect a used arrays key, got '.implode(PHP_EOL, $lines)); + throw new \Exception('Failed to extract list of opened semaphores. Expected a "used arrays" key, got '.implode(PHP_EOL, $lines)); } return (int) $value; diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 264f8bfdcdf5c..bd3c721b83752 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -128,11 +128,13 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) if ('__invoke' !== $method) { $wrapperDefinition = (new Definition('callable'))->addArgument(array(new Reference($serviceId), $method))->setFactory('Closure::fromCallable'); - $definitions[$serviceId = '.messenger.method_on_object_wrapper.'.ContainerBuilder::hash($messageClass.':'.$messagePriority.':'.$serviceId.':'.$method)] = $wrapperDefinition; + $definitions[$definitionId = '.messenger.method_on_object_wrapper.'.ContainerBuilder::hash($messageClass.':'.$messagePriority.':'.$serviceId.':'.$method)] = $wrapperDefinition; + } else { + $definitionId = $serviceId; } foreach ($handlerBuses as $handlerBus) { - $handlersByBusAndMessage[$handlerBus][$messageClass][$messagePriority][] = $serviceId; + $handlersByBusAndMessage[$handlerBus][$messageClass][$messagePriority][] = $definitionId; } } } diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index 618604f2396c9..4b2a0de1b9981 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -288,7 +288,12 @@ public function testItShouldNotThrowIfGeneratorIsReturnedInsteadOfArray() $handlerMapping = $handlerLocatorDefinition->getArgument(0); $this->assertArrayHasKey('handler.'.DummyMessage::class, $handlerMapping); + $firstReference = $handlerMapping['handler.'.DummyMessage::class]->getValues()[0]; + $this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'dummyMethod'), $container->getDefinition($firstReference)->getArgument(0)); + $this->assertArrayHasKey('handler.'.SecondMessage::class, $handlerMapping); + $secondReference = $handlerMapping['handler.'.SecondMessage::class]->getValues()[0]; + $this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'secondMessage'), $container->getDefinition($secondReference)->getArgument(0)); } /** diff --git a/src/Symfony/Component/Messenger/Transport/ChainSender.php b/src/Symfony/Component/Messenger/Transport/ChainSender.php index 97e3ce44bbaf1..2d2d55f03df76 100644 --- a/src/Symfony/Component/Messenger/Transport/ChainSender.php +++ b/src/Symfony/Component/Messenger/Transport/ChainSender.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Messenger\Transport; +use Symfony\Component\Messenger\Envelope; + /** * @author Tobias Schultze */ @@ -29,7 +31,7 @@ public function __construct(iterable $senders) /** * {@inheritdoc} */ - public function send($message): void + public function send(Envelope $message): void { foreach ($this->senders as $sender) { $sender->send($message); diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 68b4154b10da2..5f291f7cbff2b 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -433,7 +433,7 @@ public function setAllowedValues($option, $allowedValues) )); } - $this->allowedValues[$option] = is_array($allowedValues) ? $allowedValues : array($allowedValues); + $this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues); // Make sure the option is processed unset($this->resolved[$option]); @@ -785,14 +785,13 @@ public function offsetGet($option) } if (!$valid) { - throw new InvalidOptionsException(sprintf( - 'The option "%s" with value %s is expected to be of type '. - '"%s", but is of type "%s".', - $option, - $this->formatValue($value), - implode('" or "', $this->allowedTypes[$option]), - implode('|', array_keys($invalidTypes)) - )); + $keys = array_keys($invalidTypes); + + if (1 === \count($keys) && '[]' === substr($keys[0], -2)) { + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0])); + } + + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes)))); } } @@ -868,32 +867,10 @@ public function offsetGet($option) return $value; } - /** - * @param string $type - * @param mixed $value - * @param array &$invalidTypes - * - * @return bool - */ - private function verifyTypes($type, $value, array &$invalidTypes) + private function verifyTypes(string $type, $value, array &$invalidTypes): bool { - if ('[]' === substr($type, -2) && is_array($value)) { - $originalType = $type; - $type = substr($type, 0, -2); - $invalidValues = array_filter( // Filter out valid values, keeping invalid values in the resulting array - $value, - function ($value) use ($type) { - return !self::isValueValidType($type, $value); - } - ); - - if (!$invalidValues) { - return true; - } - - $invalidTypes[$this->formatTypeOf($value, $originalType)] = true; - - return false; + if (\is_array($value) && '[]' === substr($type, -2)) { + return $this->verifyArrayType($type, $value, $invalidTypes); } if (self::isValueValidType($type, $value)) { @@ -907,6 +884,43 @@ function ($value) use ($type) { return false; } + private function verifyArrayType(string $type, array $value, array &$invalidTypes, int $level = 0): bool + { + $type = substr($type, 0, -2); + + $suffix = '[]'; + while (\strlen($suffix) <= $level * 2) { + $suffix .= '[]'; + } + + if ('[]' === substr($type, -2)) { + $success = true; + foreach ($value as $item) { + if (!\is_array($item)) { + $invalidTypes[$this->formatTypeOf($item, null).$suffix] = true; + + return false; + } + + if (!$this->verifyArrayType($type, $item, $invalidTypes, $level + 1)) { + $success = false; + } + } + + return $success; + } + + foreach ($value as $item) { + if (!self::isValueValidType($type, $item)) { + $invalidTypes[$this->formatTypeOf($item, $type).$suffix] = $value; + + return false; + } + } + + return true; + } + /** * Returns whether a resolved option with the given name exists. * @@ -987,13 +1001,13 @@ private function formatTypeOf($value, ?string $type): string while ('[]' === substr($type, -2)) { $type = substr($type, 0, -2); $value = array_shift($value); - if (!is_array($value)) { + if (!\is_array($value)) { break; } $suffix .= '[]'; } - if (is_array($value)) { + if (\is_array($value)) { $subTypes = array(); foreach ($value as $val) { $subTypes[$this->formatTypeOf($val, null)] = true; @@ -1003,7 +1017,7 @@ private function formatTypeOf($value, ?string $type): string } } - return (is_object($value) ? get_class($value) : gettype($value)).$suffix; + return (\is_object($value) ? get_class($value) : gettype($value)).$suffix; } /** @@ -1017,19 +1031,19 @@ private function formatTypeOf($value, ?string $type): string */ private function formatValue($value): string { - if (is_object($value)) { + if (\is_object($value)) { return get_class($value); } - if (is_array($value)) { + if (\is_array($value)) { return 'array'; } - if (is_string($value)) { + if (\is_string($value)) { return '"'.$value.'"'; } - if (is_resource($value)) { + if (\is_resource($value)) { return 'resource'; } @@ -1065,8 +1079,21 @@ private function formatValues(array $values): string return implode(', ', $values); } - private static function isValueValidType($type, $value) + private static function isValueValidType(string $type, $value): bool { return (function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type; } + + private function getInvalidValues(array $arrayValues, string $type): array + { + $invalidValues = array(); + + foreach ($arrayValues as $key => $value) { + if (!self::isValueValidType($type, $value)) { + $invalidValues[$key] = $value; + } + } + + return $invalidValues; + } } diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 12dc77b3c6b1c..ac12984447604 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -483,7 +483,7 @@ public function testFailIfSetAllowedTypesFromLazyOption() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]". */ public function testResolveFailsIfInvalidTypedArray() { @@ -507,7 +507,7 @@ public function testResolveFailsWithNonArray() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]". */ public function testResolveFailsIfTypedArrayContainsInvalidTypes() { @@ -524,7 +524,7 @@ public function testResolveFailsIfTypedArrayContainsInvalidTypes() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]". */ public function testResolveFailsWithCorrectLevelsButWrongScalar() { @@ -1586,4 +1586,151 @@ public function testCountFailsOutsideResolve() count($this->resolver); } + + public function testNestedArrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + 1, 2, + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array(1, 2), + ), + ) + )); + } + + public function testNested2Arrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][][][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + array( + array( + 1, 2, + ), + ), + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]". + */ + public function testNestedArraysException() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'float[][][][]'); + + $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + ); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException1() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(1, true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException2() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]". + */ + public function testNestedArrayException3() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array('str', array(1, 2)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]". + */ + public function testNestedArrayException4() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]". + */ + public function testNestedArrayException5() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); + } } diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index f11569f162f66..f6f3ba06d1bf2 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -161,25 +161,21 @@ private function getDocBlock(string $class, string $property): array $ucFirstProperty = ucfirst($property); - try { - switch (true) { - case $docBlock = $this->getDocBlockFromProperty($class, $property): - $data = array($docBlock, self::PROPERTY, null); - break; + switch (true) { + case $docBlock = $this->getDocBlockFromProperty($class, $property): + $data = array($docBlock, self::PROPERTY, null); + break; - case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR): - $data = array($docBlock, self::ACCESSOR, null); - break; + case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR): + $data = array($docBlock, self::ACCESSOR, null); + break; - case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR): - $data = array($docBlock, self::MUTATOR, $prefix); - break; + case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR): + $data = array($docBlock, self::MUTATOR, $prefix); + break; - default: - $data = array(null, null, null); - } - } catch (\InvalidArgumentException $e) { - $data = array(null, null, null); + default: + $data = array(null, null, null); } return $this->docBlocks[$propertyHash] = $data; @@ -194,7 +190,11 @@ private function getDocBlockFromProperty(string $class, string $property): ?DocB return null; } - return $this->docBlockFactory->create($reflectionProperty, $this->contextFactory->createFromReflector($reflectionProperty->getDeclaringClass())); + try { + return $this->docBlockFactory->create($reflectionProperty, $this->contextFactory->createFromReflector($reflectionProperty->getDeclaringClass())); + } catch (\InvalidArgumentException $e) { + return null; + } } private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array @@ -226,6 +226,10 @@ private function getDocBlockFromMethod(string $class, string $ucFirstProperty, i return null; } - return array($this->docBlockFactory->create($reflectionMethod, $this->contextFactory->createFromReflector($reflectionMethod)), $prefix); + try { + return array($this->docBlockFactory->create($reflectionMethod, $this->contextFactory->createFromReflector($reflectionMethod)), $prefix); + } catch (\InvalidArgumentException $e) { + return null; + } } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index af22aa9c918b7..080df2893a877 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -40,6 +40,31 @@ public function testExtract($property, array $type = null, $shortDescription, $l $this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); } + public function testParamTagTypeIsOmitted() + { + $this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType')); + } + + /** + * @dataProvider typesWithCustomPrefixesProvider + */ + public function testExtractTypesWithCustomPrefixes($property, array $type = null) + { + $customExtractor = new PhpDocExtractor(null, array('add', 'remove'), array('is', 'can')); + + $this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); + } + + /** + * @dataProvider typesWithNoPrefixesProvider + */ + public function testExtractTypesWithNoPrefixes($property, array $type = null) + { + $noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array()); + + $this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); + } + public function typesProvider() { return array( @@ -68,8 +93,10 @@ public function typesProvider() array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null), array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null), array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), - array('g', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null), - array('array', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), + array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), + array('h', array(new Type(Type::BUILTIN_TYPE_STRING, true)), null, null), + array('i', array(new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)), null, null), + array('j', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTime')), null, null), array('donotexist', null, null, null), array('staticGetter', null, null, null), array('staticSetter', null, null, null), @@ -77,21 +104,6 @@ public function typesProvider() ); } - public function testParamTagTypeIsOmitted() - { - $this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType')); - } - - /** - * @dataProvider typesWithCustomPrefixesProvider - */ - public function testExtractTypesWithCustomPrefixes($property, array $type = null) - { - $customExtractor = new PhpDocExtractor(null, array('add', 'remove'), array('is', 'can')); - - $this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); - } - public function typesWithCustomPrefixesProvider() { return array( @@ -120,23 +132,16 @@ public function typesWithCustomPrefixesProvider() array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null), array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null), array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), - array('g', null), + array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), + array('h', array(new Type(Type::BUILTIN_TYPE_STRING, true)), null, null), + array('i', array(new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)), null, null), + array('j', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTime')), null, null), array('donotexist', null, null, null), array('staticGetter', null, null, null), array('staticSetter', null, null, null), ); } - /** - * @dataProvider typesWithNoPrefixesProvider - */ - public function testExtractTypesWithNoPrefixes($property, array $type = null) - { - $noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array()); - - $this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); - } - public function typesWithNoPrefixesProvider() { return array( @@ -165,7 +170,10 @@ public function typesWithNoPrefixesProvider() array('d', null, null, null), array('e', null, null, null), array('f', null, null, null), - array('g', null), + array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null), + array('h', array(new Type(Type::BUILTIN_TYPE_STRING, true)), null, null), + array('i', array(new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)), null, null), + array('j', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTime')), null, null), array('donotexist', null, null, null), array('staticGetter', null, null, null), array('staticSetter', null, null, null), @@ -176,6 +184,29 @@ public function testReturnNullOnEmptyDocBlock() { $this->assertNull($this->extractor->getShortDescription(EmptyDocBlock::class, 'foo')); } + + public function dockBlockFallbackTypesProvider() + { + return array( + 'pub' => array( + 'pub', array(new Type(Type::BUILTIN_TYPE_STRING)), + ), + 'protAcc' => array( + 'protAcc', array(new Type(Type::BUILTIN_TYPE_INT)), + ), + 'protMut' => array( + 'protMut', array(new Type(Type::BUILTIN_TYPE_BOOL)), + ), + ); + } + + /** + * @dataProvider dockBlockFallbackTypesProvider + */ + public function testDocBlockFallback($property, $types) + { + $this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DockBlockFallback', $property)); + } } class EmptyDocBlock diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index f5b47196ee5f4..4d188cb4bf265 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -40,7 +40,10 @@ public function testGetProperties() 'collection', 'B', 'Guid', - 'array', + 'g', + 'h', + 'i', + 'j', 'emptyVar', 'foo', 'foo2', @@ -58,7 +61,6 @@ public function testGetProperties() 'd', 'e', 'f', - 'g', ), $this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy') ); @@ -77,7 +79,10 @@ public function testGetPropertiesWithCustomPrefixes() 'collection', 'B', 'Guid', - 'array', + 'g', + 'h', + 'i', + 'j', 'emptyVar', 'foo', 'foo2', @@ -105,7 +110,10 @@ public function testGetPropertiesWithNoPrefixes() 'collection', 'B', 'Guid', - 'array', + 'g', + 'h', + 'i', + 'j', 'emptyVar', 'foo', 'foo2', @@ -118,24 +126,6 @@ public function testGetPropertiesWithNoPrefixes() ); } - public function testGetPropertiesPhp71() - { - $noPrefixExtractor = new ReflectionExtractor(); - - $this->assertSame( - array( - 'string', - 'stringOrNull', - 'foo', - 'buz', - 'bar', - 'baz', - 'intWithAccessor', - ), - $noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy') - ); - } - /** * @dataProvider typesProvider */ @@ -197,22 +187,9 @@ public function php71TypesProvider() array('bar', array(new Type(Type::BUILTIN_TYPE_INT, true))), array('baz', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))), array('donotexist', null), - array('string', array(new Type(Type::BUILTIN_TYPE_STRING, false))), - array('stringOrNull', array(new Type(Type::BUILTIN_TYPE_STRING, true))), - array('intPrivate', array(new Type(Type::BUILTIN_TYPE_INT, false))), - array('intWithAccessor', array(new Type(Type::BUILTIN_TYPE_INT, false))), ); } - public function testExtractPhp71TypeWithParentConstructor() - { - $property = 'string'; - $type = array(new Type(Type::BUILTIN_TYPE_STRING, false)); - $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild', $property, array())); - $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild2', $property, array())); - $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild3', $property, array())); - } - /** * @dataProvider getReadableProperties */ @@ -236,12 +213,10 @@ public function getReadableProperties() array('d', true), array('e', false), array('f', false), - array('g', true), array('Id', true), array('id', true), array('Guid', true), array('guid', false), - array('guid', false), ); } @@ -268,7 +243,6 @@ public function getWritableProperties() array('d', false), array('e', true), array('f', true), - array('g', false), array('Id', false), array('Guid', true), array('guid', false), diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DockBlockFallback.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DockBlockFallback.php new file mode 100644 index 0000000000000..3f9c303b59137 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DockBlockFallback.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo\Tests\Fixtures; + +/** + * PhpDocExtractor should fallback from property -> accessor -> mutator when looking up dockblocks. + * + * @author Martin Rademacher + */ +class DockBlockFallback +{ + /** @var string $pub */ + public $pub = 'pub'; + + protected $protAcc; + protected $protMut; + + public function getPub() + { + return $this->pub; + } + + public function setPub($pub) + { + $this->pub = $pub; + } + + /** + * @return int + */ + public function getProtAcc() + { + return $this->protAcc; + } + + public function setProt($protAcc) + { + $this->protAcc = $protAcc; + } + + public function getProtMut() + { + return $this->protMut; + } + + /** + * @param bool $protMut + */ + public function setProtMut($protMut) + { + $this->protMut = $protMut; + } +} diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index afe09ef929dfe..76c2e1042c604 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -66,7 +66,22 @@ class Dummy extends ParentDummy * * @var array|null */ - public $array; + public $g; + + /** + * @var ?string + */ + public $h; + + /** + * @var ?string|int + */ + public $i; + + /** + * @var ?\DateTime + */ + public $j; /** * This should not be removed. diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php index b3f6d779077d3..8d5c4fe107162 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php @@ -75,11 +75,4 @@ public function addE($e) public function removeF(\DateTime $f) { } - - /** - * @return bool|null - */ - public function hasG() - { - } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php index 67d68eb9ccb81..e72d376c492fa 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php @@ -16,22 +16,6 @@ */ class Php71Dummy { - public $string; - - public $stringOrNull; - - private $intPrivate; - - private $intWithAccessor; - - public function __construct(string $string, ?string $stringOrNull, int $intPrivate, int $intWithAccessor) - { - $this->string = $string; - $this->stringOrNull = $stringOrNull; - $this->intPrivate = $intPrivate; - $this->intWithAccessor = $intWithAccessor; - } - public function getFoo(): ?array { } @@ -47,9 +31,4 @@ public function setBar(?int $bar) public function addBaz(string $baz) { } - - public function getIntWithAccessor() - { - return $this->intWithAccessor; - } } diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index 939fb7c195932..7a1a9e03c9181 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -14,6 +14,7 @@ use phpDocumentor\Reflection\Type as DocType; use phpDocumentor\Reflection\Types\Compound; use phpDocumentor\Reflection\Types\Null_; +use phpDocumentor\Reflection\Types\Nullable; use Symfony\Component\PropertyInfo\Type; /** @@ -34,6 +35,11 @@ public function getTypes(DocType $varType): array $types = array(); $nullable = false; + if ($varType instanceof Nullable) { + $nullable = true; + $varType = $varType->getActualType(); + } + if (!$varType instanceof Compound) { if ($varType instanceof Null_) { $nullable = true; @@ -54,10 +60,10 @@ public function getTypes(DocType $varType): array // If null is present, all types are nullable $nullKey = array_search(Type::BUILTIN_TYPE_NULL, $varTypes); - $nullable = false !== $nullKey; + $nullable = $nullable || false !== $nullKey; // Remove the null type from the type if other types are defined - if ($nullable && count($varTypes) > 1) { + if ($nullable && false !== $nullKey && count($varTypes) > 1) { unset($varTypes[$nullKey]); } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index 0350fb411f315..88f177d00491f 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -35,7 +35,7 @@ }, "conflict": { "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", - "phpdocumentor/type-resolver": "<0.2.1", + "phpdocumentor/type-resolver": "<0.3.0", "symfony/dependency-injection": "<3.4" }, "suggest": { diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index adb2b0a1c0148..c5af8c52a3679 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -379,7 +379,7 @@ private function compileDynamicRoutes(RouteCollection $collection, bool $matchHo $hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')\.'; $state->hostVars = $state->vars; } else { - $hostRegex = '(?:(?:[^.]*+\.)++)'; + $hostRegex = '(?:(?:[^./]*+\.)++)'; $state->hostVars = array(); } $state->mark += strlen($rx = ($prev ? ')' : '')."|{$hostRegex}(?"); @@ -746,6 +746,10 @@ public static function export($value): string return 'null'; } if (!\is_array($value)) { + if (\is_object($value)) { + throw new \InvalidArgumentException('Symfony\Component\Routing\Route cannot contain objects.'); + } + return str_replace("\n", '\'."\n".\'', var_export($value, true)); } if (!$value) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php index b39bc7f2bae0a..c91066a46d281 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php @@ -82,47 +82,47 @@ public function match($rawPathinfo) $matchedPathinfo = $host.'.'.$pathinfo; $regexList = array( 0 => '{^(?' - .'|(?:(?:[^.]*+\\.)++)(?' - .'|/foo/(baz|symfony)(*:46)' + .'|(?:(?:[^./]*+\\.)++)(?' + .'|/foo/(baz|symfony)(*:47)' .'|/bar(?' - .'|/([^/]++)(*:69)' - .'|head/([^/]++)(*:89)' + .'|/([^/]++)(*:70)' + .'|head/([^/]++)(*:90)' .')' .'|/test/([^/]++)/(?' - .'|(*:115)' + .'|(*:116)' .')' - .'|/([\']+)(*:131)' + .'|/([\']+)(*:132)' .'|/a/(?' .'|b\'b/([^/]++)(?' - .'|(*:160)' - .'|(*:168)' + .'|(*:161)' + .'|(*:169)' .')' - .'|(.*)(*:181)' + .'|(.*)(*:182)' .'|b\'b/([^/]++)(?' - .'|(*:204)' - .'|(*:212)' + .'|(*:205)' + .'|(*:213)' .')' .')' - .'|/multi/hello(?:/([^/]++))?(*:248)' + .'|/multi/hello(?:/([^/]++))?(*:249)' .'|/([^/]++)/b/([^/]++)(?' - .'|(*:279)' - .'|(*:287)' + .'|(*:280)' + .'|(*:288)' .')' - .'|/aba/([^/]++)(*:309)' + .'|/aba/([^/]++)(*:310)' .')|(?i:([^\\.]++)\\.example\\.com)\\.(?' .'|/route1(?' - .'|3/([^/]++)(*:371)' - .'|4/([^/]++)(*:389)' + .'|3/([^/]++)(*:372)' + .'|4/([^/]++)(*:390)' .')' .')|(?i:c\\.example\\.com)\\.(?' - .'|/route15/([^/]++)(*:441)' - .')|(?:(?:[^.]*+\\.)++)(?' - .'|/route16/([^/]++)(*:488)' + .'|/route15/([^/]++)(*:442)' + .')|(?:(?:[^./]*+\\.)++)(?' + .'|/route16/([^/]++)(*:490)' .'|/a/(?' - .'|a\\.\\.\\.(*:509)' + .'|a\\.\\.\\.(*:511)' .'|b/(?' - .'|([^/]++)(*:530)' - .'|c/([^/]++)(*:548)' + .'|([^/]++)(*:532)' + .'|c/([^/]++)(*:550)' .')' .')' .')' @@ -132,7 +132,7 @@ public function match($rawPathinfo) foreach ($regexList as $offset => $regex) { while (preg_match($regex, $matchedPathinfo, $matches)) { switch ($m = (int) $matches['MARK']) { - case 115: + case 116: $matches = array('foo' => $matches[1] ?? null); // baz4 @@ -159,7 +159,7 @@ public function match($rawPathinfo) not_bazbaz6: break; - case 160: + case 161: $matches = array('foo' => $matches[1] ?? null); // foo1 @@ -173,14 +173,14 @@ public function match($rawPathinfo) not_foo1: break; - case 204: + case 205: $matches = array('foo1' => $matches[1] ?? null); // foo2 return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array()); break; - case 279: + case 280: $matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null); // foo3 @@ -189,23 +189,23 @@ public function match($rawPathinfo) break; default: $routes = array( - 46 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), - 69 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), - 89 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), - 131 => array(array('_route' => 'quoter'), array('quoter'), null, null), - 168 => array(array('_route' => 'bar1'), array('bar'), null, null), - 181 => array(array('_route' => 'overridden'), array('var'), null, null), - 212 => array(array('_route' => 'bar2'), array('bar1'), null, null), - 248 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), - 287 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), - 309 => array(array('_route' => 'foo4'), array('foo'), null, null), - 371 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), - 389 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), - 441 => array(array('_route' => 'route15'), array('name'), null, null), - 488 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), - 509 => array(array('_route' => 'a'), array(), null, null), - 530 => array(array('_route' => 'b'), array('var'), null, null), - 548 => array(array('_route' => 'c'), array('var'), null, null), + 47 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), + 70 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), + 90 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), + 132 => array(array('_route' => 'quoter'), array('quoter'), null, null), + 169 => array(array('_route' => 'bar1'), array('bar'), null, null), + 182 => array(array('_route' => 'overridden'), array('var'), null, null), + 213 => array(array('_route' => 'bar2'), array('bar1'), null, null), + 249 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), + 288 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), + 310 => array(array('_route' => 'foo4'), array('foo'), null, null), + 372 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), + 390 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), + 442 => array(array('_route' => 'route15'), array('name'), null, null), + 490 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), + 511 => array(array('_route' => 'a'), array(), null, null), + 532 => array(array('_route' => 'b'), array('var'), null, null), + 550 => array(array('_route' => 'c'), array('var'), null, null), ); list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; @@ -231,7 +231,7 @@ public function match($rawPathinfo) return $ret; } - if (548 === $m) { + if (550 === $m) { break; } $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php index 79b650eb5b26d..a711dbaf61263 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -119,47 +119,47 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a $matchedPathinfo = $host.'.'.$pathinfo; $regexList = array( 0 => '{^(?' - .'|(?:(?:[^.]*+\\.)++)(?' - .'|/foo/(baz|symfony)(*:46)' + .'|(?:(?:[^./]*+\\.)++)(?' + .'|/foo/(baz|symfony)(*:47)' .'|/bar(?' - .'|/([^/]++)(*:69)' - .'|head/([^/]++)(*:89)' + .'|/([^/]++)(*:70)' + .'|head/([^/]++)(*:90)' .')' .'|/test/([^/]++)/(?' - .'|(*:115)' + .'|(*:116)' .')' - .'|/([\']+)(*:131)' + .'|/([\']+)(*:132)' .'|/a/(?' .'|b\'b/([^/]++)(?' - .'|(*:160)' - .'|(*:168)' + .'|(*:161)' + .'|(*:169)' .')' - .'|(.*)(*:181)' + .'|(.*)(*:182)' .'|b\'b/([^/]++)(?' - .'|(*:204)' - .'|(*:212)' + .'|(*:205)' + .'|(*:213)' .')' .')' - .'|/multi/hello(?:/([^/]++))?(*:248)' + .'|/multi/hello(?:/([^/]++))?(*:249)' .'|/([^/]++)/b/([^/]++)(?' - .'|(*:279)' - .'|(*:287)' + .'|(*:280)' + .'|(*:288)' .')' - .'|/aba/([^/]++)(*:309)' + .'|/aba/([^/]++)(*:310)' .')|(?i:([^\\.]++)\\.example\\.com)\\.(?' .'|/route1(?' - .'|3/([^/]++)(*:371)' - .'|4/([^/]++)(*:389)' + .'|3/([^/]++)(*:372)' + .'|4/([^/]++)(*:390)' .')' .')|(?i:c\\.example\\.com)\\.(?' - .'|/route15/([^/]++)(*:441)' - .')|(?:(?:[^.]*+\\.)++)(?' - .'|/route16/([^/]++)(*:488)' + .'|/route15/([^/]++)(*:442)' + .')|(?:(?:[^./]*+\\.)++)(?' + .'|/route16/([^/]++)(*:490)' .'|/a/(?' - .'|a\\.\\.\\.(*:509)' + .'|a\\.\\.\\.(*:511)' .'|b/(?' - .'|([^/]++)(*:530)' - .'|c/([^/]++)(*:548)' + .'|([^/]++)(*:532)' + .'|c/([^/]++)(*:550)' .')' .')' .')' @@ -169,7 +169,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a foreach ($regexList as $offset => $regex) { while (preg_match($regex, $matchedPathinfo, $matches)) { switch ($m = (int) $matches['MARK']) { - case 115: + case 116: $matches = array('foo' => $matches[1] ?? null); // baz4 @@ -196,7 +196,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a not_bazbaz6: break; - case 160: + case 161: $matches = array('foo' => $matches[1] ?? null); // foo1 @@ -210,14 +210,14 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a not_foo1: break; - case 204: + case 205: $matches = array('foo1' => $matches[1] ?? null); // foo2 return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array()); break; - case 279: + case 280: $matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null); // foo3 @@ -226,23 +226,23 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a break; default: $routes = array( - 46 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), - 69 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), - 89 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), - 131 => array(array('_route' => 'quoter'), array('quoter'), null, null), - 168 => array(array('_route' => 'bar1'), array('bar'), null, null), - 181 => array(array('_route' => 'overridden'), array('var'), null, null), - 212 => array(array('_route' => 'bar2'), array('bar1'), null, null), - 248 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), - 287 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), - 309 => array(array('_route' => 'foo4'), array('foo'), null, null), - 371 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), - 389 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), - 441 => array(array('_route' => 'route15'), array('name'), null, null), - 488 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), - 509 => array(array('_route' => 'a'), array(), null, null), - 530 => array(array('_route' => 'b'), array('var'), null, null), - 548 => array(array('_route' => 'c'), array('var'), null, null), + 47 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), + 70 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), + 90 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), + 132 => array(array('_route' => 'quoter'), array('quoter'), null, null), + 169 => array(array('_route' => 'bar1'), array('bar'), null, null), + 182 => array(array('_route' => 'overridden'), array('var'), null, null), + 213 => array(array('_route' => 'bar2'), array('bar1'), null, null), + 249 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), + 288 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), + 310 => array(array('_route' => 'foo4'), array('foo'), null, null), + 372 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), + 390 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), + 442 => array(array('_route' => 'route15'), array('name'), null, null), + 490 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), + 511 => array(array('_route' => 'a'), array(), null, null), + 532 => array(array('_route' => 'b'), array('var'), null, null), + 550 => array(array('_route' => 'c'), array('var'), null, null), ); list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; @@ -268,7 +268,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a return $ret; } - if (548 === $m) { + if (550 === $m) { break; } $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php index 4bbfe131a7bf9..a1c28ecab4d67 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php @@ -491,6 +491,18 @@ private function generateDumpedMatcher(RouteCollection $collection, $redirectabl return $this->matcherClass; } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Symfony\Component\Routing\Route cannot contain objects + */ + public function testGenerateDumperMatcherWithObject() + { + $routeCollection = new RouteCollection(); + $routeCollection->add('_', new Route('/', array(new \stdClass()))); + $dumper = new PhpMatcherDumper($routeCollection); + $dumper->dump(); + } } abstract class RedirectableUrlMatcherStub extends UrlMatcher implements RedirectableUrlMatcherInterface diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index f0013d7bc3d10..5a34535eb6cc2 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -670,6 +670,16 @@ public function testUtf8AndMethodMatching() $this->assertEquals('c', $matcher->match('/admin/api/package.json')['_route']); } + public function testHostWithDot() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/foo', array(), array(), array(), 'foo.example.com')); + $coll->add('b', new Route('/bar/{baz}')); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals('b', $matcher->match('/bar/abc.123')['_route']); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext()); diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php index 5648dd730cdbe..491c62bcc491f 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php @@ -76,14 +76,7 @@ public function getUser() } /** - * Sets the user in the token. - * - * The user can be a UserInterface instance, or an object implementing - * a __toString method or the username as a regular string. - * - * @param string|object $user The user - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function setUser($user) { @@ -261,7 +254,7 @@ private function hasUserChanged(UserInterface $user) } if ($this->user instanceof AdvancedUserInterface && $user instanceof AdvancedUserInterface) { - @trigger_error(sprintf('Checking for the AdvancedUserInterface in %s has been deprecated in 4.1. Implement the %s to check if the user has been changed,', __METHOD__, EquatableInterface::class), E_USER_DEPRECATED); + @trigger_error(sprintf('Checking for the AdvancedUserInterface in "%s()" is deprecated since Symfony 4.1 and support for it will be removed in 5.0. Implement the %s to check if the user has been changed,', __METHOD__, EquatableInterface::class), E_USER_DEPRECATED); if ($this->user->isAccountNonExpired() !== $user->isAccountNonExpired()) { return true; } @@ -278,7 +271,7 @@ private function hasUserChanged(UserInterface $user) return true; } } elseif ($this->user instanceof AdvancedUserInterface xor $user instanceof AdvancedUserInterface) { - @trigger_error(sprintf('Checking for the AdvancedUserInterface in %s has been deprecated in 4.1. Implement the %s to check if the user has been changed,', __METHOD__, EquatableInterface::class), E_USER_DEPRECATED); + @trigger_error(sprintf('Checking for the AdvancedUserInterface in "%s()" is deprecated since Symfony 4.1 and support for it will be removed in 5.0. Implement the %s to check if the user has been changed,', __METHOD__, EquatableInterface::class), E_USER_DEPRECATED); return true; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php b/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php index bb5711ee89107..bcf6fbdd94621 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php @@ -47,17 +47,22 @@ public function getCredentials(); /** * Returns a user representation. * - * @return mixed Can be a UserInterface instance, an object implementing a __toString method, - * or the username as a regular string + * @return string|object Can be a UserInterface instance, an object implementing a __toString method, + * or the username as a regular string * * @see AbstractToken::setUser() */ public function getUser(); /** - * Sets a user. + * Sets the user in the token. * - * @param mixed $user + * The user can be a UserInterface instance, or an object implementing + * a __toString method or the username as a regular string. + * + * @param string|object $user The user + * + * @throws \InvalidArgumentException */ public function setUser($user); diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php index cbee938667789..187430884625a 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php @@ -42,7 +42,7 @@ public function __construct(ExpressionLanguage $expressionLanguage, Authenticati */ public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1, register the provider directly on the injected ExpressionLanguage instance instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, register the provider directly on the injected ExpressionLanguage instance instead.', __METHOD__), E_USER_DEPRECATED); $this->expressionLanguage->registerProvider($provider); } diff --git a/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php b/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php index 541c94ce6525b..1d1d98ed123ad 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/UserCheckerTest.php @@ -32,7 +32,7 @@ public function testCheckPostAuthPass() /** * @group legacy - * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPostAuth with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. + * @expectedDeprecation Calling "Symfony\Component\Security\Core\User\UserChecker::checkPostAuth()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. */ public function testCheckPostAuthPassAdvancedUser() { @@ -55,7 +55,7 @@ public function testCheckPostAuthCredentialsExpired() /** * @group legacy - * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPostAuth with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. + * @expectedDeprecation Calling "Symfony\Component\Security\Core\User\UserChecker::checkPostAuth()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. * @expectedException \Symfony\Component\Security\Core\Exception\CredentialsExpiredException */ public function testCheckPostAuthCredentialsExpiredAdvancedUser() @@ -70,7 +70,7 @@ public function testCheckPostAuthCredentialsExpiredAdvancedUser() /** * @group legacy - * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. + * @expectedDeprecation Calling "Symfony\Component\Security\Core\User\UserChecker::checkPreAuth()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. */ public function testCheckPreAuthPassAdvancedUser() { @@ -95,7 +95,7 @@ public function testCheckPreAuthAccountLocked() /** * @group legacy - * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. + * @expectedDeprecation Calling "Symfony\Component\Security\Core\User\UserChecker::checkPreAuth()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. * @expectedException \Symfony\Component\Security\Core\Exception\LockedException */ public function testCheckPreAuthAccountLockedAdvancedUser() @@ -119,7 +119,7 @@ public function testCheckPreAuthDisabled() /** * @group legacy - * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. + * @expectedDeprecation Calling "Symfony\Component\Security\Core\User\UserChecker::checkPreAuth()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. * @expectedException \Symfony\Component\Security\Core\Exception\DisabledException */ public function testCheckPreAuthDisabledAdvancedUser() @@ -144,7 +144,7 @@ public function testCheckPreAuthAccountExpired() /** * @group legacy - * @expectedDeprecation Calling Symfony\Component\Security\Core\User\UserChecker::checkPreAuth with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. + * @expectedDeprecation Calling "Symfony\Component\Security\Core\User\UserChecker::checkPreAuth()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality. * @expectedException \Symfony\Component\Security\Core\Exception\AccountExpiredException */ public function testCheckPreAuthAccountExpiredAdvancedUser() diff --git a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php index b3fa3fa6b94f8..5f47fe8923b99 100644 --- a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php @@ -35,8 +35,12 @@ class LdapUserProvider implements UserProviderInterface private $defaultSearch; private $passwordAttribute; - public function __construct(LdapInterface $ldap, string $baseDn, string $searchDn = null, string $searchPassword = null, array $defaultRoles = array(), string $uidKey = 'sAMAccountName', string $filter = '({uid_key}={username})', string $passwordAttribute = null) + public function __construct(LdapInterface $ldap, string $baseDn, string $searchDn = null, string $searchPassword = null, array $defaultRoles = array(), ?string $uidKey = 'sAMAccountName', string $filter = '({uid_key}={username})', string $passwordAttribute = null) { + if (null === $uidKey) { + $uidKey = 'sAMAccountName'; + } + $this->ldap = $ldap; $this->baseDn = $baseDn; $this->searchDn = $searchDn; diff --git a/src/Symfony/Component/Security/Core/User/UserChecker.php b/src/Symfony/Component/Security/Core/User/UserChecker.php index a4aa09031bc7a..37d567842519b 100644 --- a/src/Symfony/Component/Security/Core/User/UserChecker.php +++ b/src/Symfony/Component/Security/Core/User/UserChecker.php @@ -33,7 +33,7 @@ public function checkPreAuth(UserInterface $user) } if ($user instanceof AdvancedUserInterface && !$user instanceof User) { - @trigger_error(sprintf('Calling %s with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('Calling "%s()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality.', __METHOD__), E_USER_DEPRECATED); } if (!$user->isAccountNonLocked()) { @@ -65,7 +65,7 @@ public function checkPostAuth(UserInterface $user) } if ($user instanceof AdvancedUserInterface && !$user instanceof User) { - @trigger_error(sprintf('Calling %s with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('Calling "%s()" with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality.', __METHOD__), E_USER_DEPRECATED); } if (!$user->isCredentialsNonExpired()) { diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 4b0d41f17fb95..2fb29bf58934f 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -70,7 +70,7 @@ public function __construct(TokenStorageInterface $tokenStorage, iterable $userP */ public function setLogoutOnUserChange($logoutOnUserChange) { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); } /** diff --git a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php index e03fcc0d7ff63..6d31fb36009b8 100644 --- a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php @@ -28,6 +28,7 @@ class CsvEncoder implements EncoderInterface, DecoderInterface const KEY_SEPARATOR_KEY = 'csv_key_separator'; const HEADERS_KEY = 'csv_headers'; const ESCAPE_FORMULAS_KEY = 'csv_escape_formulas'; + const AS_COLLECTION_KEY = 'as_collection'; private $delimiter; private $enclosure; @@ -157,7 +158,7 @@ public function decode($data, $format, array $context = array()) } fclose($handle); - if ($context['as_collection'] ?? false) { + if ($context[self::AS_COLLECTION_KEY] ?? false) { return $result; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 7f8d341846f7d..08e5841060dbd 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -294,7 +294,7 @@ abstract protected function setAttributeValue($object, $attribute, $value, $form */ private function validateAndDenormalize(string $currentClass, string $attribute, $data, ?string $format, array $context) { - if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($currentClass, $attribute)) { + if (null === $types = $this->getTypes($currentClass, $attribute)) { return $data; } @@ -357,6 +357,36 @@ private function validateAndDenormalize(string $currentClass, string $attribute, throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), gettype($data))); } + /** + * @return Type[]|null + */ + private function getTypes(string $currentClass, string $attribute) + { + if (null === $this->propertyTypeExtractor) { + return null; + } + + if (null !== $types = $this->propertyTypeExtractor->getTypes($currentClass, $attribute)) { + return $types; + } + + if (null !== $this->classDiscriminatorResolver && null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($currentClass)) { + if ($discriminatorMapping->getTypeProperty() === $attribute) { + return array( + new Type(Type::BUILTIN_TYPE_STRING), + ); + } + + foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) { + if (null !== $types = $this->propertyTypeExtractor->getTypes($mappedClass, $attribute)) { + return $types; + } + } + } + + return null; + } + /** * Sets an attribute and apply the name converter if necessary. * diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index bf463fe5457d3..77c98377ee70a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -16,6 +16,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Serializer\Exception\RuntimeException; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -131,4 +132,29 @@ protected function setAttributeValue($object, $attribute, $value, $format = null // Properties not found are ignored } } + + /** + * {@inheritdoc} + */ + protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false) + { + if (false === $allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)) { + return false; + } + + if (null !== $this->classDiscriminatorResolver) { + $class = \is_object($classOrObject) ? \get_class($classOrObject) : $classOrObject; + if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForMappedObject($classOrObject)) { + $allowedAttributes[] = $attributesAsString ? $discriminatorMapping->getTypeProperty() : new AttributeMetadata($discriminatorMapping->getTypeProperty()); + } + + if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($class)) { + foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) { + $allowedAttributes = array_merge($allowedAttributes, parent::getAllowedAttributes($mappedClass, $context, $attributesAsString)); + } + } + } + + return $allowedAttributes; + } } diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php index d990d51ec75b3..4a0bc8169b03b 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php @@ -324,7 +324,7 @@ public function testDecodeOnlyOneAsCollection() a CSV - , 'csv', array('as_collection' => true))); + , 'csv', array(CsvEncoder::AS_COLLECTION_KEY => true))); } public function testDecodeToManyRelation() diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageInterface.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageInterface.php index f0b4c4d128c38..55bb00bc8e253 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageInterface.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageInterface.php @@ -15,8 +15,8 @@ /** * @DiscriminatorMap(typeProperty="type", mapping={ - * "first"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild", - * "second"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild" + * "one"="Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne", + * "two"="Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo" * }) * * @author Samuel Roze diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberOne.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberOne.php index 381f7f8a6c70b..200476b54232d 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberOne.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberOne.php @@ -11,10 +11,17 @@ namespace Symfony\Component\Serializer\Tests\Fixtures; +use Symfony\Component\Serializer\Annotation\Groups; + /** * @author Samuel Roze */ class DummyMessageNumberOne implements DummyMessageInterface { public $one; + + /** + * @Groups({"two"}) + */ + public $two; } diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberTwo.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberTwo.php new file mode 100644 index 0000000000000..5a24e7c9ff08e --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyMessageNumberTwo.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Annotation\Groups; + +/** + * @author Samuel Roze + */ +class DummyMessageNumberTwo implements DummyMessageInterface +{ + /** + * @Groups({"two"}) + */ + public $three; + + /** + * @var DummyMessageNumberOne + */ + private $nested; + + public function setNested(DummyMessageNumberOne $nested) + { + $this->nested = $nested; + } + + public function getNested(): DummyMessageNumberOne + { + return $this->nested; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 7550b3c9b7ba1..743461a727a01 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -11,11 +11,15 @@ namespace Symfony\Component\Serializer\Tests; +use Doctrine\Common\Annotations\AnnotationReader; use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -32,6 +36,7 @@ use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild; use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface; use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne; +use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo; use Symfony\Component\Serializer\Tests\Fixtures\TraversableDummy; use Symfony\Component\Serializer\Tests\Fixtures\NormalizableTraversableDummy; use Symfony\Component\Serializer\Tests\Normalizer\TestNormalizer; @@ -398,11 +403,9 @@ public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadata $example = new DummyMessageNumberOne(); $example->one = 1; - $jsonData = '{"message-type":"one","one":1}'; - - $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface()); - $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); + $jsonData = '{"type":"one","one":1,"two":null}'; + $serializer = $this->serializerWithClassDiscriminator(); $deserialized = $serializer->deserialize($jsonData, DummyMessageInterface::class, 'json'); $this->assertEquals($example, $deserialized); @@ -410,51 +413,64 @@ public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadata $this->assertEquals($jsonData, $serialized); } + public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadataDiscriminatorResolverAndGroups() + { + $example = new DummyMessageNumberOne(); + $example->two = 2; + + $serializer = $this->serializerWithClassDiscriminator(); + $deserialized = $serializer->deserialize('{"type":"one","one":1,"two":2}', DummyMessageInterface::class, 'json', array( + 'groups' => array('two'), + )); + + $this->assertEquals($example, $deserialized); + + $serialized = $serializer->serialize($deserialized, 'json', array( + 'groups' => array('two'), + )); + + $this->assertEquals('{"two":2,"type":"one"}', $serialized); + } + + public function testDeserializeAndSerializeNestedInterfacedObjectsWithTheClassMetadataDiscriminator() + { + $nested = new DummyMessageNumberOne(); + $nested->one = 'foo'; + + $example = new DummyMessageNumberTwo(); + $example->setNested($nested); + + $serializer = $this->serializerWithClassDiscriminator(); + + $serialized = $serializer->serialize($example, 'json'); + $deserialized = $serializer->deserialize($serialized, DummyMessageInterface::class, 'json'); + + $this->assertEquals($example, $deserialized); + } + /** * @expectedException \Symfony\Component\Serializer\Exception\RuntimeException * @expectedExceptionMessage The type "second" has no mapped class for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface" */ public function testExceptionWhenTypeIsNotKnownInDiscriminator() { - $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface()); - $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); - $serializer->deserialize('{"message-type":"second","one":1}', DummyMessageInterface::class, 'json'); + $this->serializerWithClassDiscriminator()->deserialize('{"type":"second","one":1}', DummyMessageInterface::class, 'json'); } /** * @expectedException \Symfony\Component\Serializer\Exception\RuntimeException - * @expectedExceptionMessage Type property "message-type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface" + * @expectedExceptionMessage Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface" */ public function testExceptionWhenTypeIsNotInTheBodyToDeserialiaze() { - $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface()); - $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); - $serializer->deserialize('{"one":1}', DummyMessageInterface::class, 'json'); + $this->serializerWithClassDiscriminator()->deserialize('{"one":1}', DummyMessageInterface::class, 'json'); } - private function metadataFactoryMockForDummyInterface() + private function serializerWithClassDiscriminator() { - $factoryMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock(); - $factoryMock->method('hasMetadataFor')->will($this->returnValueMap(array( - array( - DummyMessageInterface::class, - true, - ), - ))); - - $factoryMock->method('getMetadataFor')->will($this->returnValueMap(array( - array( - DummyMessageInterface::class, - new ClassMetadata( - DummyMessageInterface::class, - new ClassDiscriminatorMapping('message-type', array( - 'one' => DummyMessageNumberOne::class, - )) - ), - ), - ))); + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); - return $factoryMock; + return new Serializer(array(new ObjectNormalizer($classMetadataFactory, null, null, new ReflectionExtractor(), new ClassDiscriminatorFromClassMetadata($classMetadataFactory))), array('json' => new JsonEncoder())); } } diff --git a/src/Symfony/Component/Translation/Dumper/FileDumper.php b/src/Symfony/Component/Translation/Dumper/FileDumper.php index 5a67b3a0d29dc..07c38b8291010 100644 --- a/src/Symfony/Component/Translation/Dumper/FileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/FileDumper.php @@ -51,7 +51,7 @@ public function setRelativePathTemplate($relativePathTemplate) */ public function setBackup($backup) { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); if (false !== $backup) { throw new \LogicException('The backup feature is no longer supported.'); diff --git a/src/Symfony/Component/Translation/PluralizationRules.php b/src/Symfony/Component/Translation/PluralizationRules.php index 48a6c608cbfe2..94d9e4350c3c2 100644 --- a/src/Symfony/Component/Translation/PluralizationRules.php +++ b/src/Symfony/Component/Translation/PluralizationRules.php @@ -144,6 +144,7 @@ public static function get($number, $locale) case 'bs': case 'hr': case 'ru': + case 'sh': case 'sr': case 'uk': return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); diff --git a/src/Symfony/Component/Translation/Writer/TranslationWriter.php b/src/Symfony/Component/Translation/Writer/TranslationWriter.php index c03ded3c91993..ee35ff63eb61f 100644 --- a/src/Symfony/Component/Translation/Writer/TranslationWriter.php +++ b/src/Symfony/Component/Translation/Writer/TranslationWriter.php @@ -43,7 +43,7 @@ public function addDumper($format, DumperInterface $dumper) */ public function disableBackup() { - @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); foreach ($this->dumpers as $dumper) { if (method_exists($dumper, 'setBackup')) { diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf index 85b6970fc31cf..078a25d052d10 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ca.xlf @@ -302,6 +302,10 @@ An empty file is not allowed. No està permès un fixter buit. + + This is not a valid UUID. + Aquest valor no és un UUID vàlid. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf index 6e89e17ec3f1f..3e44e1e284b8c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf @@ -318,6 +318,10 @@ Error Fehler + + This is not a valid UUID. + Dies ist keine gültige UUID. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index 7e0e9614de50a..3d173846a54f1 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -318,6 +318,10 @@ Error Error + + This is not a valid UUID. + This is not a valid UUID. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf index 948594d03e91b..25d5b8a5d33a7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf @@ -318,6 +318,10 @@ Error Error + + This is not a valid UUID. + Este valor no es un UUID válido. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf index d4125fdb0fbed..d311dedb5e62e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.eu.xlf @@ -282,6 +282,10 @@ Error Errore + + This is not a valid UUID. + Balio hau ez da onartutako UUID bat. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf index 6c1b4030f3b0d..382acb975ce66 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf @@ -318,6 +318,10 @@ Error Erreur + + This is not a valid UUID. + Ceci n'est pas un UUID valide. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf index 3a8a1db6eebd0..1011d5481829b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf @@ -318,6 +318,10 @@ Error Hiba + + This is not a valid UUID. + Érvénytelen egyedi azonosító (UUID). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf index bd71ece6aea49..f19544701d637 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.it.xlf @@ -318,6 +318,10 @@ Error Errore + + This is not a valid UUID. + Questo non è un UUID valido. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf index 70ec678ec9a6f..413a97eb17c6f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf @@ -314,6 +314,10 @@ Error Fout + + This is not a valid UUID. + Deze waarde is geen geldige UUID waarde. + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RegexTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RegexTest.php index 3291c77356f23..26ef1b27361ad 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RegexTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RegexTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Constraints; +namespace Symfony\Component\Validator\Tests\Constraints; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Regex; diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 947b9c002eb52..f977157231812 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -541,6 +541,10 @@ private function hasColorSupport($stream) return false; } + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + if (DIRECTORY_SEPARATOR === '\\') { return (function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support($stream)) @@ -575,7 +579,8 @@ private function isWindowsTrueColor() { $result = 183 <= getenv('ANSICON_VER') || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM'); + || 'xterm' === getenv('TERM') + || 'Hyper' === getenv('TERM_PROGRAM'); if (!$result && PHP_VERSION_ID >= 70200) { $version = sprintf( diff --git a/src/Symfony/Component/Workflow/Event/Event.php b/src/Symfony/Component/Workflow/Event/Event.php index 943a4da5a681e..8e7cbe2f213b0 100644 --- a/src/Symfony/Component/Workflow/Event/Event.php +++ b/src/Symfony/Component/Workflow/Event/Event.php @@ -30,18 +30,21 @@ class Event extends BaseEvent private $workflowName; /** - * @param object $subject - * @param Marking $marking - * @param Transition $transition - * @param Workflow $workflow + * @param object $subject + * @param Marking $marking + * @param Transition $transition + * @param WorkflowInterface $workflow */ public function __construct($subject, Marking $marking, Transition $transition, $workflow = null) { $this->subject = $subject; $this->marking = $marking; $this->transition = $transition; - if (is_string($workflow)) { - @trigger_error(sprintf('Passing a string as 4th parameter of "%s" is deprecated since Symfony 4.1. Pass a %s instance instead.', __METHOD__, WorkflowInterface::class), E_USER_DEPRECATED); + if (null === $workflow) { + @trigger_error(sprintf('Passing only three parameters to "%s" is deprecated since Symfony 4.1. Pass a %s instance as fourth parameter instead.', __METHOD__, WorkflowInterface::class), E_USER_DEPRECATED); + $this->workflowName = 'unnamed'; + } elseif (is_string($workflow)) { + @trigger_error(sprintf('Passing a string as the 4th parameter of "%s()" is deprecated since Symfony 4.1. Pass a %s instance instead.', __METHOD__, WorkflowInterface::class), E_USER_DEPRECATED); $this->workflowName = $workflow; } elseif ($workflow instanceof WorkflowInterface) { $this->workflow = $workflow; diff --git a/src/Symfony/Component/Workflow/Registry.php b/src/Symfony/Component/Workflow/Registry.php index f3fd384e0d5c2..f2117f8715039 100644 --- a/src/Symfony/Component/Workflow/Registry.php +++ b/src/Symfony/Component/Workflow/Registry.php @@ -31,7 +31,7 @@ class Registry */ public function add(Workflow $workflow, $supportStrategy) { - @trigger_error(sprintf('%s is deprecated since Symfony 4.1. Use addWorkflow() instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use addWorkflow() instead.', __METHOD__), E_USER_DEPRECATED); $this->workflows[] = array($workflow, $supportStrategy); } diff --git a/src/Symfony/Component/Workflow/Tests/RegistryTest.php b/src/Symfony/Component/Workflow/Tests/RegistryTest.php index e61d9b6b6372e..6d30d2f7e21d6 100644 --- a/src/Symfony/Component/Workflow/Tests/RegistryTest.php +++ b/src/Symfony/Component/Workflow/Tests/RegistryTest.php @@ -31,7 +31,7 @@ protected function tearDown() /** * @group legacy - * @expectedDeprecation Symfony\Component\Workflow\Registry::add is deprecated since Symfony 4.1. Use addWorkflow() instead. + * @expectedDeprecation The "Symfony\Component\Workflow\Registry::add()" method is deprecated since Symfony 4.1. Use addWorkflow() instead. */ public function testAddIsDeprecated() {