diff --git a/.appveyor.yml b/.appveyor.yml index 34d3a703337e9..4b417c5dfc122 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -54,6 +54,7 @@ test_script: - SET X=0 - SET SYMFONY_PHPUNIT_SKIPPED_TESTS=phpunit.skipped - copy /Y c:\php\php.ini-min c:\php\php.ini + - IF %APPVEYOR_REPO_BRANCH% neq master (rm -Rf src\Symfony\Bridge\PhpUnit) - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! - copy /Y c:\php\php.ini-max c:\php\php.ini - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! diff --git a/.travis.yml b/.travis.yml index 19bcdeac8ef54..2e2de052182e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,6 +48,8 @@ before_install: # Enable Sury ppa sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 6B05F25D762E3157 sudo add-apt-repository -y ppa:ondrej/php + sudo rm /etc/apt/sources.list.d/google-chrome.list + sudo rm /etc/apt/sources.list.d/mongodb-3.4.list sudo apt update sudo apt install -y librabbitmq-dev libsodium-dev @@ -195,6 +197,12 @@ install: SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*') fi + - | + # Skip the phpunit-bridge on not-master branches when $deps is empty + if [[ ! $deps && $TRAVIS_BRANCH != master ]]; then + COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -not -wholename '*/Bridge/PhpUnit/*' -printf '%h\n') + fi + - | # Install symfony/flex if [[ $deps = low ]]; then diff --git a/CHANGELOG-4.2.md b/CHANGELOG-4.2.md index 6af197319cde7..8611ee540a1ca 100644 --- a/CHANGELOG-4.2.md +++ b/CHANGELOG-4.2.md @@ -7,6 +7,45 @@ in 4.2 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.2.0...v4.2.1 +* 4.2.6 (2019-04-16) + + * bug #31088 [DI] fix removing non-shared definition while inlining them (nicolas-grekas) + * bug #29944 [DI] Overriding services autowired by name under _defaults bind not working (przemyslaw-bogusz, renanbr) + * bug #30993 [FrameworkBundle] Fix for Controller DEPRECATED when using composer --optimized (aweelex) + * bug #31076 [HttpKernel] Fixed LoggerDataCollector crashing on empty file (althaus) + * bug #31071 property normalizer should also pass format and context to isAllowedAttribute (dbu) + * bug #31059 Show more accurate message in profiler when missing stopwatch (linaori) + * bug #31026 [Serializer] Add default object class resolver (jdecool) + * bug #31031 [Serializer] MetadataAwareNameConverter: Do not assume that property names are strings (soyuka) + * bug #31043 [VarExporter] support PHP7.4 __serialize & __unserialize (nicolas-grekas) + * bug #30423 [Security] Rework firewall's access denied rule (dimabory) + * bug #31020 [VarExporter] fix exporting classes with private constructors (nicolas-grekas) + * bug #31012 [Process] Fix missing $extraDirs when open_basedir returns (arsonik) + * bug #30852 [Console] fix buildTableRows when Colspan is use with content too long (Raulnet) + * bug #30950 [Serializer] Also validate callbacks when given in the normalizer context (dbu) + * bug #30907 [Serializer] Respect ignored attributes in cache key of normalizer (dbu) + * bug #30085 Fix TestRunner compatibility to PhpUnit 8 (alexander-schranz) + * bug #30999 Fix dark themed componnents (ro0NL) + * bug #30977 [serializer] prevent mixup in normalizer of the object to populate (dbu) + * bug #30976 [Debug] Fixed error handling when an error is already handled when another error is already handled (5) (lyrixx) + * bug #30979 Fix the configurability of CoreExtension deps in standalone usage (stof) + * bug #30918 [Cache] fix using ProxyAdapter inside TagAwareAdapter (dmaicher) + * bug #30961 [Form] fix translating file validation error message (xabbuh) + * bug #30951 Handle case where no translations were found (greg0ire) + * bug #29800 [Validator] Only traverse arrays that are cascaded into (corphi) + * bug #30921 [Translator] Warm up the translations cache in dev (tgalopin) + * bug #30922 [TwigBridge] fix horizontal spacing of inlined Bootstrap forms (xabbuh) + * bug #30860 [Profiler] Fix dark theme elements color (dFayet) + * bug #30895 [Form] turn failed file uploads into form errors (xabbuh) + * bug #30919 [Translator] Fix wrong dump for PO files (deguif) + * bug #30889 [DependencyInjection] Fix a wrong error when using a factory (Simperfit) + * bug #30911 [Console] Fix table trailing backslash (maidmaid) + * bug #30903 [Messenger] Uses the `SerializerStamp` when deserializing the envelope (sroze) + * bug #30879 [Form] Php doc fixes and cs + optimizations (Jules Pietri) + * bug #30883 [Console] Fix stty not reset when aborting in QuestionHelper::autocomplete() (Simperfit) + * bug #30878 [Console] Fix inconsistent result for choice questions in non-interactive mode (chalasr) + * bug #30825 [Routing] Fix: annotation loader ignores method's default values (voronkovich) + * 4.2.5 (2019-04-02) * bug #30660 [Bridge][Twig] DebugCommand - fix escaping and filter (SpacePossum) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bc64e2f7979e5..efb1b16d81ab9 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -15,21 +15,21 @@ Symfony is the result of the work of many people who made the code better - Victor Berchet (victor) - Kévin Dunglas (dunglas) - Maxime Steinhausser (ogizanagi) + - Ryan Weaver (weaverryan) - Jakub Zalas (jakubzalas) - Johannes S (johannes) - Javier Eguiluz (javier.eguiluz) - - Ryan Weaver (weaverryan) - Kris Wallsmith (kriswallsmith) - - Roland Franssen (ro0) - Grégoire Pineau (lyrixx) + - Roland Franssen (ro0) - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) - Romain Neutron (romain) - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) + - Samuel ROZE (sroze) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - - Samuel ROZE (sroze) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) - Jeremy Mikola (jmikola) @@ -42,8 +42,8 @@ Symfony is the result of the work of many people who made the code better - Guilhem Niot (energetick) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) - - Hamza Amrouche (simperfit) - Tobias Nyholm (tobias) + - Hamza Amrouche (simperfit) - Diego Saint Esteben (dosten) - Iltar van der Berg (kjarli) - Alexandre Salomé (alexandresalome) @@ -54,8 +54,8 @@ Symfony is the result of the work of many people who made the code better - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - Bulat Shakirzyanov (avalanche123) - - Matthias Pigulla (mpdude) - Jérémy DERUSSÉ (jderusse) + - Matthias Pigulla (mpdude) - Saša Stamenković (umpirsky) - Peter Rehm (rpet) - Kevin Bond (kbond) @@ -69,23 +69,24 @@ Symfony is the result of the work of many people who made the code better - Mathieu Piot (mpiot) - Florin Patan (florinpatan) - Gábor Egyed (1ed) + - Gabriel Ostrolucký (gadelat) - Titouan Galopin (tgalopin) - Vladimir Reznichenko (kalessil) - Jáchym Toušek (enumag) + - David Maicher (dmaicher) - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) - Konstantin Myakshin (koc) - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) - - David Maicher (dmaicher) - Christian Raue - Issei Murasawa (issei_m) + - Valentin Udaltsov (vudaltsov) - Arnout Boks (aboks) - Deni - Grégoire Paris (greg0ire) - Henrik Westphal (snc) - Dariusz Górecki (canni) - - Valentin Udaltsov (vudaltsov) - Douglas Greenshields (shieldo) - Dariusz Ruminski - Lee McDermott @@ -99,7 +100,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) - - gadelat (gadelat) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - Arnaud Le Blanc (arnaud-lb) @@ -108,13 +108,14 @@ Symfony is the result of the work of many people who made the code better - Tim Nagel (merk) - Brice BERNARD (brikou) - Baptiste Clavié (talus) + - Chris Wilkinson (thewilkybarkid) - marc.weistroff - Tomáš Votruba (tomas_votruba) - David Buchmann (dbu) - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) - - Chris Wilkinson (thewilkybarkid) + - Thomas Calvet (fancyweb) - Jérôme Vasseur (jvasseur) - Peter Kokot (maastermedia) - Jacob Dreesen (jdreesen) @@ -125,11 +126,10 @@ Symfony is the result of the work of many people who made the code better - Daniel Wehner (dawehner) - excelwebzone - Gordon Franke (gimler) - - Thomas Calvet (fancyweb) + - Sebastiaan Stok (sstok) - Javier Spagnoletti (phansys) - Fabien Pennequin (fabienpennequin) - Eric GELOEN (gelo) - - Sebastiaan Stok (sstok) - Lars Strojny (lstrojny) - Tugdual Saunier (tucksaun) - Théo FIDRY (theofidry) @@ -140,14 +140,18 @@ Symfony is the result of the work of many people who made the code better - Alex Pott - Vincent AUBERT (vincent) - Juti Noppornpitak (shiroyuki) + - Anthony MARTIN (xurudragon) + - Oskar Stark (oskarstark) - Tigran Azatyan (tigranazatyan) - Sebastian Hörl (blogsh) - Daniel Gomes (danielcsgomes) + - Joel Wurtz (brouznouf) - Gabriel Caruso - Hidenori Goto (hidenorigoto) - Arnaud Kleinpeter (nanocom) - Jannik Zschiesche (apfelbox) - Guilherme Blanco (guilhermeblanco) + - Teoh Han Hui (teohhanhui) - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) - Andréia Bohner (andreia) @@ -155,9 +159,7 @@ Symfony is the result of the work of many people who made the code better - Julien Falque (julienfalque) - Rafael Dohms (rdohms) - jwdeitch - - Teoh Han Hui (teohhanhui) - Mikael Pajunen - - Joel Wurtz (brouznouf) - Oleg Voronkovich - Vyacheslav Pavlov - Richard van Laak (rvanlaak) @@ -172,7 +174,6 @@ Symfony is the result of the work of many people who made the code better - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) - Dawid Nowak - - Gabriel Ostrolucký - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) - Artur Kotyrba @@ -181,12 +182,12 @@ Symfony is the result of the work of many people who made the code better - SpacePossum - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) - - Anthony MARTIN (xurudragon) - Matthieu Napoli (mnapoli) - Florent Mata (fmata) - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) + - Marek Štípek (maryo) - Daniel Espendiller - Possum - Dorian Villet (gnutix) @@ -196,7 +197,7 @@ Symfony is the result of the work of many people who made the code better - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) - DQNEO - - Oskar Stark (oskarstark) + - Samuel NELA (snela) - Benjamin Dulau (dbenjamin) - François-Xavier de Guillebon (de-gui_f) - Mathieu Lemoine (lemoinem) @@ -205,7 +206,6 @@ Symfony is the result of the work of many people who made the code better - Tom Van Looy (tvlooy) - Noel Guilbert (noel) - Yanick Witschi (toflar) - - Marek Štípek (maryo) - Stepan Anchugov (kix) - bronze1man - sun (sun) @@ -217,24 +217,28 @@ Symfony is the result of the work of many people who made the code better - apetitpa - Matthieu Bontemps (mbontemps) - apetitpa - - Samuel NELA (snela) - Pierre Minnieur (pminnieur) - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) - Michael Lee (zerustech) - Matthieu Auger (matthieuauger) + - Gregor Harlan (gharlan) - Leszek Prabucki (l3l0) - Fabien Bourigault (fbourigault) - François Zaninotto (fzaninotto) - Dustin Whittle (dustinwhittle) - jeff - John Kary (johnkary) + - Andreas Schempp (aschempp) - Justin Hileman (bobthecow) - Blanchon Vincent (blanchonvincent) - Michele Orselli (orso) - Sven Paulus (subsven) + - Maxime Veber (nek-) + - Gary PEGEOT (gary-p) - Rui Marinho (ruimarinho) + - Massimiliano Arione (garak) - Eugene Wissner - Pascal Montoya - Julien Brochet (mewt) @@ -242,15 +246,17 @@ Symfony is the result of the work of many people who made the code better - Tristan Darricau (nicofuma) - Marcel Beerta (mazen) - Pavel Batanov (scaytrase) + - Mantis Development - Loïc Faugeron - Hidde Wieringa (hiddewie) - Marco Pivetta (ocramius) + - Jan Schädlich (jschaedl) - Rob Frawley 2nd (robfrawley) - julien pauli (jpauli) - Lorenz Schori - Sébastien Lavoie (lavoiesl) - - Gregor Harlan (gharlan) - Dariusz + - Michael Babker (mbabker) - Francois Zaninotto - Alexander Kotynia (olden) - Daniel Tschinder @@ -265,7 +271,6 @@ Symfony is the result of the work of many people who made the code better - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) - Mickaël Andrieu (mickaelandrieu) - - Maxime Veber (nek-) - Xavier Perez - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA @@ -277,6 +282,7 @@ Symfony is the result of the work of many people who made the code better - Baptiste Lafontaine (magnetik) - Jakub Kucharovic (jkucharovic) - Edi Modrić (emodric) + - Alexander Schranz (alexander-schranz) - Uwe Jäger (uwej711) - Eugene Leonovich (rybakit) - Filippo Tessarotto @@ -290,10 +296,9 @@ Symfony is the result of the work of many people who made the code better - Viktor Bocharskyi (bocharsky_bw) - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - - Andreas Schempp (aschempp) - jdhoek - - Massimiliano Arione (garak) - Bob den Otter (bopp) + - Thomas Schulz (king2500) - Frank de Jonge (frenkynet) - Nikita Konstantinov - Wodor Wodorski @@ -301,7 +306,7 @@ Symfony is the result of the work of many people who made the code better - mcfedr (mcfedr) - Colin O'Dell (colinodell) - Giorgio Premi - - Jan Schädlich (jschaedl) + - Alex Rock (pierstoval) - Ben Davies (bendavies) - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) @@ -309,9 +314,9 @@ Symfony is the result of the work of many people who made the code better - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) + - Pascal Luna (skalpa) - Wouter Van Hecke - Jérôme Parmentier (lctrs) - - Michael Babker (mbabker) - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Remon van de Kamp (rpkamp) @@ -319,14 +324,13 @@ Symfony is the result of the work of many people who made the code better - Marc Weistroff (futurecat) - Christian Schmidt - MatTheCat - - Alexander Schranz (alexander-schranz) - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) - - Gary PEGEOT (gary-p) - Manuel Kiessling (manuelkiessling) - Atsuhiro KUBO (iteman) - rudy onfroy (ronfroy) + - Serkan Yildiz (srknyldz) - Andrew Moore (finewolf) - Bertrand Zuchuat (garfield-fr) - Sullivan SENECHAL (soullivaneuh) @@ -347,14 +351,16 @@ Symfony is the result of the work of many people who made the code better - Hidde Boomsma (hboomsma) - John Bafford (jbafford) - Raul Fraile (raulfraile) + - David Prévot - Adrian Rudnik (kreischweide) - Francesc Rosàs (frosas) - Romain Pierre (romain-pierre) - Julien Galenski (ruian) - Bongiraud Dominique - janschoenherr - - Thomas Schulz (king2500) + - Emanuele Gaspari (inmarelibero) - Dariusz Rumiński + - Vincent Touzet (vincenttouzet) - Berny Cantos (xphere81) - Thierry Thuon (lepiaf) - Ricard Clau (ricardclau) @@ -367,8 +373,9 @@ Symfony is the result of the work of many people who made the code better - Zan Baldwin (zanderbaldwin) - Thomas Royer (cydonia7) - alquerci + - Mateusz Sip (mateusz_sip) + - Andre Rømcke (andrerom) - Francesco Levorato - - Pascal Luna (skalpa) - Dmitrii Poddubnyi (karser) - Vitaliy Zakharov (zakharovvi) - Tobias Sjösten (tobiassjosten) @@ -384,6 +391,7 @@ Symfony is the result of the work of many people who made the code better - Yaroslav Kiliba - Terje Bråten - Robbert Klarenbeek (robbertkl) + - Eric Masoero (eric-masoero) - JhonnyL - David Badura (davidbadura) - hossein zolfi (ocean) @@ -394,6 +402,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) + - Anton Chernikov (anton_ch1989) - Grzegorz (Greg) Zdanowski (kiler129) - Iker Ibarguren (ikerib) - Kirill chEbba Chebunin (chebba) @@ -414,10 +423,10 @@ Symfony is the result of the work of many people who made the code better - Tobias Naumann (tna) - Daniel Beyer - Shein Alexey - - Alex Rock Ancelet (pierstoval) - Romain Gautier (mykiwi) - Joe Lencioni - Daniel Tschinder + - Emmanuel BORGES (eborges78) - vladimir.reznichenko - Kai - Lee Rowlands @@ -427,7 +436,6 @@ Symfony is the result of the work of many people who made the code better - Karoly Negyesi (chx) - Ivan Kurnosov - Xavier HAUSHERR - - David Prévot - Albert Jessurum (ajessu) - Laszlo Korte - Miha Vrhovnik @@ -440,7 +448,6 @@ Symfony is the result of the work of many people who made the code better - Karel Souffriau - Christophe L. (christophelau) - Anthon Pang (robocoder) - - Emanuele Gaspari (inmarelibero) - Sébastien Santoro (dereckson) - Brian King - Michel Salib (michelsalib) @@ -461,7 +468,7 @@ Symfony is the result of the work of many people who made the code better - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) - Alessandro Lai (jean85) - - Andre Rømcke (andrerom) + - Nicolas LEFEVRE (nicoweb) - Arturs Vonda - Josip Kruslin - Asmir Mustafic (goetas) @@ -473,7 +480,6 @@ Symfony is the result of the work of many people who made the code better - Vlad Gregurco (vgregurco) - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) - - Mateusz Sip (mateusz_sip) - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) @@ -528,6 +534,7 @@ Symfony is the result of the work of many people who made the code better - ondrowan - Barry vd. Heuvel (barryvdh) - Craig Duncan (duncan3dc) + - Patrick Landolt (scube) - Sébastien Alfaiate (seb33300) - Evan S Kaufman (evanskaufman) - mcben @@ -603,6 +610,7 @@ Symfony is the result of the work of many people who made the code better - Jan Behrens - Mantas Var (mvar) - Sebastian Krebs + - Piotr Stankowski - Baptiste Leduc (bleduc) - Laurent VOULLEMIER (lvo) - Jean-Christophe Cuvelier [Artack] @@ -617,6 +625,7 @@ Symfony is the result of the work of many people who made the code better - Renan - Ricky Su (ricky) - Gildas Quéméner (gquemener) + - Kyle Evans (kevans91) - Charles-Henri Bruyand - Max Rath (drak3) - Stéphane Escandell (sescandell) @@ -625,6 +634,8 @@ Symfony is the result of the work of many people who made the code better - Sinan Eldem - Alexandre Dupuy (satchette) - Malte Blättermann + - Kévin THERAGE (kevin_therage) + - Simeon Kolev (simeon_kolev9) - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) - Christophe Villeger (seragan) @@ -649,11 +660,13 @@ Symfony is the result of the work of many people who made the code better - Sebastian Marek (proofek) - Guilhem N (guilhemn) - Erkhembayar Gantulga (erheme318) + - zenmate - Michal Trojanowski - David Fuhr - Max Grigorian (maxakawizard) - DerManoMann - Rostyslav Kinash + - Dennis Fridrich (dfridrich) - Maciej Malarz (malarzm) - Daisuke Ohata - Vincent Simonin @@ -707,6 +720,7 @@ Symfony is the result of the work of many people who made the code better - Paweł Wacławczyk (pwc) - Oleg Zinchenko (cystbear) - Baptiste Meyer (meyerbaptiste) + - Tales Santos (tsantos84) - Johannes Klauss (cloppy) - Evan Villemez - fzerorubigd @@ -720,6 +734,7 @@ Symfony is the result of the work of many people who made the code better - flip111 - Greg Anderson - VJ + - RJ Garcia - Delf Tonder (leberknecht) - Mark Sonnabaum - Massimiliano Braglia (massimilianobraglia) @@ -738,15 +753,14 @@ Symfony is the result of the work of many people who made the code better - Julien DIDIER (juliendidier) - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) - - Serkan Yildiz (srknyldz) - Jeroen van den Enden (stoefke) + - nikos.sotiropoulos - Pascal Helfenstein - Anthony GRASSIOT (antograssiot) - Baldur Rensch (brensch) - Pierre Rineau - Vladyslav Petrovych - Alex Xandra Albert Sim - - Patrick Landolt (scube) - Carson Full - Sergey Yastrebov - Trent Steel (trsteel88) @@ -761,6 +775,7 @@ Symfony is the result of the work of many people who made the code better - Dave Marshall (davedevelopment) - Jakub Kulhan (jakubkulhan) - avorobiev + - Grégoire Penverne (gpenverne) - Venu - Lars Vierbergen - Jonatan Männchen @@ -774,6 +789,7 @@ Symfony is the result of the work of many people who made the code better - Jordan Deitch - Casper Valdemar Poulsen - Josiah (josiah) + - Greg ORIOL - Joschi Kuphal - John Bohn (jbohn) - Marc Morera (mmoreram) @@ -922,6 +938,7 @@ Symfony is the result of the work of many people who made the code better - Stéphane Delprat - Brian Freytag (brianfreytag) - Samuele Lilli (doncallisto) + - Marko Kaznovac (kaznovac) - Brunet Laurent (lbrunet) - Florent Viel (luxifer) - Mikhail Yurasov (mym) @@ -937,15 +954,17 @@ Symfony is the result of the work of many people who made the code better - Kyle - Daniel Alejandro Castro Arellano (lexcast) - sensio + - Thomas Jarrand - Sebastien Morel (plopix) - Patrick Kaufmann - - Piotr Stankowski - Anton Dyshkant - Reece Fowell (reecefowell) - Mátyás Somfai (smatyas) - stefan.r - Valérian Galliat - d-ph + - Renan Taranto (renan-taranto) + - Thomas Talbot (ioni) - Rikijs Murgs - Ben Ramsey (ramsey) - Amaury Leroux de Lens (amo__) @@ -954,12 +973,14 @@ Symfony is the result of the work of many people who made the code better - The Whole Life to Learn - ergiegonzaga - Farhad Safarov + - Alexis Lefebvre - Liverbool (liverbool) - Sam Malone - Phan Thanh Ha (haphan) - Chris Jones (leek) - xaav - Mahmoud Mostafa (mahmoud) + - Ahmed Abdou - Pieter - Michael Tibben - Billie Thompson @@ -992,21 +1013,23 @@ Symfony is the result of the work of many people who made the code better - Irmantas Šiupšinskas (irmantas) - Danilo Silva - Arnaud PETITPAS (apetitpa) + - Ken Stanley - Zachary Tong (polyfractal) - Ashura - Hryhorii Hrebiniuk - johnstevenson - - Dennis Fridrich (dfridrich) - hamza - dantleech - Bastien DURAND (deamon) - Xavier Leune - Rudy Onfroy - Tero Alén (tero) + - Stanislav Kocanda - DerManoMann - Guillaume Royer - Artem (digi) - boite + - Silvio Ginter - MGDSoft - Vadim Tyukov (vatson) - David Wolter (davewww) @@ -1015,6 +1038,7 @@ Symfony is the result of the work of many people who made the code better - Wojciech Sznapka - Gavin Staniforth - Ariel J. Birnbaum + - Danijel Obradović - Pablo Borowicz - Mathieu Santostefano - Arjan Keeman @@ -1065,7 +1089,6 @@ Symfony is the result of the work of many people who made the code better - Jean-Guilhem Rouel (jean-gui) - jfcixmedia - Dominic Tubach - - Tales Santos (tsantos84) - Nikita Konstantinov - Martijn Evers - Vitaliy Ryaboy (vitaliy) @@ -1094,6 +1117,7 @@ Symfony is the result of the work of many people who made the code better - Tadas Gliaubicas (tadcka) - Thanos Polymeneas (thanos) - Benoit Garret + - Maximilian Ruta (deltachaos) - Jakub Sacha - Olaf Klischat - orlovv @@ -1131,7 +1155,6 @@ Symfony is the result of the work of many people who made the code better - rpg600 - Péter Buri (burci) - kaiwa - - RJ Garcia - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) - Neil Katin @@ -1148,7 +1171,6 @@ Symfony is the result of the work of many people who made the code better - rchoquet - gitlost - Taras Girnyk - - nikos.sotiropoulos - Eduardo García Sanz (coma) - Sergio (deverad) - James Gilliland @@ -1207,7 +1229,6 @@ Symfony is the result of the work of many people who made the code better - Mario Young - Ilia (aliance) - Chris McCafferty (cilefen) - - Grégoire Penverne (gpenverne) - Mo Di (modi) - Pablo Schläpfer - Gert de Pagter @@ -1215,6 +1236,7 @@ Symfony is the result of the work of many people who made the code better - Quique Porta (quiqueporta) - stoccc - Tomasz Szymczyk (karion) + - Alex Vasilchenko - Xavier Coureau - ConneXNL - Aharon Perkel @@ -1232,8 +1254,10 @@ Symfony is the result of the work of many people who made the code better - Sebastian Göttschkes (sgoettschkes) - Tatsuya Tsuruoka - Ross Tuck + - Andreas Erhard - Kévin Gomez (kevin) - Mihai Nica (redecs) + - Soufian EZ-ZANTAR (soezz) - Andrei Igna - azine - Dawid Sajdak @@ -1249,6 +1273,7 @@ Symfony is the result of the work of many people who made the code better - Saem Ghani - Clément LEFEBVRE - Conrad Kleinespel + - Matthias Althaus - Zacharias Luiten - Sebastian Utz - Adrien Gallou (agallou) @@ -1288,6 +1313,7 @@ Symfony is the result of the work of many people who made the code better - Markus Fasselt (digilist) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) + - Fleuv - Sandro Hopf - Łukasz Makuch - George Giannoulopoulos @@ -1311,11 +1337,11 @@ Symfony is the result of the work of many people who made the code better - Daniel Kay (danielkay-cp) - Matt Daum (daum) - Alberto Pirovano (geezmo) - - Nicolas LEFEVRE (nicoweb) - Pete Mitchell (peterjmit) - Tom Corrigan (tomcorrigan) - Luis Galeas - Martin Pärtel + - Frédéric Bouchery (fbouchery) - Patrick Daley (padrig) - Xavier Briand (xavierbriand) - Max Summe @@ -1336,6 +1362,7 @@ Symfony is the result of the work of many people who made the code better - Emmanuel Vella (emmanuel.vella) - Guillaume BRETOU (guiguiboy) - Jonathan Johnson (jrjohnson) + - Dāvis Zālītis (k0d3r1s) - Carsten Nielsen (phreaknerd) - Roger Guasch (rogerguasch) - Mathieu Rochette @@ -1371,6 +1398,7 @@ Symfony is the result of the work of many people who made the code better - mlively - Amine Matmati - caalholm + - Nouhail AL FIDI (alfidi) - Fabian Steiner (fabstei) - Felipy Tavares Amorim (felipyamorim) - Klaus Silveira (klaussilveira) @@ -1420,7 +1448,6 @@ Symfony is the result of the work of many people who made the code better - Hoffmann András - LubenZA - Olivier - - Anton Chernikov - Cyril PASCAL - pscheit - Wybren Koelmans @@ -1432,6 +1459,7 @@ Symfony is the result of the work of many people who made the code better - Antonio Peric-Mazar (antonioperic) - César Suárez (csuarez) - Bjorn Twachtmann (dotbjorn) + - Tobias Genberg (lorceroth) - Luis Tacón (lutacon) - Nicolas Badey (nico-b) - Shane Preece (shane) @@ -1465,10 +1493,9 @@ Symfony is the result of the work of many people who made the code better - Przemysław Piechota (kibao) - Leonid Terentyev (li0n) - Martynas Sudintas (martiis) - - Gabriel Ostrolucký - ryunosuke - - zenmate - victoria + - Dmytro Borysovskyi (dmytr0) - Francisco Facioni (fran6co) - Iwan van Staveren (istaveren) - Povilas S. (povilas) @@ -1488,6 +1515,7 @@ Symfony is the result of the work of many people who made the code better - Ikhsan Agustian - Arnau González (arnaugm) - Simon Bouland (bouland) + - Jibé Barth (jibbarth) - Matthew Foster (mfoster) - Reyo Stallenberg (reyostallenberg) - Paul Seiffert (seiffert) @@ -1531,7 +1559,6 @@ Symfony is the result of the work of many people who made the code better - Gunther Konig - Mickael GOETZ - Maciej Schmidt - - Greg ORIOL - Dennis Væversted - nuncanada - flack @@ -1652,6 +1679,7 @@ Symfony is the result of the work of many people who made the code better - Trevor Suarez - gedrox - Alan Bondarchuk + - Joe Bennett - dropfen - Andrey Chernykh - Edvinas Klovas @@ -1671,6 +1699,7 @@ Symfony is the result of the work of many people who made the code better - AmsTaFF (amstaff) - Simon Müller (boscho) - Yannick Bensacq (cibou) + - Damien (damien_vauchel) - Frédéric G. Marand (fgm) - Freek Van der Herten (freekmurze) - Luca Genuzio (genuzio) @@ -1735,6 +1764,7 @@ Symfony is the result of the work of many people who made the code better - timaschew - Jochen Mandl - Marin Nicolae + - soyuka - Alessandro Loffredo - Ian Phillips - Marco Lipparini @@ -1810,6 +1840,7 @@ Symfony is the result of the work of many people who made the code better - Kevin Verschaeve (keversc) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) + - Mehdi Mabrouk (mehdidev) - Bart Reunes (metalarend) - Muriel (metalmumu) - Michael Pohlers (mick_the_big) @@ -1861,6 +1892,7 @@ Symfony is the result of the work of many people who made the code better - Ramon Henrique Ornelas (ramonornela) - Ricardo de Vries (ricknox) - Markus S. (staabm) + - Thomas Dutrion (theocrite) - Till Klampaeckel (till) - Tobias Weinert (tweini) - Ulf Reimers (ureimers) @@ -2011,7 +2043,6 @@ Symfony is the result of the work of many people who made the code better - phc - Дмитрий Пацура - ilyes kooli - - Marko Kaznovac - Matthias Althaus - Michaël VEROUX - Julia @@ -2073,6 +2104,7 @@ Symfony is the result of the work of many people who made the code better - samuel laulhau (lalop) - Laurent Bachelier (laurentb) - Luís Cobucci (lcobucci) + - Marcos Gómez Vilches (markitosgv) - Matthieu Mota (matthieumota) - Matthieu Moquet (mattketmo) - Moritz Borgmann (mborgmann) diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index bf73c0036d030..ae481b572628e 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -46,7 +46,7 @@ protected function resetService($name) $manager = $this->container->get($name); if (!$manager instanceof LazyLoadingInterface) { - throw new \LogicException(sprintf('Resetting a non-lazy manager service is not supported. Set the "%s" service as lazy and require "symfony/proxy-manager-bridge" in your composer.json file instead.', $name)); + throw new \LogicException('Resetting a non-lazy manager service is not supported. '.(interface_exists(LazyLoadingInterface::class) ? sprintf('Declare the "%s" service as lazy.', $name) : 'Try running "composer require symfony/proxy-manager-bridge".')); } $manager->setProxyInitializer(\Closure::bind( function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) { diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php index d4b5ea26d8cd8..95dcb1e5541fc 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php @@ -23,6 +23,24 @@ class CommandForV5 extends \PHPUnit_TextUI_Command */ protected function createRunner() { - return new TestRunnerForV5($this->arguments['loader']); + $listener = new SymfonyTestsListenerForV5(); + + $this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : array(); + + $registeredLocally = false; + + foreach ($this->arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListenerForV5) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $this->arguments['listeners'][] = $listener; + } + + return parent::createRunner(); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php index fc717ef415cb3..f8f75bb09a93b 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php @@ -13,7 +13,7 @@ use PHPUnit\TextUI\Command as BaseCommand; use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\TextUI\TestRunner; +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; /** * {@inheritdoc} @@ -27,6 +27,24 @@ class CommandForV6 extends BaseCommand */ protected function createRunner(): BaseRunner { - return new TestRunner($this->arguments['loader']); + $listener = new SymfonyTestsListener(); + + $this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : []; + + $registeredLocally = false; + + foreach ($this->arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListener) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $this->arguments['listeners'][] = $listener; + } + + return parent::createRunner(); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php deleted file mode 100644 index 7897861cf52f7..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV5 extends \PHPUnit_TextUI_TestRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments) - { - $listener = new SymfonyTestsListenerForV5(); - - $result = parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListenerForV5) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - - return $result; - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php deleted file mode 100644 index 6da7c65448532..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV6 extends BaseRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments) - { - $listener = new SymfonyTestsListener(); - - parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListener) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php deleted file mode 100644 index a175fb65d7f5a..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV7 extends BaseRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments): void - { - $listener = new SymfonyTestsListener(); - - parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListener) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - } -} diff --git a/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php b/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php deleted file mode 100644 index cda59209790d5..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\TextUI; - -if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV5', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV6', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} else { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV7', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} - -if (false) { - class TestRunner - { - } -} diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 2439c5bba6ac3..0c32779d1fac2 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -379,7 +379,7 @@ private function findWrongBundleOverrides(): array $path = ltrim($relativePath.$name, \DIRECTORY_SEPARATOR); $carry[$name] = $path; - @trigger_error(sprintf('Templates directory "%s" is deprecated since Symfony 4.2, use "%s" instead.', $absolutePath, $this->twigDefaultPath.'/bundles/'.$name), E_USER_DEPRECATED); + @trigger_error(sprintf('Loading Twig templates from the "%s" directory is deprecated since Symfony 4.2, use "%s" instead.', $absolutePath, $this->twigDefaultPath.'/bundles/'.$name), E_USER_DEPRECATED); } return $carry; diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php index 800d86c20ef0f..b7d059daea7c7 100644 --- a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php +++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -127,10 +127,12 @@ public function getHtmlCallGraph() '', '', '', + '', ], [ '', '', '', + '', ], $dump); return new Markup($dump, 'UTF-8'); diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index d281aa0fc7ffc..11e8f3f70b590 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -112,11 +112,10 @@ {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%}
- {{- form_label(form) -}} - {{- form_widget(form, widget_attr) -}} - {{- form_help(form) -}} - {{- form_errors(form) -}} -
+ {{- form_label(form) }} {# -#} + {{ form_widget(form, widget_attr) }} {# -#} + {{ form_errors(form) }} {# -#} + {# -#} {%- endblock form_row %} {% block button_row -%} diff --git a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php index 16f61a1e3ad21..cf46c8e620069 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php @@ -65,7 +65,7 @@ public function testWarningsWrongBundleOverriding() /** * @group legacy - * @expectedDeprecation Templates directory "%sResources/BarBundle/views" is deprecated since Symfony 4.2, use "%stemplates/bundles/BarBundle" instead. + * @expectedDeprecation Loading Twig templates from the "%sResources/BarBundle/views" directory is deprecated since Symfony 4.2, use "%stemplates/bundles/BarBundle" instead. */ public function testDeprecationForWrongBundleOverridingInLegacyPath() { diff --git a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php index 572685a6c4b35..71180156ba2b1 100644 --- a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php +++ b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @@ -30,9 +30,14 @@ public function testCompile($source, $expected) { $env = new Environment($this->getMockBuilder('Twig\Loader\LoaderInterface')->getMock(), ['cache' => false, 'autoescape' => false, 'optimizations' => 0]); $env->addTokenParser(new FormThemeTokenParser()); - $stream = $env->tokenize(new Source($source, '')); + $source = new Source($source, ''); + $stream = $env->tokenize($source); $parser = new Parser($env); + if (method_exists($expected, 'setSourceContext')) { + $expected->setSourceContext($source); + } + $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)); } diff --git a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg index ac4ead8fbbf0f..d6cedd8a19dc3 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg +++ b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg @@ -1,4 +1 @@ - - - - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index 0b284e61c09df..6f3650fe26ea6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -145,7 +145,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $viewsPaths = []; if (is_dir($dir = $rootDir.'/Resources/views')) { if ($dir !== $this->defaultViewsPath) { - $notice = sprintf('Storing templates in the "%s" directory is deprecated since Symfony 4.2, ', $dir); + $notice = sprintf('Loading Twig templates from the "%s" directory is deprecated since Symfony 4.2, ', $dir); @trigger_error($notice.($this->defaultViewsPath ? sprintf('use the "%s" directory instead.', $this->defaultViewsPath) : 'configure and use "twig.default_path" instead.'), E_USER_DEPRECATED); } $viewsPaths[] = $dir; @@ -173,7 +173,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } if (is_dir($dir = sprintf('%s/Resources/%s/views', $rootDir, $bundle->getName()))) { $viewsPaths[] = $dir; - $notice = sprintf('Storing templates for "%s" in the "%s" directory is deprecated since Symfony 4.2, ', $bundle->getName(), $dir); + $notice = sprintf('Loading Twig templates for "%s" from the "%s" directory is deprecated since Symfony 4.2, ', $bundle->getName(), $dir); @trigger_error($notice.($this->defaultViewsPath ? sprintf('use the "%s" directory instead.', $this->defaultViewsPath) : 'configure and use "twig.default_path" instead.'), E_USER_DEPRECATED); } } catch (\InvalidArgumentException $e) { @@ -191,7 +191,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $viewsPaths = [$path.'/templates']; if (is_dir($dir = $path.'/Resources/views')) { if ($dir !== $this->defaultViewsPath) { - @trigger_error(sprintf('Storing templates in the "%s" directory is deprecated since Symfony 4.2, use the "%s" directory instead.', $dir, $path.'/templates'), E_USER_DEPRECATED); + @trigger_error(sprintf('Loading Twig templates from the "%s" directory is deprecated since Symfony 4.2, use the "%s" directory instead.', $dir, $path.'/templates'), E_USER_DEPRECATED); } $viewsPaths[] = $dir; } @@ -211,7 +211,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $viewsPaths[] = $bundle->getPath().'/Resources/views'; if (is_dir($deprecatedPath = sprintf('%s/Resources/%s/views', $rootDir, $bundle->getName()))) { $viewsPaths[] = $deprecatedPath; - $notice = sprintf('Storing templates for "%s" in the "%s" directory is deprecated since Symfony 4.2, ', $bundle->getName(), $deprecatedPath); + $notice = sprintf('Loading Twig templates for "%s" from the "%s" directory is deprecated since Symfony 4.2, ', $bundle->getName(), $deprecatedPath); @trigger_error($notice.($this->defaultViewsPath ? sprintf('use the "%s" directory instead.', $this->defaultViewsPath) : 'configure and use "twig.default_path" instead.'), E_USER_DEPRECATED); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index 9ab6f9eb4980d..b6708db544506 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -14,14 +14,12 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; -@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2, use %s instead.', Controller::class, AbstractController::class), E_USER_DEPRECATED); - /** * Controller is a simple implementation of a Controller. * * It provides methods to common features needed in controllers. * - * @deprecated since Symfony 4.2, use {@see AbstractController} instead. + * @deprecated since Symfony 4.2, use "Symfony\Bundle\FrameworkBundle\Controller\AbstractController" instead. * * @author Fabien Potencier */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 4ca196e47d3ea..36d80501bd70e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -70,6 +70,9 @@ + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php index 400f994d2f6f2..cf44257d34a51 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php @@ -68,7 +68,7 @@ public function testDebugDefaultDirectory() /** * @group legacy * @expectedDeprecation Storing translations in the "%ssf_translation%s/Resources/translations" directory is deprecated since Symfony 4.2, use the "%ssf_translation%s/translations" directory instead. - * @expectedDeprecation Storing templates in the "%ssf_translation%s/Resources/views" directory is deprecated since Symfony 4.2, use the "%ssf_translation%s/templates" directory instead. + * @expectedDeprecation Loading Twig templates from the "%ssf_translation%s/Resources/views" directory is deprecated since Symfony 4.2, use the "%ssf_translation%s/templates" directory instead. */ public function testDebugLegacyDefaultDirectory() { diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg index 5bd2f3b8f3799..1110c10a037d2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg @@ -1,3 +1 @@ - - - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php index faec77550b2b2..d9e303198f50a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php @@ -126,14 +126,12 @@ public function testEncodePasswordOutput() public function testEncodePasswordEmptySaltOutput() { - $this->passwordEncoderCommandTester->execute( - [ - 'command' => 'security:encode-password', - 'password' => 'p@ssw0rd', - 'user-class' => 'Symfony\Component\Security\Core\User\User', - '--empty-salt' => true, - ] - ); + $this->passwordEncoderCommandTester->execute([ + 'command' => 'security:encode-password', + 'password' => 'p@ssw0rd', + 'user-class' => 'Symfony\Component\Security\Core\User\User', + '--empty-salt' => true, + ]); $this->assertContains('Password encoding succeeded', $this->passwordEncoderCommandTester->getDisplay()); $this->assertContains(' Encoded password p@ssw0rd', $this->passwordEncoderCommandTester->getDisplay()); diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index a9db7bf6a8612..1855e8331c934 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -117,7 +117,7 @@ public function load(array $configs, ContainerBuilder $container) if (file_exists($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) { if ($dir !== $defaultTwigPath) { - @trigger_error(sprintf('Templates directory "%s" is deprecated since Symfony 4.2, use "%s" instead.', $dir, $defaultTwigPath), E_USER_DEPRECATED); + @trigger_error(sprintf('Loading Twig templates from the "%s" directory is deprecated since Symfony 4.2, use "%s" instead.', $dir, $defaultTwigPath), E_USER_DEPRECATED); } $twigFilesystemLoaderDefinition->addMethodCall('addPath', [$dir]); @@ -172,7 +172,7 @@ private function getBundleTemplatePaths(ContainerBuilder $container, array $conf $defaultOverrideBundlePath = $container->getParameterBag()->resolveValue($config['default_path']).'/bundles/'.$name; if (file_exists($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views')) { - @trigger_error(sprintf('Templates directory "%s" is deprecated since Symfony 4.2, use "%s" instead.', $dir, $defaultOverrideBundlePath), E_USER_DEPRECATED); + @trigger_error(sprintf('Loading Twig templates for "%s" from the "%s" directory is deprecated since Symfony 4.2, use "%s" instead.', $name, $dir, $defaultOverrideBundlePath), E_USER_DEPRECATED); $bundleHierarchy[$name][] = $dir; } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig index 65306f2fd9c42..ffb24bde9ae31 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig @@ -25,10 +25,10 @@ {% if trace.file|default(false) %}
{{ trace.file|file_excerpt(trace.line, 5)|replace({ - '#DD0000': '#183691', - '#007700': '#a71d5d', - '#0000BB': '#222222', - '#FF8000': '#969896' + '#DD0000': 'var(--highlight-string)', + '#007700': 'var(--highlight-keyword)', + '#0000BB': 'var(--highlight-default)', + '#FF8000': 'var(--highlight-comment)' })|raw }}
{% endif %} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig index 6bdfb8839c9a7..8c1f9af2a0d18 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig @@ -23,8 +23,15 @@ --table-border: #e0e0e0; --table-background: #fff; --table-header: #e0e0e0; + --tree-active-background: #F7E5A1; + --exception-title-color: var(--base-2); --shadow: 0px 0px 1px rgba(128, 128, 128, .2); --border: 1px solid #e0e0e0; + --background-error: var(--color-error); + --highlight-comment: #969896; + --highlight-default: #222222; + --highlight-keyword: #a71d5d; + --highlight-string: #183691; --base-0: #fff; --base-1: #f5f5f5; --base-2: #e0e0e0; @@ -40,7 +47,7 @@ html { /* always display the vertical scrollbar to avoid jumps when toggling contents */ overflow-y: scroll; } -body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; padding-bottom: 45px; } +body { background-color: #F9F9F9; color: var(--base-6); font: 14px/1.4 Helvetica, Arial, sans-serif; padding-bottom: 45px; } a { cursor: pointer; text-decoration: none; } a:hover { text-decoration: underline; } @@ -49,9 +56,9 @@ abbr[title] { border-bottom: none; cursor: help; text-decoration: none; } code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; } table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; } -table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } -table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; } -table th { background-color: #E0E0E0; font-weight: bold; text-align: left; } +table { background: #FFF; border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } +table th, table td { border: solid var(--base-2); border-width: 1px 0; padding: 8px 10px; } +table th { background-color: var(--base-2); font-weight: bold; text-align: left; } .m-t-5 { margin-top: 5px; } .hidden-xs-down { display: none; } @@ -65,7 +72,7 @@ table th { background-color: #E0E0E0; font-weight: bold; text-align: left; } .text-small { font-size: 12px !important; } .text-muted { color: #999; } .text-bold { font-weight: bold; } -.empty { border: 4px dashed #E0E0E0; color: #999; margin: 1em 0; padding: .5em 2em; } +.empty { border: 4px dashed var(--base-2); color: #999; margin: 1em 0; padding: .5em 2em; } .status-success { background: rgba(94, 151, 110, 0.3); } .status-warning { background: rgba(240, 181, 24, 0.3); } @@ -73,7 +80,7 @@ table th { background-color: #E0E0E0; font-weight: bold; text-align: left; } .status-success td, .status-warning td, .status-error td { background: transparent; } tr.status-error td, tr.status-warning td { border-bottom: 1px solid #FAFAFA; border-top: 1px solid #FAFAFA; } .status-warning .colored { color: #A46A1F; } -.status-error .colored { color: #B0413E; } +.status-error .colored { color: var(--color-error); } .sf-toggle { cursor: pointer; } .sf-toggle-content { -moz-transition: display .25s ease; -webkit-transition: display .25s ease; transition: display .25s ease; } @@ -91,7 +98,7 @@ thead.sf-toggle-content.sf-toggle-visible, tbody.sf-toggle-content.sf-toggle-vis .tab-navigation li.active .badge { background-color: var(--base-5); color: var(--base-2); } .tab-content > *:first-child { margin-top: 0; } .tab-navigation li .badge.status-warning { background: var(--color-warning); color: #FFF; } -.tab-navigation li .badge.status-error { background: var(--color-error); color: #FFF; } +.tab-navigation li .badge.status-error { background: var(--background-error); color: #FFF; } .sf-tabs .tab:not(:first-child) { display: none; } [data-filters] { position: relative; } @@ -131,7 +138,7 @@ thead.sf-toggle-content.sf-toggle-visible, tbody.sf-toggle-content.sf-toggle-vis .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } .container::after { content: ""; display: table; clear: both; } -header { background-color: #222; color: rgba(255, 255, 255, 0.75); font-size: 13px; height: 33px; line-height: 33px; padding: 0; } +header { background-color: var(--base-6); color: rgba(255, 255, 255, 0.75); font-size: 13px; height: 33px; line-height: 33px; padding: 0; } header .container { display: flex; justify-content: space-between; } .logo { flex: 1; font-size: 13px; font-weight: normal; margin: 0; padding: 0; } .logo svg { height: 18px; width: 18px; opacity: .8; vertical-align: -5px; } @@ -142,7 +149,7 @@ header .container { display: flex; justify-content: space-between; } .help-link a:hover { color: #EEE; text-decoration: none; } .help-link a:hover svg { opacity: .9; } -.exception-summary { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 15px; } +.exception-summary { background: var(--background-error); border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 15px; } .exception-metadata { background: rgba(0, 0, 0, 0.1); padding: 7px 0; } .exception-metadata .container { display: flex; flex-direction: row; justify-content: space-between; } .exception-metadata h2, .exception-metadata h2 > a { color: rgba(255, 255, 255, 0.8); font-size: 13px; font-weight: 400; margin: 0; } @@ -162,36 +169,36 @@ header .container { display: flex; justify-content: space-between; } .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } .trace + .trace { margin-top: 30px; } -.trace-head { background-color: #e0e0e0; padding: 10px; position: relative; } -.trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } +.trace-head { background-color: var(--base-2); padding: 10px; position: relative; } +.trace-head .trace-class { color: var(--base-6); font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } .trace-head .trace-namespace { color: #999; display: block; font-size: 13px; } .trace-head .icon { position: absolute; right: 0; top: 0; } .trace-head .icon svg { height: 24px; width: 24px; } -.trace-details { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; table-layout: fixed; } +.trace-details { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; table-layout: fixed; } .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } .trace-line { position: relative; padding-top: 8px; padding-bottom: 8px; } -.trace-line + .trace-line { border-top: 1px solid #e0e0e0; } -.trace-line:hover { background: #F5F5F5; } -.trace-line a { color: #222; } +.trace-line + .trace-line { border-top: var(--border); } +.trace-line:hover { background: var(--base-1); } +.trace-line a { color: var(--base-6); } .trace-line .icon { opacity: .4; position: absolute; left: 10px; top: 11px; } .trace-line .icon svg { height: 16px; width: 16px; } .trace-line-header { padding-left: 36px; padding-right: 10px; } -.trace-file-path, .trace-file-path a { color: #222; font-size: 13px; } -.trace-class { color: #B0413E; } +.trace-file-path, .trace-file-path a { color: var(--base-6); font-size: 13px; } +.trace-class { color: var(--color-error); } .trace-type { padding: 0 2px; } -.trace-method { color: #B0413E; font-weight: bold; } +.trace-method { color: var(--color-error); font-weight: bold; } .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } -.trace-code { background: #FFF; font-size: 12px; margin: 10px 10px 2px 10px; padding: 10px; overflow-x: auto; white-space: nowrap; } +.trace-code { background: var(--base-0); font-size: 12px; margin: 10px 10px 2px 10px; padding: 10px; overflow-x: auto; white-space: nowrap; } .trace-code ol { margin: 0; float: left; } .trace-code li { color: #969896; margin: 0; padding-left: 10px; float: left; width: 100%; } .trace-code li + li { margin-top: 5px; } -.trace-code li.selected { background: #F7E5A1; margin-top: 2px; } -.trace-code li code { color: #222; white-space: nowrap; } +.trace-code li.selected { background: var(--trace-selected-background); margin-top: 2px; } +.trace-code li code { color: var(--base-6); white-space: nowrap; } .trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/images/chevron-right.svg b/src/Symfony/Bundle/TwigBundle/Resources/views/images/chevron-right.svg index 6f07d1b5b0bbc..6837aff18bd20 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/images/chevron-right.svg +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/images/chevron-right.svg @@ -1 +1 @@ - + diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/images/icon-plus-square.svg b/src/Symfony/Bundle/TwigBundle/Resources/views/images/icon-plus-square.svg index 8fbf7c4608d90..2f5c3b3583076 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/images/icon-plus-square.svg +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/images/icon-plus-square.svg @@ -1 +1 @@ - + diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-ghost.svg b/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-ghost.svg index 08234346851b0..58266bcbfaf38 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-ghost.svg +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-ghost.svg @@ -1 +1 @@ - + diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-logo.svg b/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-logo.svg index 785341864d00b..f10824ae96f6a 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-logo.svg +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/images/symfony-logo.svg @@ -1 +1 @@ - + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index 0a227930bd1ef..726b3ee977fe6 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -203,8 +203,8 @@ public function testTwigLoaderPaths($format) /** * @group legacy * @dataProvider getFormats - * @expectedDeprecation Templates directory "%s/Resources/TwigBundle/views" is deprecated since Symfony 4.2, use "%s/templates/bundles/TwigBundle" instead. - * @expectedDeprecation Templates directory "%s/Resources/views" is deprecated since Symfony 4.2, use "%s/templates" instead. + * @expectedDeprecation Loading Twig templates for "TwigBundle" from the "%s/Resources/TwigBundle/views" directory is deprecated since Symfony 4.2, use "%s/templates/bundles/TwigBundle" instead. + * @expectedDeprecation Loading Twig templates from the "%s/Resources/views" directory is deprecated since Symfony 4.2, use "%s/templates" instead. */ public function testLegacyTwigLoaderPaths($format) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig index c849cb29666ff..78752853b92da 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig @@ -10,8 +10,8 @@ } .exception-summary { - background: #FFF; - border: 1px solid #E0E0E0; + background: var(--base-0); + border: var(--border); box-shadow: 0 0 1px rgba(128, 128, 128, .2); margin: 1em 0; padding: 10px; @@ -21,7 +21,7 @@ } .exception-message { - color: #B0413E; + color: var(--color-error); } .exception-metadata, diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 720da85750526..4ca49e7c5ff90 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -131,8 +131,11 @@ .tree .tree-inner:hover { background: #dfdfdf; } + .tree .tree-inner:hover span:not(.has-error) { + color: var(--base-0); + } .tree .tree-inner.active, .tree .tree-inner.active:hover { - background: #E0E0E0; + background: var(--tree-active-background); font-weight: bold; } .tree .tree-inner.active .toggle-icon, .tree .tree-inner:hover .toggle-icon, .tree .tree-inner.active:hover .toggle-icon { @@ -153,7 +156,7 @@ } .badge-error { float: right; - background: #B0413E; + background: var(--background-error); color: #FFF; padding: 1px 4px; font-size: 10px; @@ -161,17 +164,17 @@ vertical-align: middle; } .has-error { - color: #B0413E; + color: var(--color-error); } .errors h3 { - color: #B0413E; + color: var(--color-error); } .errors th { - background: #B0413E; + background: var(--background-error); color: #FFF; } .errors .toggle-icon { - background-color: #B0413E; + background-color: var(--background-error); } h3 a, h3 a:hover, h3 a:focus { color: inherit; @@ -183,6 +186,20 @@ h3.form-data-type + h3 { margin-top: 1em; } + .theme-dark .toggle-icon { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAVUExURUdwTH+Ag0lNUZiYmGRmbP///zU5P2n9VV4AAAAFdFJOUwCv+yror0g1sQAAAE1JREFUGNNjSFM0YGBgEEpjSGEAAzcGBQiDiUEAwmBkMIAwmBmwgVAgQGWgA7h2uIFwK+CWwp1BpHtYA6DuATEYkBlY3IOmBq6dCPcAAKMtEEs3tfChAAAAAElFTkSuQmCC'); + } + .theme-dark .toggle-icon.empty { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAASUExURUdwTDI3OzQ5PS4uLjU3PzU5P4keoyIAAAAFdFJOUwBApgtzrnKGEwAAADJJREFUCNdjCFU0YGBgEAplCGEAA1cGBQiDiUEAwmBkMIAwmBnIA3DtcAPhVsAthTkDACsZBBmrTTSxAAAAAElFTkSuQmCC'); + } + .theme-dark .tree .tree-inner.active .toggle-icon, .theme-dark .tree .tree-inner:hover .toggle-icon, .theme-dark .tree .tree-inner.active:hover .toggle-icon { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAAD1BMVEVHcEx/gIOYmJiZmZn///+IJ2wIAAAAA3RSTlMAryoIUq0uAAAAUElEQVQY02NgYFQ2NjYWYGBgMAYDBgZmCMOAQRjCMGRQhjCMoEqAipAYLkCAykBXA9cONxBuBdxShDOIc4+JM9Q9IIYxMgOLe9DUwLUT4R4AznguG0qfEa0AAAAASUVORK5CYII='); + background-color: transparent; + } + .theme-dark .tree .tree-inner.active .toggle-icon.empty, .theme-dark .tree .tree-inner:hover .toggle-icon.empty, .theme-dark .tree .tree-inner.active:hover .toggle-icon.empty { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEVHcEwyNzuqqqrd9nIgAAAAAnRSTlMAQABPjKgAAAArSURBVAjXY2BctcqBgWvVqgUMWqtWrWDIWrVqJcMqICCGACsGawMbADIKANflJYEoGMqtAAAAAElFTkSuQmCC'); + background-color: transparent; + } {% endblock %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig index f3e8fecece384..779f1259edd01 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig @@ -58,6 +58,7 @@ .message-item tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; } td.message-bus-dispatch-caller { background: #f1f2f3; } + .theme-dark td.message-bus-dispatch-caller { background: var(--base-1); } {% endblock %} @@ -144,7 +145,12 @@ diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig index 57504af1f593d..47e04b9dac942 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -97,7 +97,11 @@

Execution timeline

- {% if collector.events is empty %} + {% if not collector.isStopwatchInstalled() %} +
+

The Stopwatch component is not installed. If you want to see timing events, run: composer require symfony/stopwatch.

+
+ {% elseif collector.events is empty %}

No timing events have been recorded. Check that symfony/stopwatch is installed and debugging enabled in the kernel.

diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/validator.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/validator.html.twig index 6153637026261..f1da1f714fb26 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/validator.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/validator.html.twig @@ -59,7 +59,12 @@ diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/ajax.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/ajax.svg index bd878c3c6c21f..4019e3249bb22 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/ajax.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/ajax.svg @@ -1,6 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/cache.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/cache.svg index 5b36ae37e0158..798198928a6fb 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/cache.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/cache.svg @@ -1,3 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/close.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/close.svg index 4eef3ee624ed2..6038d73f9ab5b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/close.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/close.svg @@ -1,5 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/config.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/config.svg index a407719e8c761..ba51407d12d32 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/config.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/config.svg @@ -1,3 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/event.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/event.svg index 898117ef94558..76eaa32453453 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/event.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/event.svg @@ -1,11 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/exception.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/exception.svg index aafe2e874b30c..0e4df2b23a23c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/exception.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/exception.svg @@ -1,15 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/form.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/form.svg index 84fa75b6c3bf5..e1307960bd568 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/form.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/form.svg @@ -1,6 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/forward.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/forward.svg index 8b92d448fefd6..28a960a5bf835 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/forward.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/forward.svg @@ -1,4 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/logger.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/logger.svg index 5c46bbb2e4957..ae8c5aae4447c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/logger.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/logger.svg @@ -1,7 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/memory.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/memory.svg index 104ba57aeac79..deb047fc4a0ba 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/memory.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/memory.svg @@ -1,5 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/menu.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/menu.svg index 3c863393bc1ab..afccc7f629bb5 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/menu.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/menu.svg @@ -1,3 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/messenger.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/messenger.svg index 601797add105e..3af5178135f42 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/messenger.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/messenger.svg @@ -1 +1 @@ - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/no.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/no.svg index c0bb768d40433..5ffc020f4efe8 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/no.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/no.svg @@ -1,5 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/redirect.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/redirect.svg index dcdd15c288bd8..8c329d052f2c1 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/redirect.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/redirect.svg @@ -1,10 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/request.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/request.svg index 68b092c6b8809..67d6c643fced5 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/request.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/request.svg @@ -1,16 +1 @@ - - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/router.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/router.svg index 3b7f94355b37d..e16c617ebe18c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/router.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/router.svg @@ -1,6 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/search.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/search.svg index 7c8724aab54c1..cae0a67f9120b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/search.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/search.svg @@ -1,7 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg index 30b1c2e9aad51..c3beff6c8ddfd 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg @@ -1,12 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/time.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/time.svg index 8ae615d38bc84..d49851d440be4 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/time.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/time.svg @@ -1,5 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/translation.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/translation.svg index b7aa1249304b2..735bb92c78b70 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/translation.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/translation.svg @@ -1,13 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg index 7ff26c146e632..4a6ef7ab320f0 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg @@ -1,3 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/validator.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/validator.svg index 0b60184f9def5..6a81d92daa786 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/validator.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/validator.svg @@ -1 +1 @@ - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/yes.svg b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/yes.svg index da650231d520d..dbbff93d007d7 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/yes.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/yes.svg @@ -1,5 +1 @@ - - - + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 6fdcc77a0ed75..3a5674ebbcc4b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -25,11 +25,20 @@ --metric-unit-color: #999; --metric-label-background: #e0e0e0; --metric-label-color: inherit; + --trace-selected-background: #F7E5A1; --table-border: #e0e0e0; --table-background: #fff; --table-header: #e0e0e0; + --info-background: #ddf; + --tree-active-background: #F7E5A1; + --exception-title-color: var(--base-2); --shadow: 0px 0px 1px rgba(128, 128, 128, .2); --border: 1px solid #e0e0e0; + --background-error: var(--color-error); + --highlight-comment: #969896; + --highlight-default: #222222; + --highlight-keyword: #a71d5d; + --highlight-string: #183691; --base-0: #fff; --base-1: #f5f5f5; --base-2: #e0e0e0; @@ -43,6 +52,7 @@ --page-background: #36393e; --color-text: #e0e0e0; --color-muted: #777; + --color-error: #d43934; --tab-background: #555; --tab-color: #ccc; --tab-active-background: #888; @@ -54,11 +64,20 @@ --metric-unit-color: #999; --metric-label-background: #777; --metric-label-color: #e0e0e0; + --trace-selected-background: #71663a; --table-border: #444; --table-background: #333; --table-header: #555; + --info-background: rgba(79, 148, 195, 0.5); + --tree-active-background: var(--metric-label-background); + --exception-title-color: var(--base-2); --shadow: 0px 0px 1px rgba(32, 32, 32, .2); --border: 1px solid #666; + --background-error: #b0413e; + --highlight-comment: #dedede; + --highlight-default: var(--base-6); + --highlight-keyword: #ff413c; + --highlight-string: #70a6fd; --base-0: #2e3136; --base-1: #444; --base-2: #666; @@ -325,7 +344,7 @@ table tbody td.num-col { } .label.status-success { background: var(--color-success); color: #FFF; } .label.status-warning { background: var(--color-warning); color: #FFF; } -.label.status-error { background: var(--color-error); color: #FFF; } +.label.status-error { background: var(--background-error); color: #FFF; } {# Metrics ------------------------------------------------------------------------- #} @@ -458,9 +477,9 @@ tr.status-warning td { } .highlight .keyword { color: #8959A8; font-weight: bold; } -.highlight .word { color: #222222; } +.highlight .word { color: var(--color-text); } .highlight .variable { color: #916319; } -.highlight .symbol { color: #222222; } +.highlight .symbol { color: var(--color-text); } .highlight .comment { color: #999999; } .highlight .backtick { color: #718C00; } .highlight .string { color: #718C00; } @@ -601,7 +620,7 @@ tr.status-warning td { #summary .status-success { background: var(--color-success); } #summary .status-warning { background: var(--color-warning); } -#summary .status-error { background: var(--color-error); } +#summary .status-error { background: var(--background-error); } #summary .status-success h2, #summary .status-success a, @@ -702,7 +721,7 @@ tr.status-warning td { } #sidebar-search .form-group input { background: #ccc; - border: 1px solid #999; + border: 1px solid var(--color-muted); color: #222; width: 120px; } @@ -815,7 +834,7 @@ tr.status-warning td { background: var(--color-warning); } #menu-profiler .label-status-error .count { - background: var(--color-error); + background: var(--background-error); } {# Timeline panel @@ -905,7 +924,7 @@ tr.status-warning td { margin-top: 0; } .tab-navigation li .badge.status-warning { background: var(--color-warning); color: #FFF; } -.tab-navigation li .badge.status-error { background: var(--color-error); color: #FFF; } +.tab-navigation li .badge.status-error { background: var(--background-error); color: #FFF; } .sf-tabs .tab:not(:first-child) { display: none; } @@ -964,8 +983,8 @@ tr.status-warning td { #twig-dump pre { font-size: 12px; line-height: 1.7; - background-color: #fff; - border: 1px solid #E0E0E0; + background-color: var(--base-0); + border: var(--border); padding: 15px; box-shadow: 0 0 1px rgba(128, 128, 128, .2); } @@ -973,9 +992,10 @@ tr.status-warning td { border-radius: 2px; padding: 1px 2px; } -#twig-dump .status-error { background: transparent; color: #B0413E; } +#twig-dump .status-error { background: transparent; color: var(--color-error); } #twig-dump .status-warning { background: rgba(240, 181, 24, 0.3); } #twig-dump .status-success { background: rgba(100, 189, 99, 0.2); } +#twig-dump .status-info { background: var(--info-background); } #twig-table tbody td { vertical-align: middle; @@ -1000,6 +1020,8 @@ table.logs .metadata { display: block; font-size: 12px; } +.theme-dark tr.status-error td, +.theme-dark tr.status-warning td { border-bottom: unset; border-top: unset; } {# Doctrine panel ========================================================================= #} @@ -1060,8 +1082,8 @@ table.logs .metadata { #collector-content .sf-validator .sf-validator-context, #collector-content .sf-validator .trace { - border: 1px solid #DDD; - background: #FFF; + border: var(--border); + background: var(--base-0); padding: 10px; margin: 0.5em 0; overflow: auto; @@ -1118,7 +1140,7 @@ pre.sf-dump, pre.sf-dump .sf-dump-default { } #collector-content .sf-dump pre.sf-dump, #collector-content .sf-dump .trace { - border: 1px solid #DDD; + border: var(--border); padding: 10px; margin: 0.5em 0; overflow: auto; 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 0230c84fb90d1..dd09415568d70 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -5,8 +5,6 @@ background-color: #222; border-top-left-radius: 4px; bottom: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; box-sizing: border-box; display: none; height: 36px; @@ -36,8 +34,6 @@ } .sf-toolbarreset * { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; box-sizing: content-box; vertical-align: baseline; letter-spacing: normal; @@ -371,21 +367,8 @@ div.sf-toolbar .sf-toolbar-block a:hover { text-align: right; } .sf-ajax-request-loading { - -webkit-animation: sf-blink .5s ease-in-out infinite; - -o-animation: sf-blink .5s ease-in-out infinite; - -moz-animation: sf-blink .5s ease-in-out infinite; animation: sf-blink .5s ease-in-out infinite; } -@-webkit-keyframes sf-blink { - 0% { background: #222; } - 50% { background: #444; } - 100% { background: #222; } -} -@-moz-keyframes sf-blink { - 0% { background: #222; } - 50% { background: #444; } - 100% { background: #222; } -} @keyframes sf-blink { 0% { background: #222; } 50% { background: #444; } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Resources/IconTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Resources/IconTest.php index 040d4003f5c54..c3d691d4d3dbf 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Resources/IconTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Resources/IconTest.php @@ -20,7 +20,7 @@ class IconTest extends TestCase */ public function testIconFileContents($iconFilePath) { - $this->assertRegExp('~.*~s', file_get_contents($iconFilePath), sprintf('The SVG metadata of the %s icon is different than expected (use the same as the other icons).', $iconFilePath)); + $this->assertRegExp('~.*~s', file_get_contents($iconFilePath), sprintf('The SVG metadata of the %s icon is different than expected (use the same as the other icons).', $iconFilePath)); } public function provideIconFilePaths() diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 79f4ddb596cc6..c43de7eef3dc2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.1.3", "symfony/config": "^4.2", - "symfony/http-kernel": "~4.2", + "symfony/http-kernel": "^4.2.6", "symfony/routing": "~3.4|~4.0", "symfony/twig-bundle": "~4.2", "symfony/var-dumper": "~3.4|~4.0", diff --git a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php index 8bc169872ef70..530d05abbaf66 100644 --- a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php @@ -778,6 +778,9 @@ public function testRestart() $this->assertEquals([], $client->getCookieJar()->all(), '->restart() clears the cookies'); } + /** + * @runInSeparateProcess + */ public function testInsulatedRequests() { $client = new TestClient(); diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index f7536b1ee250b..8340bdf034177 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -44,11 +44,17 @@ public function __construct(CacheItemPoolInterface $pool, string $namespace = '' function ($key, $innerItem) use ($defaultLifetime, $poolHash) { $item = new CacheItem(); $item->key = $key; + + if (null === $innerItem) { + return $item; + } + $item->value = $v = $innerItem->get(); $item->isHit = $innerItem->isHit(); - $item->defaultLifetime = $defaultLifetime; $item->innerItem = $innerItem; + $item->defaultLifetime = $defaultLifetime; $item->poolHash = $poolHash; + // Detect wrapped values that encode for their expiry and creation duration // For compactness, these values are packed in the key of an array using // magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F @@ -202,7 +208,18 @@ private function doSave(CacheItemInterface $item, $method) if (null === $item["\0*\0expiry"] && 0 < $item["\0*\0defaultLifetime"]) { $item["\0*\0expiry"] = microtime(true) + $item["\0*\0defaultLifetime"]; } - $innerItem = $item["\0*\0poolHash"] === $this->poolHash ? $item["\0*\0innerItem"] : $this->pool->getItem($this->namespace.$item["\0*\0key"]); + + if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) { + $innerItem = $item["\0*\0innerItem"]; + } elseif ($this->pool instanceof AdapterInterface) { + // this is an optimization specific for AdapterInterface implementations + // so we can save a round-trip to the backend by just creating a new item + $f = $this->createCacheItem; + $innerItem = $f($this->namespace.$item["\0*\0key"], null); + } else { + $innerItem = $this->pool->getItem($this->namespace.$item["\0*\0key"]); + } + ($this->setInnerItem)($innerItem, $item); return $this->pool->$method($innerItem); diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index e54044b60a376..5b08418fccf34 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -51,7 +51,6 @@ function ($key, $value, CacheItem $protoItem) { $item->value = $value; $item->defaultLifetime = $protoItem->defaultLifetime; $item->expiry = $protoItem->expiry; - $item->innerItem = $protoItem->innerItem; $item->poolHash = $protoItem->poolHash; return $item; diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php new file mode 100644 index 0000000000000..b11c1f2870545 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php @@ -0,0 +1,38 @@ +getItem('foo'); + $item->tag(['tag1', 'tag2']); + $item->set('bar'); + $cache->save($item); + + $this->assertSame('bar', $cache->getItem('foo')->get()); + } + + public function dataProvider() + { + return [ + [new ArrayAdapter()], + // also testing with a non-AdapterInterface implementation + // because the ProxyAdapter behaves slightly different for those + [new ExternalAdapter()], + ]; + } +} diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index a333f45bd6205..2b6db373d8f11 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -263,8 +263,12 @@ private function applyCurrentStyle(string $text, string $current, int $width, in } $lines = explode("\n", $text); - if ($width === $currentLineLength = \strlen(end($lines))) { - $currentLineLength = 0; + + foreach ($lines as $line) { + $currentLineLength += \strlen($line); + if ($width <= $currentLineLength) { + $currentLineLength = 0; + } } if ($this->isDecorated()) { diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index cf6447a4bcb50..b8b76833a6f3f 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -49,7 +49,13 @@ public function ask(InputInterface $input, OutputInterface $output, Question $qu if (!$input->isInteractive()) { $default = $question->getDefault(); - if (null !== $default && $question instanceof ChoiceQuestion) { + if (null === $default) { + return $default; + } + + if ($validator = $question->getValidator()) { + return \call_user_func($question->getValidator(), $default); + } elseif ($question instanceof ChoiceQuestion) { $choices = $question->getChoices(); if (!$question->isMultiselect()) { @@ -215,6 +221,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { + shell_exec(sprintf('stty %s', $sttyMode)); throw new RuntimeException('Aborted.'); } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 99aa83d4c8173..ce759953f31b4 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface; use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; @@ -522,16 +523,20 @@ private function buildTableRows($rows) // Remove any new line breaks and replace it with a new line foreach ($rows[$rowKey] as $column => $cell) { + $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1; + if (isset($this->columnMaxWidths[$column]) && Helper::strlenWithoutDecoration($formatter, $cell) > $this->columnMaxWidths[$column]) { - $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column]); + $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan); } if (!strstr($cell, "\n")) { continue; } + $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell))); + $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped; $lines = explode("\n", str_replace("\n", "\n", $cell)); foreach ($lines as $lineKey => $line) { - if ($cell instanceof TableCell) { - $line = new TableCell($line, ['colspan' => $cell->getColspan()]); + if ($colspan > 1) { + $line = new TableCell($line, ['colspan' => $colspan]); } if (0 === $lineKey) { $rows[$rowKey][$column] = $line; diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index e9b1be32a647b..efbe2a81fdbcc 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -41,6 +41,21 @@ class ApplicationTest extends TestCase { protected static $fixturesPath; + private $colSize; + + protected function setUp() + { + $this->colSize = getenv('COLUMNS'); + } + + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + } + public static function setUpBeforeClass() { self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); @@ -384,6 +399,7 @@ public function testFindWithCommandLoader() */ public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) { + putenv('COLUMNS=120'); if (method_exists($this, 'expectException')) { $this->expectException('Symfony\Component\Console\Exception\CommandNotFoundException'); $this->expectExceptionMessage($expectedExceptionMessage); @@ -515,6 +531,7 @@ public function provideInvalidCommandNamesSingle() public function testFindAlternativeExceptionMessageMultiple() { + putenv('COLUMNS=120'); $application = new Application(); $application->add(new \FooCommand()); $application->add(new \Foo1Command()); @@ -1740,13 +1757,6 @@ public function testThrowingErrorListener() $tester = new ApplicationTester($application); $tester->run(['command' => 'foo']); } - - protected function tearDown() - { - putenv('SHELL_VERBOSITY'); - unset($_ENV['SHELL_VERBOSITY']); - unset($_SERVER['SHELL_VERBOSITY']); - } } class CustomApplication extends Application diff --git a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php index 3a7906173862c..ce81c353f08cc 100644 --- a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php +++ b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php @@ -332,6 +332,9 @@ public function testFormatAndWrap() $this->assertSame("pre\e[37;41m\e[39;49m\n\e[37;41mfoo\e[39;49m\n\e[37;41mbar\e[39;49m\n\e[37;41mbaz\e[39;49m\npos\nt", $formatter->formatAndWrap('pre foo bar baz post', 3)); $this->assertSame("pre \e[37;41m\e[39;49m\n\e[37;41mfoo \e[39;49m\n\e[37;41mbar \e[39;49m\n\e[37;41mbaz\e[39;49m \npost", $formatter->formatAndWrap('pre foo bar baz post', 4)); $this->assertSame("pre \e[37;41mf\e[39;49m\n\e[37;41moo ba\e[39;49m\n\e[37;41mr baz\e[39;49m\npost", $formatter->formatAndWrap('pre foo bar baz post', 5)); + $this->assertSame("Lore\nm \e[37;41mip\e[39;49m\n\e[37;41msum\e[39;49m \ndolo\nr \e[32msi\e[39m\n\e[32mt\e[39m am\net", $formatter->formatAndWrap('Lorem ipsum dolor sit amet', 4)); + $this->assertSame("Lorem \e[37;41mip\e[39;49m\n\e[37;41msum\e[39;49m dolo\nr \e[32msit\e[39m am\net", $formatter->formatAndWrap('Lorem ipsum dolor sit amet', 8)); + $this->assertSame("Lorem \e[37;41mipsum\e[39;49m dolor \e[32m\e[39m\n\e[32msit\e[39m, \e[37;41mamet\e[39;49m et \e[32mlauda\e[39m\n\e[32mntium\e[39m architecto", $formatter->formatAndWrap('Lorem ipsum dolor sit, amet et laudantium architecto', 18)); $formatter = new OutputFormatter(); diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index d4aa3e9f367c6..b0c834cc41f0e 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -23,6 +23,19 @@ */ class ProgressBarTest extends TestCase { + private $colSize; + + protected function setUp() + { + $this->colSize = getenv('COLUMNS'); + putenv('COLUMNS=120'); + } + + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); + } + public function testMultipleStart() { $bar = new ProgressBar($output = $this->getOutputStream()); diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index e3edbed02973f..69d5470b8c20c 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -137,6 +137,9 @@ public function testAskChoiceNonInteractive() $question->setMultiselect(true); $this->assertNull($questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); + $question = new ChoiceQuestion('Who are your favorite superheros?', ['a' => 'Batman', 'b' => 'Superman'], 'a'); + $this->assertSame('a', $questionHelper->ask($this->createStreamableInputInterfaceMock('', false), $this->createOutputInterface(), $question), 'ChoiceQuestion validator returns the key if it\'s a string'); + try { $question = new ChoiceQuestion('Who are your favorite superheros?', $heroes, ''); $question->setMultiselect(true); diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index 43b90fd073c91..5eef8d60e0a10 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -1072,6 +1072,26 @@ public function testColumnMaxWidths() | | ities | | | +---------------+-------+------------+-----------------+ +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnMaxWidthsWithTrailingBackslash() + { + (new Table($output = $this->getOutputStream())) + ->setColumnMaxWidth(0, 5) + ->setRows([['1234\6']]) + ->render() + ; + + $expected = + <<<'TABLE' ++-------+ +| 1234\ | +| 6 | ++-------+ + TABLE; $this->assertEquals($expected, $this->getOutputContent($output)); @@ -1124,4 +1144,56 @@ protected function getOutputContent(StreamOutput $output) return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); } + + public function testWithColspanAndMaxWith(): void + { + $table = new Table($output = $this->getOutputStream()); + + $table->setColumnMaxWidth(0, 15); + $table->setColumnMaxWidth(1, 15); + $table->setColumnMaxWidth(2, 15); + $table->setRows([ + [new TableCell('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor', ['colspan' => 3])], + new TableSeparator(), + [new TableCell('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor', ['colspan' => 3])], + new TableSeparator(), + [new TableCell('Lorem ipsum dolor sit amet, consectetur ', ['colspan' => 2]), 'hello world'], + new TableSeparator(), + ['hello world', new TableCell('Lorem ipsum dolor sit amet, consectetur adipiscing elit', ['colspan' => 2])], + new TableSeparator(), + ['hello ', new TableCell('world', ['colspan' => 1]), 'Lorem ipsum dolor sit amet, consectetur'], + new TableSeparator(), + ['Symfony ', new TableCell('Test', ['colspan' => 1]), 'Lorem ipsum dolor sit amet, consectetur'], + ]) + ; + $table->render(); + + $expected = + <<assertSame($expected, $this->getOutputContent($output)); + } } diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php index e6e061f4416b6..88d00c8a9926b 100644 --- a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php +++ b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php @@ -26,9 +26,11 @@ class SymfonyStyleTest extends TestCase protected $command; /** @var CommandTester */ protected $tester; + private $colSize; protected function setUp() { + $this->colSize = getenv('COLUMNS'); putenv('COLUMNS=121'); $this->command = new Command('sfstyle'); $this->tester = new CommandTester($this->command); @@ -36,7 +38,7 @@ protected function setUp() protected function tearDown() { - putenv('COLUMNS'); + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); $this->command = null; $this->tester = null; } diff --git a/src/Symfony/Component/Console/Tests/TerminalTest.php b/src/Symfony/Component/Console/Tests/TerminalTest.php index 91af1d0ab459d..93b8c44a78158 100644 --- a/src/Symfony/Component/Console/Tests/TerminalTest.php +++ b/src/Symfony/Component/Console/Tests/TerminalTest.php @@ -16,6 +16,21 @@ class TerminalTest extends TestCase { + private $colSize; + private $lineSize; + + protected function setUp() + { + $this->colSize = getenv('COLUMNS'); + $this->lineSize = getenv('LINES'); + } + + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); + putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize); + } + public function test() { putenv('COLUMNS=100'); diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index 5457a0d792499..2cad1df9bee11 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -373,7 +373,7 @@ private function formatPath($path, $line) $fmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); if (!$fmt) { - return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); + return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); } if (\is_string($fmt)) { @@ -389,7 +389,11 @@ private function formatPath($path, $line) $link = strtr($fmt[0], ['%f' => $path, '%l' => $line]); } else { - $link = $fmt->format($path, $line); + try { + $link = $fmt->format($path, $line); + } catch (\Exception $e) { + return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); + } } return sprintf('in %s%s', $this->escapeHtml($link), $file, 0 < $line ? ' line '.$line : ''); @@ -436,6 +440,6 @@ private function escapeHtml($str) private function getSymfonyGhostAsSvg() { - return ''; + return ''; } } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index cf23365d4f0df..43da7f27661c8 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -481,7 +481,7 @@ public function testHandleFatalError() public function testHandleErrorException() { - $exception = new \Error("Class 'Foo' not found"); + $exception = new \Error("Class 'IReallyReallyDoNotExistAnywhereInTheRepositoryISwear' not found"); $handler = new ErrorHandler(); $handler->setExceptionHandler(function () use (&$args) { @@ -491,7 +491,7 @@ public function testHandleErrorException() $handler->handleException($exception); $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); - $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); + $this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); } /** diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index f6995cc355031..a105995819258 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -159,7 +159,15 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot): if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; } else { - $reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method); + $definition = new Definition($reflectionClass->name); + try { + $reflectionMethod = $this->getReflectionMethod($definition, $method); + } catch (RuntimeException $e) { + if ($definition->getFactory()) { + continue; + } + throw $e; + } } $arguments = $this->autowireMethod($reflectionMethod, $arguments); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index 6aeb65d0e635c..3e7e3e3b951a6 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -60,6 +60,7 @@ public function process(ContainerBuilder $container) $analyzedContainer = $container; } try { + $remainingInlinedIds = []; $this->connectedIds = $this->notInlinedIds = $container->getDefinitions(); do { if ($this->analyzingPass) { @@ -83,8 +84,10 @@ public function process(ContainerBuilder $container) } } - foreach ($this->inlinedIds as $id => $isPublic) { - if (!$isPublic) { + foreach ($this->inlinedIds as $id => $isPublicOrNotShared) { + if ($isPublicOrNotShared) { + $remainingInlinedIds[$id] = $id; + } else { $container->removeDefinition($id); $analyzedContainer->removeDefinition($id); } @@ -94,6 +97,14 @@ public function process(ContainerBuilder $container) if ($this->inlinedIds && $this->repeatedPass) { $this->repeatedPass->setRepeat(); } + + foreach ($remainingInlinedIds as $id) { + $definition = $container->getDefinition($id); + + if (!$definition->isShared() && !$definition->isPublic()) { + $container->removeDefinition($id); + } + } } finally { $this->container = null; $this->connectedIds = $this->notInlinedIds = $this->inlinedIds = []; @@ -131,7 +142,7 @@ protected function processValue($value, $isRoot = false) } $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); - $this->inlinedIds[$id] = $definition->isPublic(); + $this->inlinedIds[$id] = $definition->isPublic() || !$definition->isShared(); $this->notInlinedIds[$this->currentId] = true; if ($definition->isShared()) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index 2238779a25e2c..9e064ec123cc4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -34,6 +34,8 @@ class ResolveBindingsPass extends AbstractRecursivePass */ public function process(ContainerBuilder $container) { + $this->usedBindings = $container->getRemovedBindingIds(); + try { parent::process($container); @@ -115,7 +117,14 @@ protected function processValue($value, $isRoot = false) if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; } else { - $reflectionMethod = $this->getReflectionMethod($value, $method); + try { + $reflectionMethod = $this->getReflectionMethod($value, $method); + } catch (RuntimeException $e) { + if ($value->getFactory()) { + continue; + } + throw $e; + } } foreach ($reflectionMethod->getParameters() as $key => $parameter) { diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 172654eb41e32..dde0af7653025 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -124,6 +124,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface private $removedIds = []; + private $removedBindingIds = []; + private static $internalTypes = [ 'int' => true, 'float' => true, @@ -1460,6 +1462,35 @@ public function log(CompilerPassInterface $pass, string $message) $this->getCompiler()->log($pass, $this->resolveEnvPlaceholders($message)); } + /** + * Gets removed binding ids. + * + * @return array + * + * @internal + */ + public function getRemovedBindingIds() + { + return $this->removedBindingIds; + } + + /** + * Adds a removed binding id. + * + * @param int $id + * + * @internal + */ + public function addRemovedBindingIds($id) + { + if ($this->hasDefinition($id)) { + foreach ($this->getDefinition($id)->getBindings() as $key => $binding) { + list(, $bindingId) = $binding->getValues(); + $this->removedBindingIds[(int) $bindingId] = true; + } + } + } + /** * Returns the Service Conditionals. * diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php index 8fc60fbd376bd..066baa662121d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServiceConfigurator.php @@ -59,6 +59,8 @@ public function __destruct() { parent::__destruct(); + $this->container->addRemovedBindingIds($this->id); + if (!$this->definition instanceof ChildDefinition) { $this->container->setDefinition($this->id, $this->definition->setInstanceofConditionals($this->instanceof)); } else { diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index 62ebbde7f5cae..0ccaf22e399b2 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -91,6 +91,8 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e */ protected function setDefinition($id, Definition $definition) { + $this->container->addRemovedBindingIds($id); + if ($this->isLoadingInstanceof) { if (!$definition instanceof ChildDefinition) { throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, \get_class($definition))); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 558d6412a85cc..84dd0bbccc7cd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic; use Symfony\Component\DependencyInjection\TypedReference; @@ -546,6 +547,22 @@ public function testSetterInjection() ); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @exceptedExceptionMessage Invalid service "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy": method "setLogger()" does not exist. + */ + public function testWithNonExistingSetterAndAutowiring() + { + $container = new ContainerBuilder(); + + $definition = $container->register(CaseSensitiveClass::class, CaseSensitiveClass::class)->setAutowired(true); + $definition->addMethodCall('setLogger'); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); + } + public function testExplicitMethodInjection() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index 43dcb4f8f162b..7bbecf6207f46 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; @@ -112,27 +113,23 @@ public function testScalarSetter() $this->assertEquals([['setDefaultLocale', ['fr']]], $definition->getMethodCalls()); } - public function testTupleBinding() + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @exceptedExceptionMessage Invalid service "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy": method "setLogger()" does not exist. + */ + public function testWithNonExistingSetterAndBinding() { $container = new ContainerBuilder(); $bindings = [ - '$c' => new BoundArgument(new Reference('bar')), - CaseSensitiveClass::class.'$c' => new BoundArgument(new Reference('foo')), + '$c' => (new Definition('logger'))->setFactory('logger'), ]; $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); - $definition->addMethodCall('setSensitiveClass'); - $definition->addMethodCall('setAnotherC'); + $definition->addMethodCall('setLogger'); $definition->setBindings($bindings); $pass = new ResolveBindingsPass(); $pass->process($container); - - $expected = [ - ['setSensitiveClass', [new Reference('foo')]], - ['setAnotherC', [new Reference('bar')]], - ]; - $this->assertEquals($expected, $definition->getMethodCalls()); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php index 024f7adb46531..5070f09dbbe9a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php @@ -180,16 +180,14 @@ public function testVariadics() $container = new ContainerBuilder(); $definition = $container->register(NamedArgumentsVariadicsDummy::class, NamedArgumentsVariadicsDummy::class); - $definition->setArguments( - [ - '$class' => new \stdClass(), - '$variadics' => [ - new Reference('foo'), - new Reference('bar'), - new Reference('baz'), - ], - ] - ); + $definition->setArguments([ + '$class' => new \stdClass(), + '$variadics' => [ + new Reference('foo'), + new Reference('bar'), + new Reference('baz'), + ], + ]); $pass = new ResolveNamedArgumentsPass(); $pass->process($container); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Bar.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Bar.php index 83f2da12a70ab..1aaca2f1560c9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Bar.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Bar.php @@ -13,8 +13,11 @@ class Bar implements BarInterface { + public $quz; + public function __construct($quz = null, \NonExistent $nonExistent = null, BarInterface $decorated = null, array $foo = []) { + $this->quz = $quz; } public static function create(\NonExistent $nonExistent = null, $factory = null) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/defaults_bindings.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/defaults_bindings.xml new file mode 100644 index 0000000000000..d943dfad2d7f7 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/defaults_bindings.xml @@ -0,0 +1,15 @@ + + + + + value + + value + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/defaults_bindings2.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/defaults_bindings2.xml new file mode 100644 index 0000000000000..db41330f854eb --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/defaults_bindings2.xml @@ -0,0 +1,10 @@ + + + + + overridden + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/defaults_bindings.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/defaults_bindings.yml new file mode 100644 index 0000000000000..ca5e5048de122 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/defaults_bindings.yml @@ -0,0 +1,11 @@ +services: + _defaults: + bind: + $quz: value + $foo: [value] + + bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar + + foo: + class: stdClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/defaults_bindings2.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/defaults_bindings2.yml new file mode 100644 index 0000000000000..01fc5af62458c --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/defaults_bindings2.yml @@ -0,0 +1,7 @@ +services: + _defaults: + bind: + $quz: overridden + + bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 32a7fa4af2b9f..57b9c11bf6ff9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Config\Resource\GlobResource; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\Loader\IniFileLoader; @@ -863,4 +864,20 @@ public function testTsantosContainer() $dump = $dumper->dump(); $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_tsantos.php', $dumper->dump()); } + + /** + * The pass may throw an exception, which will cause the test to fail. + */ + public function testOverriddenDefaultsBindings() + { + $container = new ContainerBuilder(); + + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('defaults_bindings.xml'); + $loader->load('defaults_bindings2.xml'); + + (new ResolveBindingsPass())->process($container); + + $this->assertSame('overridden', $container->get('bar')->quz); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index b3090786cf84b..836ce993c1da8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Config\Resource\GlobResource; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\IniFileLoader; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; @@ -781,4 +782,20 @@ public function testServiceWithSameNameAsInterfaceAndFactoryIsNotTagged() $tagged = $container->findTaggedServiceIds('bar'); $this->assertCount(1, $tagged); } + + /** + * The pass may throw an exception, which will cause the test to fail. + */ + public function testOverriddenDefaultsBindings() + { + $container = new ContainerBuilder(); + + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('defaults_bindings.yml'); + $loader->load('defaults_bindings2.yml'); + + (new ResolveBindingsPass())->process($container); + + $this->assertSame('overridden', $container->get('bar')->quz); + } } diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 513d87c7e8188..9ec9a3469684d 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -304,10 +304,6 @@ private function postProcess($eventName) if (null !== $this->logger) { $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context); } - - if (!isset($this->called[$eventName])) { - $this->called[$eventName] = new \SplObjectStorage(); - } } else { $this->callStack->detach($listener); } diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index 9217cb7190b53..5eedf29581a6a 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -883,6 +883,10 @@ public function testInWithNonDirectoryGlob() public function testInWithGlobBrace() { + if (!\defined('GLOB_BRACE')) { + $this->markTestSkipped('Glob brace is not supported on this system.'); + } + $finder = $this->buildFinder(); $finder->in([__DIR__.'/Fixtures/{A,copy/A}/B/C'])->getIterator(); diff --git a/src/Symfony/Component/Form/AbstractExtension.php b/src/Symfony/Component/Form/AbstractExtension.php index 5b437344e894a..c49edb7c70084 100644 --- a/src/Symfony/Component/Form/AbstractExtension.php +++ b/src/Symfony/Component/Form/AbstractExtension.php @@ -36,7 +36,7 @@ abstract class AbstractExtension implements FormExtensionInterface /** * The type guesser provided by this extension. * - * @var FormTypeGuesserInterface + * @var FormTypeGuesserInterface|null */ private $typeGuesser; @@ -136,7 +136,7 @@ protected function loadTypeExtensions() /** * Registers the type guesser. * - * @return FormTypeGuesserInterface|null A type guesser + * @return FormTypeGuesserInterface|null */ protected function loadTypeGuesser() { diff --git a/src/Symfony/Component/Form/AbstractRendererEngine.php b/src/Symfony/Component/Form/AbstractRendererEngine.php index 5eebb20ac1eb1..e0954c9537aac 100644 --- a/src/Symfony/Component/Form/AbstractRendererEngine.php +++ b/src/Symfony/Component/Form/AbstractRendererEngine.php @@ -131,7 +131,7 @@ abstract protected function loadResourceForBlockName($cacheKey, FormView $view, * resource * @param FormView $view The form view for finding the applying * themes - * @param array $blockNameHierarchy The block hierarchy, with the most + * @param string[] $blockNameHierarchy The block hierarchy, with the most * specific block name at the end * @param int $hierarchyLevel The level in the block hierarchy that * should be loaded diff --git a/src/Symfony/Component/Form/Button.php b/src/Symfony/Component/Form/Button.php index 8ae4946c565e0..ed1106b467a21 100644 --- a/src/Symfony/Component/Form/Button.php +++ b/src/Symfony/Component/Form/Button.php @@ -22,7 +22,7 @@ class Button implements \IteratorAggregate, FormInterface { /** - * @var FormInterface|null + * @var FormInterface */ private $parent; @@ -111,6 +111,8 @@ public function setParent(FormInterface $parent = null) } $this->parent = $parent; + + return $this; } /** @@ -199,11 +201,13 @@ public function getErrors($deep = false, $flatten = true) * This method should not be invoked. * * @param mixed $modelData + * + * @return $this */ public function setData($modelData) { - // called during initialization of the form tree - // noop + // no-op, called during initialization of the form tree + return $this; } /** @@ -211,6 +215,7 @@ public function setData($modelData) */ public function getData() { + return null; } /** @@ -218,6 +223,7 @@ public function getData() */ public function getNormData() { + return null; } /** @@ -225,6 +231,7 @@ public function getNormData() */ public function getViewData() { + return null; } /** @@ -240,7 +247,7 @@ public function getExtraData() /** * Returns the button's configuration. * - * @return FormConfigInterface The configuration + * @return FormConfigInterface The configuration instance */ public function getConfig() { @@ -272,6 +279,7 @@ public function getName() */ public function getPropertyPath() { + return null; } /** @@ -309,11 +317,11 @@ public function isRequired() */ public function isDisabled() { - if (null === $this->parent || !$this->parent->isDisabled()) { - return $this->config->getDisabled(); + if ($this->parent && $this->parent->isDisabled()) { + return true; } - return true; + return $this->config->getDisabled(); } /** @@ -341,6 +349,7 @@ public function isSynchronized() */ public function getTransformationFailure() { + return null; } /** @@ -368,7 +377,7 @@ public function handleRequest($request = null) /** * Submits data to the button. * - * @param string|null $submittedData The data + * @param string|null $submittedData Not used * @param bool $clearMissing Not used * * @return $this diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php index 27d8e70f2bc65..2373b248b7cc8 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -22,9 +22,6 @@ */ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface { - /** - * @var bool - */ protected $locked = false; /** @@ -518,6 +515,7 @@ public function getFormConfig() */ public function getEventDispatcher() { + return null; } /** @@ -533,6 +531,7 @@ public function getName() */ public function getPropertyPath() { + return null; } /** @@ -600,6 +599,7 @@ public function getModelTransformers() */ public function getDataMapper() { + return null; } /** @@ -637,6 +637,7 @@ public function getErrorBubbling() */ public function getEmptyData() { + return null; } /** @@ -679,6 +680,7 @@ public function getAttribute($name, $default = null) */ public function getData() { + return null; } /** @@ -686,6 +688,7 @@ public function getData() */ public function getDataClass() { + return null; } /** @@ -703,6 +706,7 @@ public function getDataLocked() */ public function getFormFactory() { + throw new BadMethodCallException('Buttons do not support adding children.'); } /** @@ -710,6 +714,7 @@ public function getFormFactory() */ public function getAction() { + return null; } /** @@ -717,6 +722,7 @@ public function getAction() */ public function getMethod() { + return null; } /** @@ -724,6 +730,7 @@ public function getMethod() */ public function getRequestHandler() { + return null; } /** diff --git a/src/Symfony/Component/Form/CallbackTransformer.php b/src/Symfony/Component/Form/CallbackTransformer.php index 919efabd10ed8..5125214ff959c 100644 --- a/src/Symfony/Component/Form/CallbackTransformer.php +++ b/src/Symfony/Component/Form/CallbackTransformer.php @@ -11,9 +11,6 @@ namespace Symfony\Component\Form; -use Symfony\Component\Form\Exception\TransformationFailedException; -use Symfony\Component\Form\Exception\UnexpectedTypeException; - class CallbackTransformer implements DataTransformerInterface { private $transform; @@ -30,14 +27,7 @@ public function __construct(callable $transform, callable $reverseTransform) } /** - * Transforms a value from the original representation to a transformed representation. - * - * @param mixed $data The value in the original representation - * - * @return mixed The value in the transformed representation - * - * @throws UnexpectedTypeException when the argument is not of the expected type - * @throws TransformationFailedException when the transformation fails + * {@inheritdoc} */ public function transform($data) { @@ -45,15 +35,7 @@ public function transform($data) } /** - * Transforms a value from the transformed representation to its original - * representation. - * - * @param mixed $data The value in the transformed representation - * - * @return mixed The value in the original representation - * - * @throws UnexpectedTypeException when the argument is not of the expected type - * @throws TransformationFailedException when the transformation fails + * {@inheritdoc} */ public function reverseTransform($data) { diff --git a/src/Symfony/Component/Form/DataMapperInterface.php b/src/Symfony/Component/Form/DataMapperInterface.php index bb262e7b8e6b8..dee8f784910ea 100644 --- a/src/Symfony/Component/Form/DataMapperInterface.php +++ b/src/Symfony/Component/Form/DataMapperInterface.php @@ -17,22 +17,46 @@ interface DataMapperInterface { /** - * Maps properties of some data to a list of forms. + * Maps the view data of a compound form to its children. * - * @param mixed $data Structured data - * @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances + * The method is responsible for calling {@link FormInterface::setData()} + * on the children of compound forms, defining their underlying model data. + * + * @param mixed $viewData View data of the compound form being initialized + * @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances * * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported */ - public function mapDataToForms($data, $forms); + public function mapDataToForms($viewData, $forms); /** - * Maps the data of a list of forms into the properties of some data. + * Maps the model data of a list of children forms into the view data of their parent. + * + * This is the internal cascade call of FormInterface::submit for compound forms, since they + * cannot be bound to any input nor the request as scalar, but their children may: + * + * $compoundForm->submit($arrayOfChildrenViewData) + * // inside: + * $childForm->submit($childViewData); + * // for each entry, do the same and/or reverse transform + * $this->dataMapper->mapFormsToData($compoundForm, $compoundInitialViewData) + * // then reverse transform + * + * When a simple form is submitted the following is happening: + * + * $simpleForm->submit($submittedViewData) + * // inside: + * $this->viewData = $submittedViewData + * // then reverse transform + * + * The model data can be an array or an object, so this second argument is always passed + * by reference. * - * @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances - * @param mixed $data Structured data + * @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances + * @param mixed $viewData The compound form's view data that get mapped + * its children model data * * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported */ - public function mapFormsToData($forms, &$data); + public function mapFormsToData($forms, &$viewData); } diff --git a/src/Symfony/Component/Form/DataTransformerInterface.php b/src/Symfony/Component/Form/DataTransformerInterface.php index deb073c8128fe..e5ac5992944e5 100644 --- a/src/Symfony/Component/Form/DataTransformerInterface.php +++ b/src/Symfony/Component/Form/DataTransformerInterface.php @@ -23,23 +23,35 @@ interface DataTransformerInterface /** * Transforms a value from the original representation to a transformed representation. * - * This method is called on two occasions inside a form field: + * This method is called when the form field is initialized with its default data, on + * two occasions for two types of transformers: * - * 1. When the form field is initialized with the data attached from the datasource (object or array). - * 2. When data from a request is submitted using {@link Form::submit()} to transform the new input data - * back into the renderable format. For example if you have a date field and submit '2009-10-10' - * you might accept this value because its easily parsed, but the transformer still writes back - * "2009/10/10" onto the form field (for further displaying or other purposes). + * 1. Model transformers which normalize the model data. + * This is mainly useful when the same form type (the same configuration) + * has to handle different kind of underlying data, e.g The DateType can + * deal with strings or \DateTime objects as input. + * + * 2. View transformers which adapt the normalized data to the view format. + * a/ When the form is simple, the value returned by convention is used + * directly in the view and thus can only be a string or an array. In + * this case the data class should be null. + * + * b/ When the form is compound the returned value should be an array or + * an object to be mapped to the children. Each property of the compound + * data will be used as model data by each child and will be transformed + * too. In this case data class should be the class of the object, or null + * when it is an array. + * + * All transformers are called in a configured order from model data to view value. + * At the end of this chain the view data will be validated against the data class + * setting. * * This method must be able to deal with empty values. Usually this will * be NULL, but depending on your implementation other empty values are * possible as well (such as empty strings). The reasoning behind this is - * that value transformers must be chainable. If the transform() method - * of the first value transformer outputs NULL, the second value transformer - * must be able to process that value. - * - * By convention, transform() should return an empty string if NULL is - * passed. + * that data transformers must be chainable. If the transform() method + * of the first data transformer outputs NULL, the second must be able to + * process that value. * * @param mixed $value The value in the original representation * @@ -54,7 +66,10 @@ public function transform($value); * representation. * * This method is called when {@link Form::submit()} is called to transform the requests tainted data - * into an acceptable format for your data processing/model layer. + * into an acceptable format. + * + * The same transformers are called in the reverse order so the responsibility is to + * return one of the types that would be expected as input of transform(). * * This method must be able to deal with empty values. Usually this will * be an empty string, but depending on your implementation other empty diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php index e578058e5ccce..b658b9979f64c 100644 --- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php +++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -76,7 +76,7 @@ protected function loadTypes() new Type\TimeType(), new Type\TimezoneType(), new Type\UrlType(), - new Type\FileType(), + new Type\FileType($this->translator), new Type\ButtonType(), new Type\SubmitType(), new Type\ResetType(), diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ButtonType.php b/src/Symfony/Component/Form/Extension/Core/Type/ButtonType.php index 2b60f4f31e1e5..b05dcc018dc36 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ButtonType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ButtonType.php @@ -43,8 +43,6 @@ public function configureOptions(OptionsResolver $resolver) { parent::configureOptions($resolver); - $resolver->setDefaults([ - 'auto_initialize' => false, - ]); + $resolver->setDefault('auto_initialize', false); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 8d2d109699fba..5a0986d89ff93 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -311,8 +311,8 @@ public function configureOptions(OptionsResolver $resolver) 'placeholder' => $placeholderDefault, 'error_bubbling' => false, 'compound' => $compound, - // The view data is always a string, even if the "data" option - // is manually set to an object. + // The view data is always a string or an array of strings, + // even if the "data" option is manually set to an object. // See https://github.com/symfony/symfony/pull/5582 'data_class' => null, 'choice_translation_domain' => true, diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateIntervalType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateIntervalType.php index 181ce74993d64..2c41b1c6a35e6 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateIntervalType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateIntervalType.php @@ -205,38 +205,36 @@ public function configureOptions(OptionsResolver $resolver) })); }; - $resolver->setDefaults( - [ - 'with_years' => true, - 'with_months' => true, - 'with_days' => true, - 'with_weeks' => false, - 'with_hours' => false, - 'with_minutes' => false, - 'with_seconds' => false, - 'with_invert' => false, - 'years' => range(0, 100), - 'months' => range(0, 12), - 'weeks' => range(0, 52), - 'days' => range(0, 31), - 'hours' => range(0, 24), - 'minutes' => range(0, 60), - 'seconds' => range(0, 60), - 'widget' => 'choice', - 'input' => 'dateinterval', - 'placeholder' => $placeholderDefault, - 'by_reference' => true, - 'error_bubbling' => false, - // If initialized with a \DateInterval object, FormType initializes - // this option to "\DateInterval". Since the internal, normalized - // representation is not \DateInterval, but an array, we need to unset - // this option. - 'data_class' => null, - 'compound' => $compound, - 'empty_data' => $emptyData, - 'labels' => [], - ] - ); + $resolver->setDefaults([ + 'with_years' => true, + 'with_months' => true, + 'with_days' => true, + 'with_weeks' => false, + 'with_hours' => false, + 'with_minutes' => false, + 'with_seconds' => false, + 'with_invert' => false, + 'years' => range(0, 100), + 'months' => range(0, 12), + 'weeks' => range(0, 52), + 'days' => range(0, 31), + 'hours' => range(0, 24), + 'minutes' => range(0, 60), + 'seconds' => range(0, 60), + 'widget' => 'choice', + 'input' => 'dateinterval', + 'placeholder' => $placeholderDefault, + 'by_reference' => true, + 'error_bubbling' => false, + // If initialized with a \DateInterval object, FormType initializes + // this option to "\DateInterval". Since the internal, normalized + // representation is not \DateInterval, but an array, we need to unset + // this option. + 'data_class' => null, + 'compound' => $compound, + 'empty_data' => $emptyData, + 'labels' => [], + ]); $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('labels', $labelsNormalizer); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 59c72889d6683..f8afce2ee5a4d 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -13,15 +13,33 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Translation\TranslatorInterface; class FileType extends AbstractType { + const KIB_BYTES = 1024; + const MIB_BYTES = 1048576; + + private static $suffixes = [ + 1 => 'bytes', + self::KIB_BYTES => 'KiB', + self::MIB_BYTES => 'MiB', + ]; + + private $translator; + + public function __construct(TranslatorInterface $translator = null) + { + $this->translator = $translator; + } + /** * {@inheritdoc} */ @@ -43,6 +61,10 @@ public function buildForm(FormBuilderInterface $builder, array $options) foreach ($files as $file) { if ($requestHandler->isFileUpload($file)) { $data[] = $file; + + if (method_exists($requestHandler, 'getUploadFileError') && null !== $errorCode = $requestHandler->getUploadFileError($file)) { + $form->addError($this->getFileUploadError($errorCode)); + } } } @@ -54,6 +76,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) } $event->setData($data); + } elseif ($requestHandler->isFileUpload($event->getData()) && method_exists($requestHandler, 'getUploadFileError') && null !== $errorCode = $requestHandler->getUploadFileError($event->getData())) { + $form->addError($this->getFileUploadError($errorCode)); } elseif (!$requestHandler->isFileUpload($event->getData())) { $event->setData(null); } @@ -116,4 +140,109 @@ public function getBlockPrefix() { return 'file'; } + + private function getFileUploadError($errorCode) + { + $messageParameters = []; + + if (UPLOAD_ERR_INI_SIZE === $errorCode) { + list($limitAsString, $suffix) = $this->factorizeSizes(0, self::getMaxFilesize()); + $messageTemplate = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.'; + $messageParameters = [ + '{{ limit }}' => $limitAsString, + '{{ suffix }}' => $suffix, + ]; + } elseif (UPLOAD_ERR_FORM_SIZE === $errorCode) { + $messageTemplate = 'The file is too large.'; + } else { + $messageTemplate = 'The file could not be uploaded.'; + } + + if (null !== $this->translator) { + $message = $this->translator->trans($messageTemplate, $messageParameters); + } else { + $message = strtr($messageTemplate, $messageParameters); + } + + return new FormError($message, $messageTemplate, $messageParameters); + } + + /** + * Returns the maximum size of an uploaded file as configured in php.ini. + * + * This method should be kept in sync with Symfony\Component\HttpFoundation\File\UploadedFile::getMaxFilesize(). + * + * @return int The maximum size of an uploaded file in bytes + */ + private static function getMaxFilesize() + { + $iniMax = strtolower(ini_get('upload_max_filesize')); + + if ('' === $iniMax) { + return PHP_INT_MAX; + } + + $max = ltrim($iniMax, '+'); + if (0 === strpos($max, '0x')) { + $max = \intval($max, 16); + } elseif (0 === strpos($max, '0')) { + $max = \intval($max, 8); + } else { + $max = (int) $max; + } + + switch (substr($iniMax, -1)) { + case 't': $max *= 1024; + // no break + case 'g': $max *= 1024; + // no break + case 'm': $max *= 1024; + // no break + case 'k': $max *= 1024; + } + + return $max; + } + + /** + * Converts the limit to the smallest possible number + * (i.e. try "MB", then "kB", then "bytes"). + * + * This method should be kept in sync with Symfony\Component\Validator\Constraints\FileValidator::factorizeSizes(). + */ + private function factorizeSizes($size, $limit) + { + $coef = self::MIB_BYTES; + $coefFactor = self::KIB_BYTES; + + $limitAsString = (string) ($limit / $coef); + + // Restrict the limit to 2 decimals (without rounding! we + // need the precise value) + while (self::moreDecimalsThan($limitAsString, 2)) { + $coef /= $coefFactor; + $limitAsString = (string) ($limit / $coef); + } + + // Convert size to the same measure, but round to 2 decimals + $sizeAsString = (string) round($size / $coef, 2); + + // If the size and limit produce the same string output + // (due to rounding), reduce the coefficient + while ($sizeAsString === $limitAsString) { + $coef /= $coefFactor; + $limitAsString = (string) ($limit / $coef); + $sizeAsString = (string) round($size / $coef, 2); + } + + return [$limitAsString, self::$suffixes[$coef]]; + } + + /** + * This method should be kept in sync with Symfony\Component\Validator\Constraints\FileValidator::moreDecimalsThan(). + */ + private static function moreDecimalsThan($double, $numberOfDecimals) + { + return \strlen((string) $double) > \strlen(round($double, $numberOfDecimals)); + } } diff --git a/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php b/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php index d560c50d5d43b..a58ad246ea328 100644 --- a/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php +++ b/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php @@ -17,6 +17,7 @@ use Symfony\Component\Form\RequestHandlerInterface; use Symfony\Component\Form\Util\ServerParams; use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; /** @@ -115,4 +116,16 @@ public function isFileUpload($data) { return $data instanceof File; } + + /** + * @return int|null + */ + public function getUploadFileError($data) + { + if (!$data instanceof UploadedFile || $data->isValid()) { + return null; + } + + return $data->getError(); + } } diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 17ecfd6f47741..f54bd73d48340 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -21,6 +21,7 @@ use Symfony\Component\Form\Util\InheritDataAwareIterator; use Symfony\Component\Form\Util\OrderedHashMap; use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\PropertyPathInterface; /** * Form represents a form. @@ -63,79 +64,57 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterface { /** - * The form's configuration. - * * @var FormConfigInterface */ private $config; /** - * The parent of this form. - * - * @var FormInterface + * @var FormInterface|null */ private $parent; /** - * The children of this form. - * - * @var FormInterface[] A map of FormInterface instances + * @var FormInterface[]|OrderedHashMap A map of FormInterface instances */ private $children; /** - * The errors of this form. - * * @var FormError[] An array of FormError instances */ private $errors = []; /** - * Whether this form was submitted. - * * @var bool */ private $submitted = false; /** - * The button that was used to submit the form. - * - * @var Button + * @var ClickableInterface|null The button that was used to submit the form */ private $clickedButton; /** - * The form data in model format. - * * @var mixed */ private $modelData; /** - * The form data in normalized format. - * * @var mixed */ private $normData; /** - * The form data in view format. - * * @var mixed */ private $viewData; /** - * The submitted values that don't belong to any children. - * - * @var array + * @var array The submitted values that don't belong to any children */ private $extraData = []; /** - * Returns the transformation failure generated during submission, if any. - * - * @var TransformationFailedException|null + * @var TransformationFailedException|null The transformation failure generated during submission, if any */ private $transformationFailure; @@ -161,8 +140,21 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac private $lockSetData = false; /** - * Creates a new form based on the given configuration. - * + * @var string|int|null + */ + private $name; + + /** + * @var bool Whether the form inherits its underlying data from its parent + */ + private $inheritData; + + /** + * @var PropertyPathInterface|null + */ + private $propertyPath; + + /** * @throws LogicException if a data mapper is not provided for a compound form */ public function __construct(FormConfigInterface $config) @@ -176,12 +168,13 @@ public function __construct(FormConfigInterface $config) // If the form inherits the data from its parent, it is not necessary // to call setData() with the default data. - if ($config->getInheritData()) { + if ($this->inheritData = $config->getInheritData()) { $this->defaultDataSet = true; } $this->config = $config; $this->children = new OrderedHashMap(); + $this->name = $config->getName(); } public function __clone() @@ -206,7 +199,7 @@ public function getConfig() */ public function getName() { - return $this->config->getName(); + return $this->name; } /** @@ -214,11 +207,11 @@ public function getName() */ public function getPropertyPath() { - if (null !== $this->config->getPropertyPath()) { - return $this->config->getPropertyPath(); + if ($this->propertyPath || $this->propertyPath = $this->config->getPropertyPath()) { + return $this->propertyPath; } - if (null === $this->getName() || '' === $this->getName()) { + if (null === $this->name || '' === $this->name) { return null; } @@ -229,10 +222,12 @@ public function getPropertyPath() } if ($parent && null === $parent->getConfig()->getDataClass()) { - return new PropertyPath('['.$this->getName().']'); + $this->propertyPath = new PropertyPath('['.$this->name.']'); + } else { + $this->propertyPath = new PropertyPath($this->name); } - return new PropertyPath($this->getName()); + return $this->propertyPath; } /** @@ -268,7 +263,7 @@ public function setParent(FormInterface $parent = null) throw new AlreadySubmittedException('You cannot set the parent of a submitted form'); } - if (null !== $parent && '' === $this->config->getName()) { + if (null !== $parent && '' === $this->name) { throw new LogicException('A form with an empty name cannot have a parent form.'); } @@ -315,7 +310,7 @@ public function setData($modelData) // If the form inherits its parent's data, disallow data setting to // prevent merge conflicts - if ($this->config->getInheritData()) { + if ($this->inheritData) { throw new RuntimeException('You cannot change the data of a form inheriting its parent data.'); } @@ -335,7 +330,7 @@ public function setData($modelData) $this->lockSetData = true; $dispatcher = $this->config->getEventDispatcher(); - // Hook to change content of the data + // Hook to change content of the model data before transformation and mapping children if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) { $event = new FormEvent($this, $modelData); $dispatcher->dispatch(FormEvents::PRE_SET_DATA, $event); @@ -348,6 +343,7 @@ public function setData($modelData) } // Synchronize representations - must not change the content! + // Transformation exceptions are not caught on initialization $normData = $this->modelToNorm($modelData); $viewData = $this->normToView($normData); @@ -370,13 +366,10 @@ public function setData($modelData) $this->defaultDataSet = true; $this->lockSetData = false; - // It is not necessary to invoke this method if the form doesn't have children, - // even if the form is compound. + // Compound forms don't need to invoke this method if they don't have children if (\count($this->children) > 0) { - // Update child forms from the data - $iterator = new InheritDataAwareIterator($this->children); - $iterator = new \RecursiveIteratorIterator($iterator); - $this->config->getDataMapper()->mapDataToForms($viewData, $iterator); + // Update child forms from the data (unless their config data is locked) + $this->config->getDataMapper()->mapDataToForms($viewData, new \RecursiveIteratorIterator(new InheritDataAwareIterator($this->children))); } if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) { @@ -392,7 +385,7 @@ public function setData($modelData) */ public function getData() { - if ($this->config->getInheritData()) { + if ($this->inheritData) { if (!$this->parent) { throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); } @@ -416,7 +409,7 @@ public function getData() */ public function getNormData() { - if ($this->config->getInheritData()) { + if ($this->inheritData) { if (!$this->parent) { throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); } @@ -440,7 +433,7 @@ public function getNormData() */ public function getViewData() { - if ($this->config->getInheritData()) { + if ($this->inheritData) { if (!$this->parent) { throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); } @@ -505,8 +498,8 @@ public function submit($submittedData, $clearMissing = true) throw new AlreadySubmittedException('A form can only be submitted once'); } - // Initialize errors in the very beginning so that we don't lose any - // errors added during listeners + // Initialize errors in the very beginning so we're sure + // they are collectable during submission only $this->errors = []; // Obviously, a disabled form should not change its data upon submission. @@ -605,18 +598,16 @@ public function submit($submittedData, $clearMissing = true) // changes in the grandchildren (i.e. children of the form that inherits // its parent's data) into account. // (see InheritDataAwareIterator below) - if (!$this->config->getInheritData()) { - // If the form is compound, the default data in view format - // is reused. The data of the children is merged into this - // default data using the data mapper. - // If the form is not compound, the submitted data is also the data in view format. + if (!$this->inheritData) { + // If the form is compound, the view data is merged with the data + // of the children using the data mapper. + // If the form is not compound, the view data is assigned to the submitted data. $viewData = $this->config->getCompound() ? $this->viewData : $submittedData; if (FormUtil::isEmpty($viewData)) { $emptyData = $this->config->getEmptyData(); if ($emptyData instanceof \Closure) { - /* @var \Closure $emptyData */ $emptyData = $emptyData($this, $viewData); } @@ -631,9 +622,10 @@ public function submit($submittedData, $clearMissing = true) // descendants that inherit this form's data. // These descendants will not be submitted normally (see the check // for $this->config->getInheritData() above) - $childrenIterator = new InheritDataAwareIterator($this->children); - $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); - $this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData); + $this->config->getDataMapper()->mapFormsToData( + new \RecursiveIteratorIterator(new InheritDataAwareIterator($this->children)), + $viewData + ); } // Normalize data to unified representation @@ -658,7 +650,7 @@ public function submit($submittedData, $clearMissing = true) // the erroneous data is accessible on the form. // Forms that inherit data never set any data, because the getters // forward to the parent form's getters anyway. - if (null === $viewData && !$this->config->getInheritData()) { + if (null === $viewData && !$this->inheritData) { $viewData = $submittedData; } } @@ -755,8 +747,7 @@ public function isValid() /** * Returns the button that was used to submit the form. * - * @return Button|null The clicked button or NULL if the form was not - * submitted + * @return ClickableInterface|null */ public function getClickedButton() { @@ -845,29 +836,6 @@ public function add($child, $type = null, array $options = []) throw new LogicException('You cannot add children to a simple form. Maybe you should set the option "compound" to true?'); } - // Obtain the view data - $viewData = null; - - // If setData() is currently being called, there is no need to call - // mapDataToForms() here, as mapDataToForms() is called at the end - // of setData() anyway. Not doing this check leads to an endless - // recursion when initializing the form lazily and an event listener - // (such as ResizeFormListener) adds fields depending on the data: - // - // * setData() is called, the form is not initialized yet - // * add() is called by the listener (setData() is not complete, so - // the form is still not initialized) - // * getViewData() is called - // * setData() is called since the form is not initialized yet - // * ... endless recursion ... - // - // Also skip data mapping if setData() has not been called yet. - // setData() will be called upon form initialization and data mapping - // will take place by then. - if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { - $viewData = $this->getViewData(); - } - if (!$child instanceof FormInterface) { if (!\is_string($child) && !\is_int($child)) { throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface'); @@ -897,10 +865,28 @@ public function add($child, $type = null, array $options = []) $child->setParent($this); - if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { - $iterator = new InheritDataAwareIterator(new \ArrayIterator([$child->getName() => $child])); - $iterator = new \RecursiveIteratorIterator($iterator); - $this->config->getDataMapper()->mapDataToForms($viewData, $iterator); + // If setData() is currently being called, there is no need to call + // mapDataToForms() here, as mapDataToForms() is called at the end + // of setData() anyway. Not doing this check leads to an endless + // recursion when initializing the form lazily and an event listener + // (such as ResizeFormListener) adds fields depending on the data: + // + // * setData() is called, the form is not initialized yet + // * add() is called by the listener (setData() is not complete, so + // the form is still not initialized) + // * getViewData() is called + // * setData() is called since the form is not initialized yet + // * ... endless recursion ... + // + // Also skip data mapping if setData() has not been called yet. + // setData() will be called upon form initialization and data mapping + // will take place by then. + if (!$this->lockSetData && $this->defaultDataSet && !$this->inheritData) { + $viewData = $this->getViewData(); + $this->config->getDataMapper()->mapDataToForms( + $viewData, + new \RecursiveIteratorIterator(new InheritDataAwareIterator(new \ArrayIterator([$child->getName() => $child]))) + ); } return $this; @@ -1049,13 +1035,13 @@ public function createView(FormView $parent = null) } /** - * Normalizes the value if a model transformer is set. + * Normalizes the underlying data if a model transformer is set. * * @param mixed $value The value to transform * * @return mixed * - * @throws TransformationFailedException If the value cannot be transformed to "normalized" format + * @throws TransformationFailedException If the underlying data cannot be transformed to "normalized" format */ private function modelToNorm($value) { @@ -1064,7 +1050,7 @@ private function modelToNorm($value) $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException('Unable to transform value for property path "'.$this->getPropertyPath().'": '.$exception->getMessage(), $exception->getCode(), $exception); + throw new TransformationFailedException('Unable to transform data for property path "'.$this->getPropertyPath().'": '.$exception->getMessage(), $exception->getCode(), $exception); } return $value; @@ -1101,7 +1087,7 @@ private function normToModel($value) * * @return mixed * - * @throws TransformationFailedException If the value cannot be transformed to "view" format + * @throws TransformationFailedException If the normalized value cannot be transformed to "view" format */ private function normToView($value) { @@ -1110,12 +1096,12 @@ private function normToView($value) // Only do this for simple forms, as the resulting value in // compound forms is passed to the data mapper and thus should // not be converted to a string before. - if (!$this->config->getViewTransformers() && !$this->config->getCompound()) { + if (!($transformers = $this->config->getViewTransformers()) && !$this->config->getCompound()) { return null === $value || is_scalar($value) ? (string) $value : $value; } try { - foreach ($this->config->getViewTransformers() as $transformer) { + foreach ($transformers as $transformer) { $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { @@ -1132,13 +1118,11 @@ private function normToView($value) * * @return mixed * - * @throws TransformationFailedException If the value cannot be transformed to "normalized" format + * @throws TransformationFailedException If the submitted value cannot be transformed to "normalized" format */ private function viewToNorm($value) { - $transformers = $this->config->getViewTransformers(); - - if (!$transformers) { + if (!$transformers = $this->config->getViewTransformers()) { return '' === $value ? null : $value; } diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index a795a1e5fa346..56cdbb6f14298 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -72,10 +72,7 @@ public function add($child, $type = null, array $options = []) // Add to "children" to maintain order $this->children[$child] = null; - $this->unresolvedChildren[$child] = [ - 'type' => $type, - 'options' => $options, - ]; + $this->unresolvedChildren[$child] = [$type, $options]; return $this; } @@ -143,15 +140,7 @@ public function has($name) throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - if (isset($this->unresolvedChildren[$name])) { - return true; - } - - if (isset($this->children[$name])) { - return true; - } - - return false; + return isset($this->unresolvedChildren[$name]) || isset($this->children[$name]); } /** @@ -223,7 +212,7 @@ public function getForm() /** * {@inheritdoc} * - * @return FormBuilderInterface[] + * @return FormBuilderInterface[]|\Traversable */ public function getIterator() { @@ -239,12 +228,11 @@ public function getIterator() */ private function resolveChild(string $name): FormBuilderInterface { - $info = $this->unresolvedChildren[$name]; - $child = $this->create($name, $info['type'], $info['options']); - $this->children[$name] = $child; + list($type, $options) = $this->unresolvedChildren[$name]; + unset($this->unresolvedChildren[$name]); - return $child; + return $this->children[$name] = $this->create($name, $type, $options); } /** @@ -253,7 +241,7 @@ private function resolveChild(string $name): FormBuilderInterface private function resolveChildren() { foreach ($this->unresolvedChildren as $name => $info) { - $this->children[$name] = $this->create($name, $info['type'], $info['options']); + $this->children[$name] = $this->create($name, $info[0], $info[1]); } $this->unresolvedChildren = []; diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php index 1a581d9b8e67e..fa7bac32bb3c9 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -34,44 +34,18 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private static $nativeRequestHandler; - /** - * @var bool - */ protected $locked = false; - - /** - * @var EventDispatcherInterface - */ private $dispatcher; - - /** - * @var string - */ private $name; /** - * @var PropertyPathInterface + * @var PropertyPathInterface|string|null */ private $propertyPath; - /** - * @var bool - */ private $mapped = true; - - /** - * @var bool - */ private $byReference = true; - - /** - * @var bool - */ private $inheritData = false; - - /** - * @var bool - */ private $compound = false; /** @@ -79,34 +53,16 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $type; - /** - * @var array - */ private $viewTransformers = []; - - /** - * @var array - */ private $modelTransformers = []; /** - * @var DataMapperInterface + * @var DataMapperInterface|null */ private $dataMapper; - /** - * @var bool - */ private $required = true; - - /** - * @var bool - */ private $disabled = false; - - /** - * @var bool - */ private $errorBubbling = false; /** @@ -114,9 +70,6 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $emptyData; - /** - * @var array - */ private $attributes = []; /** @@ -129,39 +82,26 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $dataClass; - /** - * @var bool - */ - private $dataLocked; + private $dataLocked = false; /** - * @var FormFactoryInterface + * @var FormFactoryInterface|null */ private $formFactory; /** - * @var string + * @var string|null */ private $action; - /** - * @var string - */ private $method = 'POST'; /** - * @var RequestHandlerInterface + * @var RequestHandlerInterface|null */ private $requestHandler; - /** - * @var bool - */ private $autoInitialize = false; - - /** - * @var array - */ private $options; /** @@ -603,7 +543,7 @@ public function setErrorBubbling($errorBubbling) throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->errorBubbling = null === $errorBubbling ? null : (bool) $errorBubbling; + $this->errorBubbling = (bool) $errorBubbling; return $this; } @@ -649,7 +589,7 @@ public function setMapped($mapped) throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->mapped = $mapped; + $this->mapped = (bool) $mapped; return $this; } @@ -663,7 +603,7 @@ public function setByReference($byReference) throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->byReference = $byReference; + $this->byReference = (bool) $byReference; return $this; } @@ -677,7 +617,7 @@ public function setInheritData($inheritData) throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->inheritData = $inheritData; + $this->inheritData = (bool) $inheritData; return $this; } @@ -691,7 +631,7 @@ public function setCompound($compound) throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->compound = $compound; + $this->compound = (bool) $compound; return $this; } @@ -733,7 +673,7 @@ public function setDataLocked($locked) throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->dataLocked = $locked; + $this->dataLocked = (bool) $locked; return $this; } @@ -761,7 +701,7 @@ public function setAction($action) throw new BadMethodCallException('The config builder cannot be modified anymore.'); } - $this->action = $action; + $this->action = (string) $action; return $this; } @@ -827,7 +767,7 @@ public function getFormConfig() /** * Validates whether the given variable is a valid form name. * - * @param string|int $name The tested form name + * @param string|int|null $name The tested form name * * @throws UnexpectedTypeException if the name is not a string or an integer * @throws InvalidArgumentException if the name contains invalid characters @@ -853,7 +793,7 @@ public static function validateName($name) * * contains only letters, digits, numbers, underscores ("_"), * hyphens ("-") and colons (":") * - * @param string $name The tested form name + * @param string|null $name The tested form name * * @return bool Whether the name is valid */ diff --git a/src/Symfony/Component/Form/FormConfigBuilderInterface.php b/src/Symfony/Component/Form/FormConfigBuilderInterface.php index f422840a82c45..d516e41056ecc 100644 --- a/src/Symfony/Component/Form/FormConfigBuilderInterface.php +++ b/src/Symfony/Component/Form/FormConfigBuilderInterface.php @@ -108,7 +108,7 @@ public function setAttributes(array $attributes); public function setDataMapper(DataMapperInterface $dataMapper = null); /** - * Set whether the form is disabled. + * Sets whether the form is disabled. * * @param bool $disabled Whether the form is disabled * @@ -166,8 +166,7 @@ public function setMapped($mapped); /** * Sets whether the form's data should be modified by reference. * - * @param bool $byReference Whether the data should be - * modified by reference + * @param bool $byReference Whether the data should be modified by reference * * @return $this The configuration object */ @@ -194,7 +193,7 @@ public function setInheritData($inheritData); public function setCompound($compound); /** - * Set the types. + * Sets the resolved type. * * @return $this The configuration object */ @@ -203,7 +202,7 @@ public function setType(ResolvedFormTypeInterface $type); /** * Sets the initial data of the form. * - * @param mixed $data The data of the form in application format + * @param mixed $data The data of the form in model format * * @return $this The configuration object */ @@ -214,9 +213,12 @@ public function setData($data); * * A form with locked data is restricted to the data passed in * this configuration. The data can only be modified then by - * submitting the form. + * submitting the form or using PRE_SET_DATA event. * - * @param bool $locked Whether to lock the default data + * It means data passed to a factory method or mapped from the + * parent will be ignored. + * + * @param bool $locked Whether to lock the default configured data * * @return $this The configuration object */ diff --git a/src/Symfony/Component/Form/FormConfigInterface.php b/src/Symfony/Component/Form/FormConfigInterface.php index ce9171f3d496b..7dbda33033b55 100644 --- a/src/Symfony/Component/Form/FormConfigInterface.php +++ b/src/Symfony/Component/Form/FormConfigInterface.php @@ -70,15 +70,17 @@ public function getInheritData(); * This property is independent of whether the form actually has * children. A form can be compound and have no children at all, like * for example an empty collection form. + * The contrary is not possible, a form which is not compound + * cannot have any children. * * @return bool Whether the form is compound */ public function getCompound(); /** - * Returns the form types used to construct the form. + * Returns the resolved form type used to construct the form. * - * @return ResolvedFormTypeInterface The form's type + * @return ResolvedFormTypeInterface The form's resolved type */ public function getType(); @@ -97,7 +99,7 @@ public function getViewTransformers(); public function getModelTransformers(); /** - * Returns the data mapper of the form. + * Returns the data mapper of the compound form or null for a simple form. * * @return DataMapperInterface|null The data mapper */ @@ -125,9 +127,15 @@ public function getDisabled(); public function getErrorBubbling(); /** - * Returns the data that should be returned when the form is empty. + * Used when the view data is empty on submission. * - * @return mixed The data returned if the form is empty + * When the form is compound it will also be used to map the + * children data. + * + * The empty data must match the view format as it will passed to the first view transformer's + * "reverseTransform" method. + * + * @return mixed The data used when the submitted form is initially empty */ public function getEmptyData(); @@ -165,7 +173,7 @@ public function getAttribute($name, $default = null); public function getData(); /** - * Returns the class of the form data or null if the data is scalar or an array. + * Returns the class of the view data or null if the data is scalar or an array. * * @return string|null The data class or null */ diff --git a/src/Symfony/Component/Form/FormError.php b/src/Symfony/Component/Form/FormError.php index 6724ddc03c510..44cf040ae14cc 100644 --- a/src/Symfony/Component/Form/FormError.php +++ b/src/Symfony/Component/Form/FormError.php @@ -49,7 +49,7 @@ class FormError implements \Serializable */ public function __construct(?string $message, string $messageTemplate = null, array $messageParameters = [], int $messagePluralization = null, $cause = null) { - $this->message = $message; + $this->message = (string) $message; $this->messageTemplate = $messageTemplate ?: $message; $this->messageParameters = $messageParameters; $this->messagePluralization = $messagePluralization; diff --git a/src/Symfony/Component/Form/FormErrorIterator.php b/src/Symfony/Component/Form/FormErrorIterator.php index 2cc53c0923f94..db1d311a30d7a 100644 --- a/src/Symfony/Component/Form/FormErrorIterator.php +++ b/src/Symfony/Component/Form/FormErrorIterator.php @@ -39,10 +39,9 @@ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \Array private $errors; /** - * Creates a new iterator. - * - * @param FormInterface $form The erroneous form - * @param FormError[]|FormErrorIterator[] $errors The form errors + * @param FormInterface $form The erroneous form + * @param FormError[]|self[] $errors An array of form errors and instances + * of FormErrorIterator * * @throws InvalidArgumentException If the errors are invalid */ @@ -71,7 +70,7 @@ public function __toString() if ($error instanceof FormError) { $string .= 'ERROR: '.$error->getMessage()."\n"; } else { - /* @var $error FormErrorIterator */ + /** @var self $error */ $string .= $error->form->getName().":\n"; $string .= self::indent((string) $error); } @@ -93,8 +92,7 @@ public function getForm() /** * Returns the current element of the iterator. * - * @return FormError|FormErrorIterator an error or an iterator containing - * nested errors + * @return FormError|self An error or an iterator containing nested errors */ public function current() { diff --git a/src/Symfony/Component/Form/FormEvent.php b/src/Symfony/Component/Form/FormEvent.php index c688a19566b76..3b6d484e75803 100644 --- a/src/Symfony/Component/Form/FormEvent.php +++ b/src/Symfony/Component/Form/FormEvent.php @@ -21,6 +21,10 @@ class FormEvent extends Event private $form; protected $data; + /** + * @param FormInterface $form The associated form + * @param mixed $data The data + */ public function __construct(FormInterface $form, $data) { $this->form = $form; diff --git a/src/Symfony/Component/Form/FormEvents.php b/src/Symfony/Component/Form/FormEvents.php index c9b01736d495f..27e61498e23c6 100644 --- a/src/Symfony/Component/Form/FormEvents.php +++ b/src/Symfony/Component/Form/FormEvents.php @@ -34,21 +34,35 @@ final class FormEvents const PRE_SUBMIT = 'form.pre_submit'; /** - * The SUBMIT event is dispatched just before the Form::submit() method - * transforms back the normalized data to the model and view data. + * The SUBMIT event is dispatched after the Form::submit() method + * has changed the view data by the request data, or submitted and mapped + * the children if the form is compound, and after reverse transformation + * to normalized representation. * - * It can be used to change data from the normalized representation of the data. + * It's also dispatched just before the Form::submit() method transforms back + * the normalized data to the model and view data. + * + * So at this stage children of compound forms are submitted and synchronized, unless + * their transformation failed, but a parent would still be at the PRE_SUBMIT level. + * + * Since the current form is not synchronized yet, it is still possible to add and + * remove fields. * * @Event("Symfony\Component\Form\FormEvent") */ const SUBMIT = 'form.submit'; /** - * The FormEvents::POST_SUBMIT event is dispatched after the Form::submit() - * once the model and view data have been denormalized. + * The FormEvents::POST_SUBMIT event is dispatched at the very end of the Form::submit(). + * + * It this stage the model and view data may have been denormalized. Otherwise the form + * is desynchronized because transformation failed during submission. * * It can be used to fetch data after denormalization. * + * The event attaches the current view data. To know whether this is the renormalized data + * or the invalid request data, call Form::isSynchronized() first. + * * @Event("Symfony\Component\Form\FormEvent") */ const POST_SUBMIT = 'form.post_submit'; @@ -58,7 +72,7 @@ final class FormEvents * * It can be used to: * - Modify the data given during pre-population; - * - Modify a form depending on the pre-populated data (adding or removing fields dynamically). + * - Keep synchronized the form depending on the data (adding or removing fields dynamically). * * @Event("Symfony\Component\Form\FormEvent") */ @@ -67,7 +81,8 @@ final class FormEvents /** * The FormEvents::POST_SET_DATA event is dispatched at the end of the Form::setData() method. * - * This event is mostly here for reading data after having pre-populated the form. + * This event can be used to modify the form depending on the final state of the underlying data + * accessible in every representation: model, normalized and view. * * @Event("Symfony\Component\Form\FormEvent") */ diff --git a/src/Symfony/Component/Form/FormFactoryBuilder.php b/src/Symfony/Component/Form/FormFactoryBuilder.php index fc8b12ffadbc0..d73b438b4be7a 100644 --- a/src/Symfony/Component/Form/FormFactoryBuilder.php +++ b/src/Symfony/Component/Form/FormFactoryBuilder.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Form; +use Symfony\Component\Form\Extension\Core\CoreExtension; + /** * The default implementation of FormFactoryBuilderInterface. * @@ -18,6 +20,8 @@ */ class FormFactoryBuilder implements FormFactoryBuilderInterface { + private $forceCoreExtension; + /** * @var ResolvedFormTypeFactoryInterface */ @@ -43,6 +47,14 @@ class FormFactoryBuilder implements FormFactoryBuilderInterface */ private $typeGuessers = []; + /** + * @param bool $forceCoreExtension + */ + public function __construct($forceCoreExtension = false) + { + $this->forceCoreExtension = $forceCoreExtension; + } + /** * {@inheritdoc} */ @@ -150,6 +162,21 @@ public function getFormFactory() { $extensions = $this->extensions; + if ($this->forceCoreExtension) { + $hasCoreExtension = false; + + foreach ($extensions as $extension) { + if ($extension instanceof CoreExtension) { + $hasCoreExtension = true; + break; + } + } + + if (!$hasCoreExtension) { + array_unshift($extensions, new CoreExtension()); + } + } + if (\count($this->types) > 0 || \count($this->typeExtensions) > 0 || \count($this->typeGuessers) > 0) { if (\count($this->typeGuessers) > 1) { $typeGuesser = new FormTypeGuesserChain($this->typeGuessers); diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 565b79af39416..5ed0376ed6008 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Form; -use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\PropertyAccess\PropertyPathInterface; /** * A form group bundling multiple forms in a hierarchical structure. @@ -23,7 +23,9 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable /** * Sets the parent form. * - * @return self + * @param FormInterface|null $parent The parent form or null if it's the root + * + * @return $this * * @throws Exception\AlreadySubmittedException if the form has already been submitted * @throws Exception\LogicException when trying to set a parent for a form with @@ -45,7 +47,7 @@ public function getParent(); * @param string|null $type The child's type, if a name was passed * @param array $options The child's options, if a name was passed * - * @return self + * @return $this * * @throws Exception\AlreadySubmittedException if the form has already been submitted * @throws Exception\LogicException when trying to add a child to a non-compound form @@ -104,44 +106,70 @@ public function all(); public function getErrors($deep = false, $flatten = true); /** - * Updates the form with default data. + * Updates the form with default model data. * * @param mixed $modelData The data formatted as expected for the underlying object * * @return $this * - * @throws Exception\AlreadySubmittedException if the form has already been submitted - * @throws Exception\LogicException If listeners try to call setData in a cycle. Or if - * the view data does not match the expected type - * according to {@link FormConfigInterface::getDataClass}. + * @throws Exception\AlreadySubmittedException If the form has already been submitted + * @throws Exception\LogicException If the view data does not match the expected type + * according to {@link FormConfigInterface::getDataClass}. + * @throws Exception\RuntimeException If listeners try to call setData in a cycle or if + * the form inherits data from its parent + * @throws Exception\TransformationFailedException If the synchronization failed. */ public function setData($modelData); /** - * Returns the data in the format needed for the underlying object. + * Returns the model data in the format needed for the underlying object. * - * @return mixed + * @return mixed When the field is not submitted, the default data is returned. + * When the field is submitted, the default data has been bound + * to the submitted view data. + * + * @throws Exception\RuntimeException If the form inherits data but has no parent */ public function getData(); /** - * Returns the normalized data of the field. + * Returns the normalized data of the field, used as internal bridge + * between model data and view data. * * @return mixed When the field is not submitted, the default data is returned. - * When the field is submitted, the normalized submitted data is - * returned if the field is valid, null otherwise. + * When the field is submitted, the normalized submitted data + * is returned if the field is synchronized with the view data, + * null otherwise. + * + * @throws Exception\RuntimeException If the form inherits data but has no parent */ public function getNormData(); /** - * Returns the data transformed by the value transformer. + * Returns the view data of the field. + * + * It may be defined by {@link FormConfigInterface::getDataClass}. + * + * There are two cases: + * + * - When the form is compound the view data is mapped to the children. + * Each child will use its mapped data as model data. + * It can be an array, an object or null. + * + * - When the form is simple its view data is used to be bound + * to the submitted data. + * It can be a string or an array. + * + * In both cases the view data is the actual altered data on submission. * * @return mixed + * + * @throws Exception\RuntimeException If the form inherits data but has no parent */ public function getViewData(); /** - * Returns the extra data. + * Returns the extra submitted data. * * @return array The submitted data which do not belong to a child */ @@ -150,7 +178,7 @@ public function getExtraData(); /** * Returns the form's configuration. * - * @return FormConfigInterface The configuration + * @return FormConfigInterface The configuration instance */ public function getConfig(); @@ -164,6 +192,8 @@ public function isSubmitted(); /** * Returns the name by which the form is identified in forms. * + * Only root forms are allowed to have an empty name. + * * @return string The name of the form */ public function getName(); @@ -171,7 +201,7 @@ public function getName(); /** * Returns the property path that the form is mapped to. * - * @return \Symfony\Component\PropertyAccess\PropertyPathInterface|null The property path + * @return PropertyPathInterface|null The property path instance */ public function getPropertyPath(); @@ -230,14 +260,16 @@ public function isEmpty(); * If the data is not synchronized, you can get the transformation failure * by calling {@link getTransformationFailure()}. * + * If the form is not submitted, this method always returns true. + * * @return bool */ public function isSynchronized(); /** - * Returns the data transformation failure, if any. + * Returns the data transformation failure, if any, during submission. * - * @return TransformationFailedException|null The transformation failure + * @return Exception\TransformationFailedException|null The transformation failure or null */ public function getTransformationFailure(); @@ -247,6 +279,8 @@ public function getTransformationFailure(); * Should be called on the root form after constructing the tree. * * @return $this + * + * @throws Exception\RuntimeException If the form is not the root */ public function initialize(); @@ -265,11 +299,13 @@ public function initialize(); public function handleRequest($request = null); /** - * Submits data to the form, transforms and validates it. + * Submits data to the form. * - * @param mixed $submittedData The submitted data - * @param bool $clearMissing Whether to set fields to NULL when they - * are missing in the submitted data + * @param string|array|null $submittedData The submitted data + * @param bool $clearMissing Whether to set fields to NULL + * when they are missing in the + * submitted data. This argument + * is only used in compound form * * @return $this * @@ -280,7 +316,7 @@ public function submit($submittedData, $clearMissing = true); /** * Returns the root of the form tree. * - * @return self The root of the tree + * @return self The root of the tree, may be the instance itself */ public function getRoot(); @@ -292,8 +328,6 @@ public function getRoot(); public function isRoot(); /** - * Creates a view. - * * @return FormView The view */ public function createView(FormView $parent = null); diff --git a/src/Symfony/Component/Form/FormRegistry.php b/src/Symfony/Component/Form/FormRegistry.php index 1446976191c6d..cbb1d7a4174c7 100644 --- a/src/Symfony/Component/Form/FormRegistry.php +++ b/src/Symfony/Component/Form/FormRegistry.php @@ -26,7 +26,7 @@ class FormRegistry implements FormRegistryInterface /** * Extensions. * - * @var FormExtensionInterface[] An array of FormExtensionInterface + * @var FormExtensionInterface[] */ private $extensions = []; diff --git a/src/Symfony/Component/Form/FormRendererEngineInterface.php b/src/Symfony/Component/Form/FormRendererEngineInterface.php index 6635a63b94d31..4743ffea3f307 100644 --- a/src/Symfony/Component/Form/FormRendererEngineInterface.php +++ b/src/Symfony/Component/Form/FormRendererEngineInterface.php @@ -74,7 +74,7 @@ public function getResourceForBlockName(FormView $view, $blockName); * First the themes attached directly to * the view with {@link setTheme()} are * considered, then the ones of its parent etc. - * @param array $blockNameHierarchy The block name hierarchy, with the root block + * @param string[] $blockNameHierarchy The block name hierarchy, with the root block * at the beginning * @param int $hierarchyLevel The level in the hierarchy at which to start * looking. Level 0 indicates the root block, i.e. @@ -112,7 +112,7 @@ public function getResourceForBlockNameHierarchy(FormView $view, array $blockNam * First the themes attached directly to * the view with {@link setTheme()} are * considered, then the ones of its parent etc. - * @param array $blockNameHierarchy The block name hierarchy, with the root block + * @param string[] $blockNameHierarchy The block name hierarchy, with the root block * at the beginning * @param int $hierarchyLevel The level in the hierarchy at which to start * looking. Level 0 indicates the root block, i.e. diff --git a/src/Symfony/Component/Form/FormTypeGuesserChain.php b/src/Symfony/Component/Form/FormTypeGuesserChain.php index 68097dc98b3cb..d84ef4c174bf7 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserChain.php +++ b/src/Symfony/Component/Form/FormTypeGuesserChain.php @@ -19,7 +19,7 @@ class FormTypeGuesserChain implements FormTypeGuesserInterface private $guessers = []; /** - * @param FormTypeGuesserInterface[] $guessers Guessers as instances of FormTypeGuesserInterface + * @param FormTypeGuesserInterface[] $guessers * * @throws UnexpectedTypeException if any guesser does not implement FormTypeGuesserInterface */ diff --git a/src/Symfony/Component/Form/FormTypeGuesserInterface.php b/src/Symfony/Component/Form/FormTypeGuesserInterface.php index 6521ea47ca767..3be9a0c9f8570 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserInterface.php +++ b/src/Symfony/Component/Form/FormTypeGuesserInterface.php @@ -49,8 +49,8 @@ public function guessMaxLength($class, $property); /** * Returns a guess about the field's pattern. * - * - When you have a min value, you guess a min length of this min (LOW_CONFIDENCE) , lines below - * - If this value is a float type, this is wrong so you guess null with MEDIUM_CONFIDENCE to override the previous guess. + * - When you have a min value, you guess a min length of this min (LOW_CONFIDENCE) + * - Then line below, if this value is a float type, this is wrong so you guess null with MEDIUM_CONFIDENCE to override the previous guess. * Example: * You want a float greater than 5, 4.512313 is not valid but length(4.512314) > length(5) * diff --git a/src/Symfony/Component/Form/Forms.php b/src/Symfony/Component/Form/Forms.php index a67c436fe01f2..6b25ed15f9410 100644 --- a/src/Symfony/Component/Form/Forms.php +++ b/src/Symfony/Component/Form/Forms.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Form; -use Symfony\Component\Form\Extension\Core\CoreExtension; - /** * Entry point of the Form component. * @@ -105,10 +103,7 @@ public static function createFormFactory(): FormFactoryInterface */ public static function createFormFactoryBuilder(): FormFactoryBuilderInterface { - $builder = new FormFactoryBuilder(); - $builder->addExtension(new CoreExtension()); - - return $builder; + return new FormFactoryBuilder(true); } /** diff --git a/src/Symfony/Component/Form/NativeRequestHandler.php b/src/Symfony/Component/Form/NativeRequestHandler.php index cfccf180b9abc..60f8cf79ae1ba 100644 --- a/src/Symfony/Component/Form/NativeRequestHandler.php +++ b/src/Symfony/Component/Form/NativeRequestHandler.php @@ -41,6 +41,8 @@ public function __construct(ServerParams $params = null) /** * {@inheritdoc} + * + * @throws Exception\UnexpectedTypeException If the $request is not null */ public function handleRequest(FormInterface $form, $request = null) { @@ -133,6 +135,30 @@ public function isFileUpload($data) return \is_array($data) && isset($data['error']) && \is_int($data['error']); } + /** + * @return int|null + */ + public function getUploadFileError($data) + { + if (!\is_array($data)) { + return null; + } + + if (!isset($data['error'])) { + return null; + } + + if (!\is_int($data['error'])) { + return null; + } + + if (UPLOAD_ERR_OK === $data['error']) { + return null; + } + + return $data['error']; + } + /** * Returns the method used to submit the request to the server. * diff --git a/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php index 16d4045e6d580..b470769344bb2 100644 --- a/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php @@ -360,6 +360,28 @@ public function testInvalidFilesAreRejected() $this->assertFalse($this->requestHandler->isFileUpload($this->getInvalidFile())); } + /** + * @dataProvider uploadFileErrorCodes + */ + public function testFailedFileUploadIsTurnedIntoFormError($errorCode, $expectedErrorCode) + { + $this->assertSame($expectedErrorCode, $this->requestHandler->getUploadFileError($this->getFailedUploadedFile($errorCode))); + } + + public function uploadFileErrorCodes() + { + return [ + 'no error' => [UPLOAD_ERR_OK, null], + 'upload_max_filesize ini directive' => [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_INI_SIZE], + 'MAX_FILE_SIZE from form' => [UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_FORM_SIZE], + 'partially uploaded' => [UPLOAD_ERR_PARTIAL, UPLOAD_ERR_PARTIAL], + 'no file upload' => [UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_FILE], + 'missing temporary directory' => [UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_NO_TMP_DIR], + 'write failure' => [UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_CANT_WRITE], + 'stopped by extension' => [UPLOAD_ERR_EXTENSION, UPLOAD_ERR_EXTENSION], + ]; + } + abstract protected function setRequestData($method, $data, $files = []); abstract protected function getRequestHandler(); @@ -368,6 +390,8 @@ abstract protected function getUploadedFile($suffix = ''); abstract protected function getInvalidFile(); + abstract protected function getFailedUploadedFile($errorCode); + protected function createForm($name, $method = null, $compound = false) { $config = $this->createBuilder($name, $compound); diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php index c520ab1a0de74..5a9884e2951b0 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -144,12 +144,10 @@ function ($object) { return $object->value; } public function testCreateFromChoicesGrouped() { - $list = $this->factory->createListFromChoices( - [ - 'Group 1' => ['A' => $this->obj1, 'B' => $this->obj2], - 'Group 2' => ['C' => $this->obj3, 'D' => $this->obj4], - ] - ); + $list = $this->factory->createListFromChoices([ + 'Group 1' => ['A' => $this->obj1, 'B' => $this->obj2], + 'Group 2' => ['C' => $this->obj3, 'D' => $this->obj4], + ]); $this->assertObjectListWithGeneratedValues($list); } diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index aa3268c262f68..642b25ee12c23 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -450,6 +450,31 @@ public function testSetDataMapsViewDataToChildren() $form->setData('foo'); } + public function testSetDataDoesNotMapViewDataToChildrenWithLockedSetData() + { + $mapper = new PropertyPathMapper(); + $viewData = [ + 'firstName' => 'Fabien', + 'lastName' => 'Pot', + ]; + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->addViewTransformer(new FixedDataTransformer([ + '' => '', + 'foo' => $viewData, + ])) + ->getForm(); + + $form->add($child1 = $this->getBuilder('firstName')->getForm()); + $form->add($child2 = $this->getBuilder('lastName')->setData('Potencier')->setDataLocked(true)->getForm()); + + $form->setData('foo'); + + $this->assertSame('Fabien', $form->get('firstName')->getData()); + $this->assertSame('Potencier', $form->get('lastName')->getData()); + } + public function testSubmitSupportsDynamicAdditionAndRemovalOfChildren() { $form = $this->form; diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php index 6dde61e870414..311529c294799 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -11,16 +11,26 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\CoreExtension; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; use Symfony\Component\Form\NativeRequestHandler; use Symfony\Component\Form\RequestHandlerInterface; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\Translation\TranslatorInterface; class FileTypeTest extends BaseTypeTest { const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\FileType'; + protected function getExtensions() + { + $translator = $this->getMockBuilder(TranslatorInterface::class)->getMock(); + $translator->expects($this->any())->method('trans')->willReturnArgument(0); + + return array_merge(parent::getExtensions(), [new CoreExtension(null, null, $translator)]); + } + // https://github.com/symfony/symfony/pull/5028 public function testSetData() { @@ -184,15 +194,145 @@ public function requestHandlerProvider() ]; } - private function createUploadedFile(RequestHandlerInterface $requestHandler, $path, $originalName) + /** + * @dataProvider uploadFileErrorCodes + */ + public function testFailedFileUploadIsTurnedIntoFormErrorUsingHttpFoundationRequestHandler($errorCode, $expectedErrorMessage) + { + $requestHandler = new HttpFoundationRequestHandler(); + $form = $this->factory + ->createBuilder(static::TESTED_TYPE) + ->setRequestHandler($requestHandler) + ->getForm(); + $form->submit($this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo', $errorCode)); + + if (UPLOAD_ERR_OK === $errorCode) { + $this->assertTrue($form->isValid()); + } else { + $this->assertFalse($form->isValid()); + $this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage()); + } + } + + /** + * @dataProvider uploadFileErrorCodes + */ + public function testFailedFileUploadIsTurnedIntoFormErrorUsingNativeRequestHandler($errorCode, $expectedErrorMessage) + { + $form = $this->factory + ->createBuilder(static::TESTED_TYPE) + ->setRequestHandler(new NativeRequestHandler()) + ->getForm(); + $form->submit([ + 'name' => 'foo.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => $errorCode, + 'size' => 100, + ]); + + if (UPLOAD_ERR_OK === $errorCode) { + $this->assertTrue($form->isValid()); + } else { + $this->assertFalse($form->isValid()); + $this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage()); + } + } + + /** + * @dataProvider uploadFileErrorCodes + */ + public function testMultipleSubmittedFailedFileUploadsAreTurnedIntoFormErrorUsingHttpFoundationRequestHandler($errorCode, $expectedErrorMessage) + { + $requestHandler = new HttpFoundationRequestHandler(); + $form = $this->factory + ->createBuilder(static::TESTED_TYPE, null, [ + 'multiple' => true, + ]) + ->setRequestHandler($requestHandler) + ->getForm(); + $form->submit([ + $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo', $errorCode), + $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'bar', $errorCode), + ]); + + if (UPLOAD_ERR_OK === $errorCode) { + $this->assertTrue($form->isValid()); + } else { + $this->assertFalse($form->isValid()); + $this->assertCount(2, $form->getErrors()); + $this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage()); + $this->assertSame($expectedErrorMessage, $form->getErrors()[1]->getMessage()); + } + } + + /** + * @dataProvider uploadFileErrorCodes + */ + public function testMultipleSubmittedFailedFileUploadsAreTurnedIntoFormErrorUsingNativeRequestHandler($errorCode, $expectedErrorMessage) + { + $form = $this->factory + ->createBuilder(static::TESTED_TYPE, null, [ + 'multiple' => true, + ]) + ->setRequestHandler(new NativeRequestHandler()) + ->getForm(); + $form->submit([ + [ + 'name' => 'foo.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => $errorCode, + 'size' => 100, + ], + [ + 'name' => 'bar.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => $errorCode, + 'size' => 100, + ], + ]); + + if (UPLOAD_ERR_OK === $errorCode) { + $this->assertTrue($form->isValid()); + } else { + $this->assertFalse($form->isValid()); + $this->assertCount(2, $form->getErrors()); + $this->assertSame($expectedErrorMessage, $form->getErrors()[0]->getMessage()); + $this->assertSame($expectedErrorMessage, $form->getErrors()[1]->getMessage()); + } + } + + public function uploadFileErrorCodes() + { + return [ + 'no error' => [UPLOAD_ERR_OK, null], + 'upload_max_filesize ini directive' => [UPLOAD_ERR_INI_SIZE, 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.'], + 'MAX_FILE_SIZE from form' => [UPLOAD_ERR_FORM_SIZE, 'The file is too large.'], + 'partially uploaded' => [UPLOAD_ERR_PARTIAL, 'The file could not be uploaded.'], + 'no file upload' => [UPLOAD_ERR_NO_FILE, 'The file could not be uploaded.'], + 'missing temporary directory' => [UPLOAD_ERR_NO_TMP_DIR, 'The file could not be uploaded.'], + 'write failure' => [UPLOAD_ERR_CANT_WRITE, 'The file could not be uploaded.'], + 'stopped by extension' => [UPLOAD_ERR_EXTENSION, 'The file could not be uploaded.'], + ]; + } + + private function createUploadedFile(RequestHandlerInterface $requestHandler, $path, $originalName, $errorCode = 0) { if ($requestHandler instanceof HttpFoundationRequestHandler) { - return new UploadedFile($path, $originalName, null, null, true); + $class = new \ReflectionClass(UploadedFile::class); + + if (5 === $class->getConstructor()->getNumberOfParameters()) { + return new UploadedFile($path, $originalName, null, $errorCode, true); + } + + return new UploadedFile($path, $originalName, null, null, $errorCode, true); } return [ 'name' => $originalName, - 'error' => 0, + 'error' => $errorCode, 'type' => 'text/plain', 'tmp_name' => $path, 'size' => null, diff --git a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php index 2b134511830e7..dc082505a1bb0 100644 --- a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php @@ -56,4 +56,15 @@ protected function getInvalidFile() { return 'file:///etc/passwd'; } + + protected function getFailedUploadedFile($errorCode) + { + $class = new \ReflectionClass(UploadedFile::class); + + if (5 === $class->getConstructor()->getNumberOfParameters()) { + return new UploadedFile(__DIR__.'/../../Fixtures/foo', 'foo', null, $errorCode, true); + } + + return new UploadedFile(__DIR__.'/../../Fixtures/foo', 'foo', null, null, $errorCode, true); + } } diff --git a/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php index 25a4716650755..36638a124f072 100644 --- a/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php +++ b/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php @@ -275,4 +275,15 @@ protected function getInvalidFile() 'size' => '100', ]; } + + protected function getFailedUploadedFile($errorCode) + { + return [ + 'name' => 'upload.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => $errorCode, + 'size' => 100, + ]; + } } diff --git a/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php index 3b8afe415e37a..922375c46d208 100644 --- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php +++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -54,6 +54,26 @@ public function getIterator() class SimpleFormTest extends AbstractFormTest { + /** + * @dataProvider provideFormNames + */ + public function testGetPropertyPath($name, $propertyPath) + { + $config = new FormConfigBuilder($name, null, $this->dispatcher); + $form = new Form($config); + + $this->assertEquals($propertyPath, $form->getPropertyPath()); + } + + public function provideFormNames() + { + yield [null, null]; + yield ['', null]; + yield ['0', new PropertyPath('0')]; + yield [0, new PropertyPath('0')]; + yield ['name', new PropertyPath('name')]; + } + public function testDataIsInitializedToConfiguredValue() { $model = new FixedDataTransformer([ @@ -76,7 +96,7 @@ public function testDataIsInitializedToConfiguredValue() /** * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException - * @expectedExceptionMessage Unable to transform value for property path "name": No mapping for value "arg" + * @expectedExceptionMessage Unable to transform data for property path "name": No mapping for value "arg" */ public function testDataTransformationFailure() { diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php index 885375d733313..00a7874e3c741 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -127,9 +127,13 @@ private function getContainerDeprecationLogs() return []; } + if ('' === $logContent = trim(file_get_contents($file))) { + return []; + } + $bootTime = filemtime($file); $logs = []; - foreach (unserialize(file_get_contents($file)) as $log) { + foreach (unserialize($logContent) as $log) { $log['context'] = ['exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line'], $log['trace'], $log['count'])]; $log['timestamp'] = $bootTime; $log['priority'] = 100; diff --git a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php index 99149ab0be569..f48db705686b6 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php @@ -47,6 +47,7 @@ public function collect(Request $request, Response $response, \Exception $except 'token' => $response->headers->get('X-Debug-Token'), 'start_time' => $startTime * 1000, 'events' => [], + 'stopwatch_installed' => \class_exists(Stopwatch::class, false), ]; } @@ -139,6 +140,14 @@ public function getStartTime() return $this->data['start_time']; } + /** + * @return bool whether or not the stopwatch component is installed + */ + public function isStopwatchInstalled() + { + return $this->data['stopwatch_installed']; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index a51d9ecb882f1..6659e21793ffc 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -19,6 +19,7 @@ use Symfony\Component\Debug\ExceptionHandler; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\HttpKernel\Event\KernelEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -40,13 +41,13 @@ class DebugHandlersListener implements EventSubscriberInterface private $hasTerminatedWithException; /** - * @param callable|null $exceptionHandler A handler that will be called on Exception - * @param LoggerInterface|null $logger A PSR-3 logger - * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants - * @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value - * @param bool $scream Enables/disables screaming mode, where even silenced errors are logged - * @param string|array $fileLinkFormat The format for links to source files - * @param bool $scope Enables/disables scoping mode + * @param callable|null $exceptionHandler A handler that will be called on Exception + * @param LoggerInterface|null $logger A PSR-3 logger + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value + * @param bool $scream Enables/disables screaming mode, where even silenced errors are logged + * @param string|FileLinkFormatter|null $fileLinkFormat The format for links to source files + * @param bool $scope Enables/disables scoping mode */ public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true) { diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 762e4b5995e26..bdba3ad5aad5e 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.2.5'; - const VERSION_ID = 40205; + const VERSION = '4.2.6'; + const VERSION_ID = 40206; const MAJOR_VERSION = 4; const MINOR_VERSION = 2; - const RELEASE_VERSION = 5; + const RELEASE_VERSION = 6; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2019'; diff --git a/src/Symfony/Component/HttpKernel/Resources/welcome.html.php b/src/Symfony/Component/HttpKernel/Resources/welcome.html.php index caac7fd6ebb99..8fdc00506c860 100644 --- a/src/Symfony/Component/HttpKernel/Resources/welcome.html.php +++ b/src/Symfony/Component/HttpKernel/Resources/welcome.html.php @@ -51,21 +51,7 @@