From 19134c055469cdeb14bd8ea89b0fef3b2fa7a5f5 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Tue, 2 Apr 2019 10:51:52 +0200 Subject: [PATCH 01/77] [EventDispatcher] cleanup --- .../EventDispatcher/Debug/TraceableEventDispatcher.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 2339a68874a6d..752c8aa8293e6 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -290,10 +290,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); } From 86b382787d12d7504db881bc5472e44b15cc958f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 2 Apr 2019 15:47:25 +0200 Subject: [PATCH 02/77] updated CHANGELOG for 3.4.24 --- CHANGELOG-3.4.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md index 20ddc15fe2747..ba31a942c3a35 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-3.4.md @@ -7,6 +7,34 @@ in 3.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.4.0...v3.4.1 +* 3.4.24 (2019-04-02) + + * bug #30660 [Bridge][Twig] DebugCommand - fix escaping and filter (SpacePossum) + * bug #30720 Fix getSetMethodNormalizer to correctly ignore the attributes specified in "ignored_attributes" (Emmanuel BORGES) + * bug #30749 [Serializer] Added check of constuctor modifiers to AbstractNormalizer (NekaKawaii) + * bug #30776 [Routing] Fix routes annotation loading with glob pattern (snoob) + * bug #30773 [DependencyInjection] Fix hardcoded hotPathTagName (jderusse) + * bug #30737 [Validator] Improve constraint default option check (vudaltsov) + * bug #30736 [Validator] Fix annotation default for @Count and @Length (vudaltsov) + * bug #30620 [FrameworkBundle][HttpFoundation] make session service resettable (dmaicher) + * bug #30640 [Phpunit] fixed support for PHP 5.3 (fabpot) + * bug #30595 Do not validate child constraints if form has no validation groups (maryo) + * bug #30479 Check if Client exists when test.client does not exist, to provide clearer exception message (SerkanYildiz) + * feature #30584 [Intl] Add compile binary (ro0NL) + * bug #30487 Fix Cache error while using anonymous class (Emmanuel BORGES) + * bug #30548 Correct language code for ukrainian language (stanleyk) + * bug #30518 [Cache] Fix perf when using RedisCluster by reducing roundtrips to the servers (nicolas-grekas) + * bug #30515 [Cache] Only delete one key at a time when on Predis + Cluster (andrerom) + * bug #30507 [Routing] Fixed XML options resolution (Jules Pietri) + * bug #30506 [TwigBridge] remove deprecation triggered when using Twig 2.7 (nicolas-grekas) + * bug #30496 [PHPUnit-Bridge] override some Composer environment variables (nicoweb) + * bug #30505 [TwigBridge] Remove usages of the spaceless tag (nicolas-grekas) + * bug #30474 compatibility with phpunit8 (garak) + * bug #30497 [HttpKernel] Change default log level for output streams (yceruto) + * bug #30498 [translation] Update defaut format from yml to yaml (GaryPEGEOT) + * bug #30437 [Debug] detect annotations before blank docblock lines (xabbuh) + * bug #30392 [PropertyAccess] Fixed PropertyPathBuilder remove that fails to reset internal indexes (GregOriol) + * 3.4.23 (2019-03-03) * bug #26532 [HttpKernel] Correctly merging cache directives in HttpCache/ResponseCacheStrategy (aschempp) From 483ec24812d48e3299b27e64c632902085d87a64 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 2 Apr 2019 15:47:34 +0200 Subject: [PATCH 03/77] update CONTRIBUTORS for 3.4.24 --- CONTRIBUTORS.md | 126 ++++++++++++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 47 deletions(-) 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) From 3dc0a03d8bd96f763edc365894c8d4545a2411ee Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 2 Apr 2019 15:47:51 +0200 Subject: [PATCH 04/77] updated VERSION for 3.4.24 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 58beb735420fb..f283f0c7bdb60 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.24-DEV'; + const VERSION = '3.4.24'; const VERSION_ID = 30424; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; const RELEASE_VERSION = 24; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From 668f585df416d9481316eb89a75b9b80f59cd47f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 2 Apr 2019 21:03:08 +0200 Subject: [PATCH 05/77] bumped Symfony version to 3.4.25 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f283f0c7bdb60..0f98fdf0d76d9 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.24'; - const VERSION_ID = 30424; + const VERSION = '3.4.25-DEV'; + const VERSION_ID = 30425; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; - const RELEASE_VERSION = 24; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 25; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From a362b8b8384570519f5465cb45b8009d6afd53b3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 2 Apr 2019 21:06:21 +0200 Subject: [PATCH 06/77] bumped Symfony version to 4.2.6 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 762e4b5995e26..f71606e107fa6 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ 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-DEV'; + const VERSION_ID = 40206; const MAJOR_VERSION = 4; const MINOR_VERSION = 2; - const RELEASE_VERSION = 5; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 6; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '07/2019'; const END_OF_LIFE = '01/2020'; From ce38fe3e58096d12182fe919bd9fd4241c7f5b34 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Tue, 2 Apr 2019 21:50:10 +0200 Subject: [PATCH 07/77] Skip Glob brace test when GLOB_BRACE is unavailable --- src/Symfony/Component/Finder/Tests/FinderTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index aac35bc234195..c0eac6da12b59 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -339,6 +339,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(); From 2e4020c4303b395c1997d4a472602e41a9216a03 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 3 Apr 2019 12:07:21 +0200 Subject: [PATCH 08/77] [HttpKernel] Fix DebugHandlersListener constructor docblock --- .../EventListener/DebugHandlersListener.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index fed9e7e3febe7..df9df09c0bc32 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, $throwAt = E_ALL, $scream = true, $fileLinkFormat = null, $scope = true) { From 9b37793cbec60a8919db1fba38b11b3dc9aebaec Mon Sep 17 00:00:00 2001 From: Oleg Voronkovich Date: Mon, 1 Apr 2019 19:19:31 +0300 Subject: [PATCH 09/77] [Routing] Fix: annotation loader ignores method's default values --- .../Component/Routing/Loader/AnnotationClassLoader.php | 2 +- .../AnnotationFixtures/DefaultValueController.php | 8 ++++++++ .../Routing/Tests/Loader/AnnotationClassLoaderTest.php | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index 0f5dcdb82aa43..ae834bf4ba5c4 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -196,7 +196,7 @@ protected function addRoute(RouteCollection $collection, $annot, $globals, \Refl continue; } foreach ($paths as $locale => $path) { - if (false !== strpos($path, sprintf('{%s}', $param->name))) { + if (preg_match(sprintf('/\{%s(?:<.*?>)?\}/', preg_quote($param->name)), $path)) { $defaults[$param->name] = $param->getDefaultValue(); break; } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotationFixtures/DefaultValueController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotationFixtures/DefaultValueController.php index e66bee91831a4..f7e38c290d2e7 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotationFixtures/DefaultValueController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotationFixtures/DefaultValueController.php @@ -12,4 +12,12 @@ class DefaultValueController public function action($default = 'value') { } + + /** + * @Route("/hello/{name<\w+>}", name="hello_without_default") + * @Route("/hello/{name<\w+>?Symfony}", name="hello_with_default") + */ + public function hello(string $name = 'World') + { + } } diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php index 74dfcf8f49ff8..491c2b60071b5 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php @@ -136,9 +136,11 @@ public function testLocalizedPathRoutesWithExplicitPathPropety() public function testDefaultValuesForMethods() { $routes = $this->loader->load(DefaultValueController::class); - $this->assertCount(1, $routes); + $this->assertCount(3, $routes); $this->assertEquals('/{default}/path', $routes->get('action')->getPath()); $this->assertEquals('value', $routes->get('action')->getDefault('default')); + $this->assertEquals('Symfony', $routes->get('hello_with_default')->getDefault('name')); + $this->assertEquals('World', $routes->get('hello_without_default')->getDefault('name')); } public function testMethodActionControllers() From 03987f2ba5eff459f4a4165be367dcebdb4813c1 Mon Sep 17 00:00:00 2001 From: Jaik Dean Date: Fri, 5 Apr 2019 10:10:41 +0100 Subject: [PATCH 10/77] Define null return type for Constraint::getDefaultOption() --- src/Symfony/Component/Validator/Constraint.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php index 3f8b803f78972..349698eb1af59 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -232,12 +232,13 @@ public function addImplicitGroupName($group) * * Override this method to define a default option. * - * @return string + * @return string|null * * @see __construct() */ public function getDefaultOption() { + return null; } /** From 198b895eac309316424f63536be9ff4946a5e667 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sat, 30 Mar 2019 18:26:16 +0100 Subject: [PATCH 11/77] [Console] Fix inconsistent result for choice questions in non-interactive mode --- src/Symfony/Component/Console/Helper/QuestionHelper.php | 8 +++++++- .../Component/Console/Tests/Helper/QuestionHelperTest.php | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 9c200fe897ea9..2cd2adb5fe45c 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()) { diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 46cfe8184278d..56ba1c6891322 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); From f54389b95ce52f7ed1f49d8ec9be3be97e8bcc38 Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Sat, 6 Apr 2019 12:03:31 +0200 Subject: [PATCH 12/77] bugfix: the terminal state was wrong and not reseted --- src/Symfony/Component/Console/Helper/QuestionHelper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 9c200fe897ea9..b722ee4b8843c 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -254,6 +254,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) { From 94d41825fd5b3b5e9437c6c589e20f91715b5a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 6 Apr 2019 11:47:31 +0200 Subject: [PATCH 13/77] Ensure the parent process is always killed If you try to run the test suite but do not have a redis instance running, the parent process that was supposed to be killed will never be as the thing being thrown is not an exception. This results in the test suite hanging forever at the end. In this patch, the exception is thrown again, and then caught in the trait, and the parent gets killed as it should. --- .../Component/Lock/Tests/Store/BlockingStoreTestTrait.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php b/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php index 21f1245e7e685..139fc2511160d 100644 --- a/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php +++ b/src/Symfony/Component/Lock/Tests/Store/BlockingStoreTestTrait.php @@ -74,8 +74,8 @@ public function testBlockingLocks() // Block SIGHUP signal pcntl_sigprocmask(SIG_BLOCK, [SIGHUP]); - $store = $this->getStore(); try { + $store = $this->getStore(); $store->save($key); // send the ready signal to the parent posix_kill($parentPID, SIGHUP); @@ -87,7 +87,8 @@ public function testBlockingLocks() usleep($clockDelay); $store->delete($key); exit(0); - } catch (\Exception $e) { + } catch (\Throwable $e) { + posix_kill($parentPID, SIGHUP); exit(1); } } From 56429a6f082c057880e88ca665910b04d26e76d8 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Tue, 12 Mar 2019 21:16:42 +0100 Subject: [PATCH 14/77] [Form] various minor fixes --- .../Component/Form/AbstractExtension.php | 4 +- .../Component/Form/AbstractRendererEngine.php | 2 +- src/Symfony/Component/Form/Button.php | 25 ++- src/Symfony/Component/Form/ButtonBuilder.php | 15 +- .../Component/Form/CallbackTransformer.php | 22 +-- .../Component/Form/DataMapperInterface.php | 40 +++- .../Form/DataTransformerInterface.php | 41 ++-- .../Form/Extension/Core/Type/ButtonType.php | 4 +- .../Form/Extension/Core/Type/ChoiceType.php | 4 +- src/Symfony/Component/Form/Form.php | 182 ++++++++---------- src/Symfony/Component/Form/FormBuilder.php | 28 +-- .../Component/Form/FormConfigBuilder.php | 91 ++------- .../Form/FormConfigBuilderInterface.php | 16 +- .../Component/Form/FormConfigInterface.php | 20 +- src/Symfony/Component/Form/FormError.php | 2 +- .../Component/Form/FormErrorIterator.php | 12 +- src/Symfony/Component/Form/FormEvent.php | 4 + src/Symfony/Component/Form/FormEvents.php | 29 ++- src/Symfony/Component/Form/FormInterface.php | 86 ++++++--- src/Symfony/Component/Form/FormRegistry.php | 2 +- .../Form/FormRendererEngineInterface.php | 4 +- .../Component/Form/FormTypeGuesserChain.php | 2 +- .../Form/FormTypeGuesserInterface.php | 4 +- .../Component/Form/NativeRequestHandler.php | 2 + .../Component/Form/Tests/CompoundFormTest.php | 25 +++ .../Component/Form/Tests/SimpleFormTest.php | 22 ++- 26 files changed, 370 insertions(+), 318 deletions(-) diff --git a/src/Symfony/Component/Form/AbstractExtension.php b/src/Symfony/Component/Form/AbstractExtension.php index f4ac2e5ce2b0f..d5ab6bd48b883 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 649b600ddcf23..00bc254daefd3 100644 --- a/src/Symfony/Component/Form/AbstractRendererEngine.php +++ b/src/Symfony/Component/Form/AbstractRendererEngine.php @@ -133,7 +133,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 903e842b9e8b8..598749eeac27d 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; /** @@ -53,8 +50,6 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface private $options; /** - * Creates a new button builder. - * * @param string $name The name of the button * @param array $options The button's options * @@ -524,6 +519,7 @@ public function getFormConfig() */ public function getEventDispatcher() { + return null; } /** @@ -539,6 +535,7 @@ public function getName() */ public function getPropertyPath() { + return null; } /** @@ -606,6 +603,7 @@ public function getModelTransformers() */ public function getDataMapper() { + return null; } /** @@ -643,6 +641,7 @@ public function getErrorBubbling() */ public function getEmptyData() { + return null; } /** @@ -685,6 +684,7 @@ public function getAttribute($name, $default = null) */ public function getData() { + return null; } /** @@ -692,6 +692,7 @@ public function getData() */ public function getDataClass() { + return null; } /** @@ -709,6 +710,7 @@ public function getDataLocked() */ public function getFormFactory() { + throw new BadMethodCallException('Buttons do not support adding children.'); } /** @@ -716,6 +718,7 @@ public function getFormFactory() */ public function getAction() { + return null; } /** @@ -723,6 +726,7 @@ public function getAction() */ public function getMethod() { + return null; } /** @@ -730,6 +734,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 8155e4dca8ed1..6db5bfb183351 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/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 28320ca38942e..2ad0859e880fa 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -328,8 +328,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/Form.php b/src/Symfony/Component/Form/Form.php index cfa7eb5b6a6d8..5f417d6d90901 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 { /** - * 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 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; } } @@ -757,8 +749,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() { @@ -826,29 +817,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'); @@ -878,10 +846,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; @@ -1030,13 +1016,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) { @@ -1045,7 +1031,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; @@ -1082,7 +1068,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) { @@ -1091,12 +1077,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) { @@ -1113,13 +1099,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 00affbf6d8c3d..13b1ea3b36420 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -38,8 +38,6 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB private $unresolvedChildren = []; /** - * Creates a new form builder. - * * @param string $name * @param string|null $dataClass * @param EventDispatcherInterface $dispatcher @@ -81,10 +79,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; } @@ -152,15 +147,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]); } /** @@ -232,7 +219,7 @@ public function getForm() /** * {@inheritdoc} * - * @return FormBuilderInterface[] + * @return FormBuilderInterface[]|\Traversable */ public function getIterator() { @@ -252,12 +239,11 @@ public function getIterator() */ private function resolveChild($name) { - $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); } /** @@ -266,7 +252,7 @@ private function resolveChild($name) 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 fc864fc617173..fea1d70cd8ee4 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -47,44 +47,19 @@ class FormConfigBuilder implements FormConfigBuilderInterface 'PATCH', ]; - /** - * @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; /** @@ -92,34 +67,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; /** @@ -127,9 +84,6 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $emptyData; - /** - * @var array - */ private $attributes = []; /** @@ -142,39 +96,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; /** @@ -616,7 +557,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; } @@ -662,7 +603,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; } @@ -676,7 +617,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; } @@ -690,7 +631,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; } @@ -704,7 +645,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; } @@ -746,7 +687,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; } @@ -774,7 +715,7 @@ public function setAction($action) throw new BadMethodCallException('The config builder cannot be modified anymore.'); } - $this->action = $action; + $this->action = (string) $action; return $this; } @@ -790,7 +731,7 @@ public function setMethod($method) $upperCaseMethod = strtoupper($method); - if (!\in_array($upperCaseMethod, self::$allowedMethods)) { + if (!\in_array($upperCaseMethod, self::$allowedMethods, true)) { throw new InvalidArgumentException(sprintf('The form method is "%s", but should be one of "%s".', $method, implode('", "', self::$allowedMethods))); } @@ -846,7 +787,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 @@ -872,7 +813,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 7717c19019a3c..98a1e29a83a82 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($message, $messageTemplate = null, array $messageParameters = [], $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 b795f95dcfafc..c4c613f567f19 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_bind'; /** - * 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.bind'; /** - * 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_bind'; @@ -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/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 2add7938443aa..022b60aa1368d 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 9ae5233f46273..2f0b868dd0098 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 f38ba8e5d431a..5ce8794248801 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/NativeRequestHandler.php b/src/Symfony/Component/Form/NativeRequestHandler.php index 246ea92ccee50..f3c0d839fec66 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) { diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index ba4f26ecc9d8c..edc3aeda14b2c 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/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php index 214ff3beab241..5233a8f1aae18 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(new PropertyPath($propertyPath), $form->getPropertyPath()); + } + + public function provideFormNames() + { + yield [null, null]; + yield ['', null]; + yield ['0', '0']; + yield [0, '0']; + yield ['name', '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() { From 16289952687530134c031600733735b26a07ed18 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sat, 6 Apr 2019 16:21:02 +0200 Subject: [PATCH 15/77] fix merge --- src/Symfony/Component/Form/FormConfigBuilder.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php index 4a0f26784bf48..fa7bac32bb3c9 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -34,10 +34,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private static $nativeRequestHandler; - /** - * @var bool - */ - + protected $locked = false; private $dispatcher; private $name; From ab55e8e7ef0e6b4eb91730696dbd24ee5ead688f Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Sat, 6 Apr 2019 15:56:41 +0200 Subject: [PATCH 16/77] Uses the SerializerStamp when deserializing the envelope --- .../Serialization/SerializerTest.php | 55 +++++++++++++++++-- .../Transport/Serialization/Serializer.php | 20 ++++++- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php index d68103f7ca682..34c0e579f82b8 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/SerializerTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Messenger\Transport\Serialization\Serializer; use Symfony\Component\Serializer as SerializerComponent; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Symfony\Component\Serializer\SerializerInterface as SerializerComponentInterface; class SerializerTest extends TestCase { @@ -74,11 +75,23 @@ public function testUsesTheCustomFormatAndContext() public function testEncodedWithSymfonySerializerForStamps() { - $serializer = new Serializer(); + $serializer = new Serializer( + $symfonySerializer = $this->createMock(SerializerComponentInterface::class) + ); - $envelope = (new Envelope(new DummyMessage('Hello'))) + $envelope = (new Envelope($message = new DummyMessage('test'))) ->with($serializerStamp = new SerializerStamp([ObjectNormalizer::GROUPS => ['foo']])) - ->with($validationStamp = new ValidationStamp(['foo', 'bar'])) + ->with($validationStamp = new ValidationStamp(['foo', 'bar'])); + + $symfonySerializer + ->expects($this->at(2)) + ->method('serialize')->with( + $message, + 'json', + [ + ObjectNormalizer::GROUPS => ['foo'], + ] + ) ; $encoded = $serializer->encode($envelope); @@ -88,10 +101,40 @@ public function testEncodedWithSymfonySerializerForStamps() $this->assertArrayHasKey('type', $encoded['headers']); $this->assertArrayHasKey('X-Message-Stamp-'.SerializerStamp::class, $encoded['headers']); $this->assertArrayHasKey('X-Message-Stamp-'.ValidationStamp::class, $encoded['headers']); + } - $decoded = $serializer->decode($encoded); + public function testDecodeWithSymfonySerializerStamp() + { + $serializer = new Serializer( + $symfonySerializer = $this->createMock(SerializerComponentInterface::class) + ); + + $symfonySerializer + ->expects($this->at(0)) + ->method('deserialize') + ->with('[{"context":{"groups":["foo"]}}]', SerializerStamp::class.'[]', 'json', []) + ->willReturn([new SerializerStamp(['groups' => ['foo']])]) + ; + + $symfonySerializer + ->expects($this->at(1)) + ->method('deserialize')->with( + '{}', + DummyMessage::class, + 'json', + [ + ObjectNormalizer::GROUPS => ['foo'], + ] + ) + ->willReturn(new DummyMessage('test')) + ; - $this->assertEquals($serializerStamp, $decoded->last(SerializerStamp::class)); - $this->assertEquals($validationStamp, $decoded->last(ValidationStamp::class)); + $serializer->decode([ + 'body' => '{}', + 'headers' => [ + 'type' => DummyMessage::class, + 'X-Message-Stamp-'.SerializerStamp::class => '[{"context":{"groups":["foo"]}}]', + ], + ]); } } diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php index 3f0979bcef1d3..cc8ac58bb2ddc 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php @@ -15,6 +15,7 @@ use Symfony\Component\Messenger\Exception\InvalidArgumentException; use Symfony\Component\Messenger\Exception\LogicException; use Symfony\Component\Messenger\Stamp\SerializerStamp; +use Symfony\Component\Messenger\Stamp\StampInterface; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; @@ -69,10 +70,11 @@ public function decode(array $encodedEnvelope): Envelope } $stamps = $this->decodeStamps($encodedEnvelope); + $serializerStamp = $this->findFirstSerializerStamp($stamps); $context = $this->context; - if (isset($stamps[SerializerStamp::class])) { - $context = end($stamps[SerializerStamp::class])->getContext() + $context; + if (null !== $serializerStamp) { + $context = $serializerStamp->getContext() + $context; } $message = $this->serializer->deserialize($encodedEnvelope['body'], $encodedEnvelope['headers']['type'], $this->format, $context); @@ -129,4 +131,18 @@ private function encodeStamps(Envelope $envelope): array return $headers; } + + /** + * @param StampInterface[] $stamps + */ + private function findFirstSerializerStamp(array $stamps): ?SerializerStamp + { + foreach ($stamps as $stamp) { + if ($stamp instanceof SerializerStamp) { + return $stamp; + } + } + + return null; + } } From 83c661d5cf18d572bdf4a286af8cec5a51e9c9b3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 6 Apr 2019 16:32:25 +0200 Subject: [PATCH 17/77] fix tests --- src/Symfony/Component/Form/Tests/SimpleFormTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php index 5233a8f1aae18..32c21c8432b7a 100644 --- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php +++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -62,16 +62,16 @@ public function testGetPropertyPath($name, $propertyPath) $config = new FormConfigBuilder($name, null, $this->dispatcher); $form = new Form($config); - $this->assertEquals(new PropertyPath($propertyPath), $form->getPropertyPath()); + $this->assertEquals($propertyPath, $form->getPropertyPath()); } public function provideFormNames() { yield [null, null]; yield ['', null]; - yield ['0', '0']; - yield [0, '0']; - yield ['name', 'name']; + yield ['0', new PropertyPath('0')]; + yield [0, new PropertyPath('0')]; + yield ['name', new PropertyPath('name')]; } public function testDataIsInitializedToConfiguredValue() From 48f020f9e38fe47299686d568f8d3e8ecc30f98d Mon Sep 17 00:00:00 2001 From: Dany Maillard Date: Sat, 6 Apr 2019 17:11:40 +0200 Subject: [PATCH 18/77] Fix table trailing backslash --- .../Component/Console/Helper/Table.php | 3 +++ .../Console/Tests/Helper/TableTest.php | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 99aa83d4c8173..0b8f4174b3332 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; @@ -528,6 +529,8 @@ private function buildTableRows($rows) 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) { diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index 43b90fd073c91..539804fb031c7 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)); From dde74aa8c9368a72a8b0e53033baa29843e7f312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 6 Apr 2019 17:06:10 +0200 Subject: [PATCH 19/77] Restore previous state for libxml option Whenever libxml_use_internal_errors() is called, the previous value for it should be restored. This used to be the case in this piece of code, but it was wrongly removed in e53bf5839b625e5ab92ffc4859331f4f46161bc6 , which has the nasty side effect of making the Validator component test suite break with this message: Validation failed: No DTD found! --- .../Component/Translation/Command/XliffLintCommand.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/Command/XliffLintCommand.php b/src/Symfony/Component/Translation/Command/XliffLintCommand.php index f8791a490ec04..9bea4d9499392 100644 --- a/src/Symfony/Component/Translation/Command/XliffLintCommand.php +++ b/src/Symfony/Component/Translation/Command/XliffLintCommand.php @@ -115,7 +115,7 @@ private function validate($content, $file = null) return ['file' => $file, 'valid' => true]; } - libxml_use_internal_errors(true); + $internal = libxml_use_internal_errors(true); $document = new \DOMDocument(); $document->loadXML($content); @@ -143,6 +143,9 @@ private function validate($content, $file = null) ]; } + libxml_clear_errors(); + libxml_use_internal_errors($internal); + return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors]; } From 5d4e3a2d57c1ea1be3a12e4695ac8eadf1a8209d Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Sat, 6 Apr 2019 14:30:12 +0200 Subject: [PATCH 20/77] [WIP] [DependencyInjection] Fix a wrong error when using a factory and a call --- .../Compiler/AutowirePass.php | 10 ++++- .../Compiler/ResolveBindingsPass.php | 9 +++- .../Tests/Compiler/AutowirePassTest.php | 13 ++++++ .../Compiler/ResolveBindingsPassTest.php | 44 +++++++++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 252b304f118f5..c8e7a0f575f4e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -181,7 +181,15 @@ private function autowireCalls(\ReflectionClass $reflectionClass, array $methodC 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/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index 20b262b6d461c..bb67eb16794ae 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -115,7 +115,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/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 6bd49fa5c6f82..6235ed50be25c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic; use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\HttpKernel\HttpKernelInterface; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -605,6 +606,18 @@ public function testSetterInjection() ); } + public function testWithNonExistingSetterAndAutowiring() + { + $container = new ContainerBuilder(); + + $definition = $container->register(HttpKernelInterface::class, HttpKernelInterface::class)->setAutowired(true); + $definition->addMethodCall('setLogger'); + $this->expectException(RuntimeException::class); + (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 5118b416f93fa..84b3d6c652fb6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -16,11 +16,14 @@ 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\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists; use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\HttpKernel\HttpKernelInterface; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -111,4 +114,45 @@ public function testScalarSetter() $this->assertEquals([['setDefaultLocale', ['fr']]], $definition->getMethodCalls()); } + + public function testWithNonExistingSetterAndBinding() + { + $container = new ContainerBuilder(); + + $bindings = [ + '$c' => (new Definition('logger'))->setFactory('logger'), + ]; + + $definition = $container->register(HttpKernelInterface::class, HttpKernelInterface::class); + $definition->addMethodCall('setLogger'); + $definition->setBindings($bindings); + $this->expectException(RuntimeException::class); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + } + + public function testTupleBinding() + { + $container = new ContainerBuilder(); + + $bindings = [ + '$c' => new BoundArgument(new Reference('bar')), + CaseSensitiveClass::class.'$c' => new BoundArgument(new Reference('foo')), + ]; + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->addMethodCall('setSensitiveClass'); + $definition->addMethodCall('setAnotherC'); + $definition->setBindings($bindings); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + + $expected = [ + ['setSensitiveClass', [new Reference('foo')]], + ['setAnotherC', [new Reference('bar')]], + ]; + $this->assertEquals($expected, $definition->getMethodCalls()); + } } From 1a21ca73625fe19d056fde1e7ca1ebd5919bd597 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 6 Apr 2019 14:28:07 +0200 Subject: [PATCH 21/77] turn failed file uploads into form errors --- .../Form/Extension/Core/Type/FileType.php | 115 +++++++++++++++++ .../HttpFoundationRequestHandler.php | 13 ++ .../Component/Form/NativeRequestHandler.php | 24 ++++ .../Form/Tests/AbstractRequestHandlerTest.php | 24 ++++ .../Extension/Core/Type/FileTypeTest.php | 122 ++++++++++++++++++ .../HttpFoundationRequestHandlerTest.php | 5 + .../Form/Tests/NativeRequestHandlerTest.php | 11 ++ 7 files changed, 314 insertions(+) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 59c72889d6683..a7f912d5e3109 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -13,6 +13,7 @@ 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; @@ -22,6 +23,15 @@ 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', + ]; + /** * {@inheritdoc} */ @@ -43,6 +53,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 +68,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 +132,103 @@ 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.'; + } + + return new FormError($messageTemplate, $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 75ee65443f450..cf255792fee83 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/NativeRequestHandler.php b/src/Symfony/Component/Form/NativeRequestHandler.php index f3c0d839fec66..5997fba67df15 100644 --- a/src/Symfony/Component/Form/NativeRequestHandler.php +++ b/src/Symfony/Component/Form/NativeRequestHandler.php @@ -135,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/Extension/Core/Type/FileTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php index ea012c451e885..73d39ace025db 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -184,6 +184,128 @@ public function requestHandlerProvider() ]; } + /** + * @dataProvider uploadFileErrorCodes + */ + public function testFailedFileUploadIsTurnedIntoFormErrorUsingHttpFoundationRequestHandler($errorCode, $expectedErrorMessage) + { + $form = $this->factory + ->createBuilder(static::TESTED_TYPE) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->submit(new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'foo', null, null, $errorCode, true)); + + 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) + { + $form = $this->factory + ->createBuilder(static::TESTED_TYPE, null, [ + 'multiple' => true, + ]) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->submit([ + new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'foo', null, null, $errorCode, true), + new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'bar', null, null, $errorCode, true), + ]); + + 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) { if ($requestHandler instanceof HttpFoundationRequestHandler) { diff --git a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php index 2b134511830e7..0e5389568e5ce 100644 --- a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php @@ -56,4 +56,9 @@ protected function getInvalidFile() { return 'file:///etc/passwd'; } + + protected function getFailedUploadedFile($errorCode) + { + 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, + ]; + } } From a5f1afca15e26b1b886d85a357e1370d7c479e47 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Sat, 6 Apr 2019 19:05:56 +0200 Subject: [PATCH 22/77] [Translator] Warm up the translations cache in dev --- .../Component/Translation/DataCollectorTranslator.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index 6f826dfaa6ff4..c0cc05996aa3e 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -11,12 +11,13 @@ namespace Symfony\Component\Translation; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; /** * @author Abdellatif Ait boudad */ -class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface +class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface, WarmableInterface { const MESSAGE_DEFINED = 0; const MESSAGE_MISSING = 1; @@ -87,6 +88,14 @@ public function getCatalogue($locale = null) return $this->translator->getCatalogue($locale); } + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + return $this->translator->warmUp($cacheDir); + } + /** * Gets the fallback locales. * From b54abfc64305a9aab2d9570a159d17587af3e2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20de=20Guillebon?= Date: Sat, 6 Apr 2019 18:57:58 +0200 Subject: [PATCH 23/77] Fix wrong dump for PO files --- src/Symfony/Component/Translation/Dumper/PoFileDumper.php | 2 +- .../Component/Translation/Tests/Dumper/PoFileDumperTest.php | 2 +- .../Component/Translation/Tests/Loader/PoFileLoaderTest.php | 2 +- .../Component/Translation/Tests/fixtures/resources.po | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Translation/Dumper/PoFileDumper.php b/src/Symfony/Component/Translation/Dumper/PoFileDumper.php index e9673b6d29908..0f7e6fa834bc2 100644 --- a/src/Symfony/Component/Translation/Dumper/PoFileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/PoFileDumper.php @@ -40,7 +40,7 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti $newLine = true; } $output .= sprintf('msgid "%s"'."\n", $this->escape($source)); - $output .= sprintf('msgstr "%s"', $this->escape($target)); + $output .= sprintf('msgstr "%s"'."\n", $this->escape($target)); } return $output; diff --git a/src/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php index d694a6dd3fb11..960ec2df6500c 100644 --- a/src/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php +++ b/src/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php @@ -20,7 +20,7 @@ class PoFileDumperTest extends TestCase public function testFormatCatalogue() { $catalogue = new MessageCatalogue('en'); - $catalogue->add(['foo' => 'bar']); + $catalogue->add(['foo' => 'bar', 'bar' => 'foo']); $dumper = new PoFileDumper(); diff --git a/src/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php b/src/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php index 4bf2ee6545b6f..d8e2c1993ba1c 100644 --- a/src/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php +++ b/src/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php @@ -23,7 +23,7 @@ public function testLoad() $resource = __DIR__.'/../fixtures/resources.po'; $catalogue = $loader->load($resource, 'en', 'domain1'); - $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1')); + $this->assertEquals(['foo' => 'bar', 'bar' => 'foo'], $catalogue->all('domain1')); $this->assertEquals('en', $catalogue->getLocale()); $this->assertEquals([new FileResource($resource)], $catalogue->getResources()); } diff --git a/src/Symfony/Component/Translation/Tests/fixtures/resources.po b/src/Symfony/Component/Translation/Tests/fixtures/resources.po index ccfce6bb98d46..a20e619828ce6 100644 --- a/src/Symfony/Component/Translation/Tests/fixtures/resources.po +++ b/src/Symfony/Component/Translation/Tests/fixtures/resources.po @@ -5,4 +5,7 @@ msgstr "" "Language: en\n" msgid "foo" -msgstr "bar" \ No newline at end of file +msgstr "bar" + +msgid "bar" +msgstr "foo" From 7113a53e197193537eabea85739a61d7304c5148 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 6 Apr 2019 19:28:56 +0200 Subject: [PATCH 24/77] fix horizontal spacing of inlined Bootstrap forms --- .../Resources/views/Form/bootstrap_3_layout.html.twig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 e1164cdfbce1b..708e149bce82b 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 @@ -108,10 +108,10 @@ {% block form_row -%}
- {{- form_label(form) -}} - {{- form_widget(form) -}} - {{- form_errors(form) -}} -
+ {{- form_label(form) }} {# -#} + {{ form_widget(form) }} {# -#} + {{ form_errors(form) }} {# -#} + {# -#} {%- endblock form_row %} {% block button_row -%} From 8a9c2c86ef4033bc7bc38ab2f508942476a7f92b Mon Sep 17 00:00:00 2001 From: dFayet Date: Thu, 4 Apr 2019 09:40:26 +0200 Subject: [PATCH 25/77] [Profiler] Fix dark theme elements color --- .../Twig/DataCollector/TwigDataCollector.php | 2 + .../Resources/views/Exception/trace.html.twig | 8 +-- .../Resources/views/exception.css.twig | 49 +++++++++++-------- .../views/Collector/exception.css.twig | 6 +-- .../Resources/views/Collector/form.html.twig | 29 ++++++++--- .../views/Collector/messenger.html.twig | 7 ++- .../views/Collector/validator.html.twig | 7 ++- .../views/Profiler/profiler.css.twig | 46 ++++++++++++----- 8 files changed, 105 insertions(+), 49 deletions(-) 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/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/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..faef05d963df2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig @@ -144,7 +144,12 @@ 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/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 6fdcc77a0ed75..3cd09336221bd 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; @@ -1060,8 +1080,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 +1138,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; From a53e0fe1cdce66ad9ebb9a25e661337e006dfb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 6 Apr 2019 21:12:52 +0200 Subject: [PATCH 26/77] Use a class name that does not actually exist Using "Foo", a class name that corresponds to no less than 22 fixture classes, results in the first found "Foo" being loaded when one is found by the ClassNotFoundFatalErrorHandler error handler, I am not sure exactly why, but it is not really a big issue because this is a fatal error handler and execution is not supposed to continue after that. Except that is very much the case when running the whole test suite sequentially with ./phpunit . Then we arrive to the DI component test suite, and a failure happens because \\foo was not supposed to be defined: > Failed asserting that exception message 'The definition for "\foo" has > no class attribute, and appears to reference a class or interface in the > global namespace. Leaving out the "class" attribute is only allowed for > namespaced classes. Please specify the class attribute explicitly to get > rid of this error.' contains 'The definition for "\foo" has no class.'. --- src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index c1dea75bbd90e..15de37c7b7808 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -525,7 +525,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) { @@ -535,7 +535,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()); } /** From 6d51a04b11ee6cf85cdc5c1b6fb36ab1cb456308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 6 Apr 2019 18:22:13 +0200 Subject: [PATCH 27/77] Run test in separate process This test calls code that defines some environment variables, which in turn trigger the registration of a the deprecation error handler, which causes unexpected issues when testing other components. --- src/Symfony/Component/BrowserKit/Tests/ClientTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php index ab6d118589c11..a21a9481a7ac6 100644 --- a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php @@ -621,6 +621,9 @@ public function testRestart() $this->assertEquals([], $client->getCookieJar()->all(), '->restart() clears the cookies'); } + /** + * @runInSeparateProcess + */ public function testInsulatedRequests() { $client = new TestClient(); From e55f6e6f5e63cf840caaf592e8b5d7934ffb953c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 6 Apr 2019 20:39:34 +0200 Subject: [PATCH 28/77] fix tests --- .../Tests/Compiler/AutowirePassTest.php | 10 ++++-- .../Compiler/ResolveBindingsPassTest.php | 33 +++---------------- .../Translation/DataCollectorTranslator.php | 4 ++- .../Component/Translation/composer.json | 2 ++ 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 6235ed50be25c..31fa665ae7a85 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -21,9 +21,9 @@ 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; -use Symfony\Component\HttpKernel\HttpKernelInterface; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -606,13 +606,17 @@ 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(HttpKernelInterface::class, HttpKernelInterface::class)->setAutowired(true); + $definition = $container->register(CaseSensitiveClass::class, CaseSensitiveClass::class)->setAutowired(true); $definition->addMethodCall('setLogger'); - $this->expectException(RuntimeException::class); + (new ResolveClassPass())->process($container); (new AutowireRequiredMethodsPass())->process($container); (new AutowirePass())->process($container); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index 84b3d6c652fb6..7bbecf6207f46 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -17,13 +17,11 @@ use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists; use Symfony\Component\DependencyInjection\TypedReference; -use Symfony\Component\HttpKernel\HttpKernelInterface; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -115,6 +113,10 @@ public function testScalarSetter() $this->assertEquals([['setDefaultLocale', ['fr']]], $definition->getMethodCalls()); } + /** + * @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(); @@ -123,36 +125,11 @@ public function testWithNonExistingSetterAndBinding() '$c' => (new Definition('logger'))->setFactory('logger'), ]; - $definition = $container->register(HttpKernelInterface::class, HttpKernelInterface::class); - $definition->addMethodCall('setLogger'); - $definition->setBindings($bindings); - $this->expectException(RuntimeException::class); - - $pass = new ResolveBindingsPass(); - $pass->process($container); - } - - public function testTupleBinding() - { - $container = new ContainerBuilder(); - - $bindings = [ - '$c' => new BoundArgument(new Reference('bar')), - CaseSensitiveClass::class.'$c' => new BoundArgument(new Reference('foo')), - ]; - $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/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index c0cc05996aa3e..7eaf928e7ff93 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -93,7 +93,9 @@ public function getCatalogue($locale = null) */ public function warmUp($cacheDir) { - return $this->translator->warmUp($cacheDir); + if ($this->translator instanceof WarmableInterface) { + $this->translator->warmUp($cacheDir); + } } /** diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index 45ff664ca4391..93c1236f27323 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -22,7 +22,9 @@ "require-dev": { "symfony/config": "~2.8|~3.0|~4.0", "symfony/dependency-injection": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", "symfony/intl": "^2.8.18|^3.2.5|~4.0", + "symfony/var-dumper": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0", "symfony/finder": "~2.8|~3.0|~4.0", "psr/log": "~1.0" From 6a732dc0316b51812b5353eb5a2bc211d383c375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Egyed?= Date: Sat, 6 Apr 2019 23:15:33 +0200 Subject: [PATCH 29/77] [Validator] Translate unique collection message to Hungarian --- .../Validator/Resources/translations/validators.hu.xlf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf index c527c58d2d9e9..50d433f665ec5 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf @@ -334,6 +334,10 @@ This value should be valid JSON. Ez az érték érvényes JSON kell, hogy legyen. + + This collection should contain only unique elements. + Ez a gyűjtemény csak egyedi elemeket tartalmazhat. + From 79b1fb83338b73af25f1949bfcea54a6b74905c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 7 Apr 2019 09:54:46 +0200 Subject: [PATCH 30/77] Handle case where no translations were found Right now, the program emits a warning when trying to use max() on an empty array. --- .../Translation/Resources/bin/translation-status.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/Translation/Resources/bin/translation-status.php b/src/Symfony/Component/Translation/Resources/bin/translation-status.php index acc8b4e227222..0d37c3e0aa38b 100644 --- a/src/Symfony/Component/Translation/Resources/bin/translation-status.php +++ b/src/Symfony/Component/Translation/Resources/bin/translation-status.php @@ -159,6 +159,11 @@ function printTitle($title) function printTable($translations, $verboseOutput) { + if (0 === count($translations)) { + echo 'No translations found'; + + return; + } $longestLocaleNameLength = max(array_map('strlen', array_keys($translations))); foreach ($translations as $locale => $translation) { From 7db9200279319a5730d7bd44cd2db137d35dde43 Mon Sep 17 00:00:00 2001 From: Philipp Cordes Date: Sun, 6 Jan 2019 15:04:00 +0100 Subject: [PATCH 31/77] [Validator] Only traverse arrays that are cascaded into --- .../Validator/Constraints/Collection.php | 2 +- .../Tests/Validator/AbstractValidatorTest.php | 24 +++++++++++++++++++ .../RecursiveContextualValidator.php | 17 +++++-------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/Collection.php b/src/Symfony/Component/Validator/Constraints/Collection.php index 21427722d9491..86418690b8d2b 100644 --- a/src/Symfony/Component/Validator/Constraints/Collection.php +++ b/src/Symfony/Component/Validator/Constraints/Collection.php @@ -68,7 +68,7 @@ protected function initializeNestedConstraints() } if (!$field instanceof Optional && !$field instanceof Required) { - $this->fields[$fieldName] = $field = new Required($field); + $this->fields[$fieldName] = new Required($field); } } } diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php index ecfc10feb643a..884ccc7da0f95 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php @@ -589,6 +589,30 @@ public function testRecursiveArrayReference() $this->assertNull($violations[0]->getCode()); } + public function testOnlyCascadedArraysAreTraversed() + { + $entity = new Entity(); + $entity->reference = ['key' => new Reference()]; + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Callback([ + 'callback' => function () {}, + 'groups' => 'Group', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + public function testArrayTraversalCannotBeDisabled() { $entity = new Entity(); diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 50253c50e1bb4..dc139c36d4254 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -352,24 +352,18 @@ private function validateObject($object, $propertyPath, array $groups, $traversa * Validates each object in a collection against the constraints defined * for their classes. * - * If the parameter $recursive is set to true, nested {@link \Traversable} - * objects are iterated as well. Nested arrays are always iterated, - * regardless of the value of $recursive. + * Nested arrays are also iterated. * * @param iterable $collection The collection * @param string $propertyPath The current property path * @param (string|GroupSequence)[] $groups The validated groups * @param ExecutionContextInterface $context The current execution context - * - * @see ClassNode - * @see CollectionNode */ private function validateEachObjectIn($collection, $propertyPath, array $groups, ExecutionContextInterface $context) { foreach ($collection as $key => $value) { if (\is_array($value)) { - // Arrays are always cascaded, independent of the specified - // traversal strategy + // Also traverse nested arrays $this->validateEachObjectIn( $value, $propertyPath.'['.$key.']', @@ -599,7 +593,8 @@ private function validateClassNode($object, $cacheKey, ClassMetadataInterface $m * in the passed metadata object. Then, if the value is an instance of * {@link \Traversable} and the selected traversal strategy permits it, * the value is traversed and each nested object validated against its own - * constraints. Arrays are always traversed. + * constraints. If the value is an array, it is traversed regardless of + * the given strategy. * * @param mixed $value The validated value * @param object|null $object The current object @@ -658,8 +653,8 @@ private function validateGenericNode($value, $object, $cacheKey, MetadataInterfa $cascadingStrategy = $metadata->getCascadingStrategy(); - // Quit unless we have an array or a cascaded object - if (!\is_array($value) && !($cascadingStrategy & CascadingStrategy::CASCADE)) { + // Quit unless we cascade + if (!($cascadingStrategy & CascadingStrategy::CASCADE)) { return; } From 331b601fed1cec3d323b7f365878bb830c2d878c Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Sun, 7 Apr 2019 09:35:09 +0200 Subject: [PATCH 32/77] [3.4] [Validator] Add missing french validation translations. --- .../Resources/translations/validators.fr.xlf | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf index 7b1799d53315c..d1601f72df95e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf @@ -334,6 +334,34 @@ This value should be valid JSON. Cette valeur doit être un JSON valide. + + This collection should contain only unique elements. + Cette collection ne doit pas comporter de doublons. + + + This value should be positive. + Cette valeur doit être strictement positive. + + + This value should be either positive or zero. + Cette valeur doit être supérieure ou égale à zéro. + + + This value should be negative. + Cette valeur doit être strictement négative. + + + This value should be either negative or zero. + Cette valeur doit être inférieure ou égale à zéro. + + + This value is not a valid timezone. + Cette valeur n'est pas un fuseau horaire valide. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Ce mot de passe a été révélé par une faille de sécurité et ne doit plus être utilisé. Veuillez utiliser un autre mot de passe. + From 98b9be9b6accfbb543dd57fedf9da980f5addb2c Mon Sep 17 00:00:00 2001 From: David Maicher Date: Sat, 6 Apr 2019 18:45:14 +0200 Subject: [PATCH 33/77] [Cache] fix using ProxyAdapter inside TagAwareAdapter --- .../Component/Cache/Adapter/ProxyAdapter.php | 24 +++++++++--- .../Cache/Adapter/TagAwareAdapter.php | 1 - ...TagAwareAndProxyAdapterIntegrationTest.php | 38 +++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index 9060523609159..0b7918287e90a 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -45,12 +45,15 @@ public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defa function ($key, $innerItem) use ($defaultLifetime, $poolHash) { $item = new CacheItem(); $item->key = $key; - $item->value = $innerItem->get(); - $item->isHit = $innerItem->isHit(); $item->defaultLifetime = $defaultLifetime; - $item->innerItem = $innerItem; $item->poolHash = $poolHash; - $innerItem->set(null); + + if (null !== $innerItem) { + $item->value = $innerItem->get(); + $item->isHit = $innerItem->isHit(); + $item->innerItem = $innerItem; + $innerItem->set(null); + } return $item; }, @@ -156,7 +159,18 @@ private function doSave(CacheItemInterface $item, $method) if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { $expiry = time() + $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"]); + } + $innerItem->set($item["\0*\0value"]); $innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null); diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index d453e271aef4c..362aceed0eb18 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -48,7 +48,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()], + ]; + } +} From 89a00b315d23c02dd42bd059ab0946865dc61ef2 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Sun, 7 Apr 2019 10:00:24 +0200 Subject: [PATCH 34/77] Improving deprecation message of the Twig templates directory src/Resources/views --- src/Symfony/Bridge/Twig/Command/DebugCommand.php | 2 +- .../Bridge/Twig/Tests/Command/DebugCommandTest.php | 2 +- .../FrameworkBundle/Command/TranslationDebugCommand.php | 8 ++++---- .../TwigBundle/DependencyInjection/TwigExtension.php | 4 ++-- .../Tests/DependencyInjection/TwigExtensionTest.php | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) 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/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/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/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/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) { From 62e5a91150968f03117f3b85045d9b11f1a057ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Egyed?= Date: Sat, 6 Apr 2019 23:31:05 +0200 Subject: [PATCH 35/77] [Validator] Add missing Hungarian translations --- .../Resources/translations/validators.hu.xlf | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf index 50d433f665ec5..300eb5f68fb97 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.hu.xlf @@ -335,9 +335,29 @@ Ez az érték érvényes JSON kell, hogy legyen. + This value should be positive. + Ennek az értéknek pozitívnak kell lennie. + + + This value should be either positive or zero. + Ennek az értéknek pozitívnak vagy nullának kell lennie. + + + This value should be negative. + Ennek az értéknek negatívnak kell lennie. + + + This value should be either negative or zero. + Ennek az értéknek negatívnak vagy nullának kell lennie. + + This collection should contain only unique elements. Ez a gyűjtemény csak egyedi elemeket tartalmazhat. + + This value is not a valid timezone. + Ez az érték nem egy érvényes időzóna. + From 9c41842756d4313025c0d2cecbf74daf3c1494a9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 7 Apr 2019 11:15:59 +0200 Subject: [PATCH 36/77] fix translating file validation error message --- .../FrameworkBundle/Resources/config/form.xml | 2 +- .../Form/Extension/Core/Type/FileType.php | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 7dd51bbed6a6a..5088554ff1947 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -93,7 +93,7 @@ The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index a7f912d5e3109..f8afce2ee5a4d 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -20,6 +20,7 @@ use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Translation\TranslatorInterface; class FileType extends AbstractType { @@ -32,6 +33,13 @@ class FileType extends AbstractType self::MIB_BYTES => 'MiB', ]; + private $translator; + + public function __construct(TranslatorInterface $translator = null) + { + $this->translator = $translator; + } + /** * {@inheritdoc} */ @@ -150,7 +158,13 @@ private function getFileUploadError($errorCode) $messageTemplate = 'The file could not be uploaded.'; } - return new FormError($messageTemplate, $messageTemplate, $messageParameters); + if (null !== $this->translator) { + $message = $this->translator->trans($messageTemplate, $messageParameters); + } else { + $message = strtr($messageTemplate, $messageParameters); + } + + return new FormError($message, $messageTemplate, $messageParameters); } /** From 83f675d9f3411065a7181364b2d5aec051bfda22 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 7 Apr 2019 09:35:31 +0200 Subject: [PATCH 37/77] sync validator translations --- .../Resources/translations/validators.de.xlf | 24 +++++++++++++++++++ .../Resources/translations/validators.en.xlf | 24 +++++++++++++++++++ .../Resources/translations/validators.vi.xlf | 24 +++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf index ada215c1c6b17..f33e4d602ca15 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf @@ -338,6 +338,30 @@ This collection should contain only unique elements. Diese Sammlung darf keine doppelten Elemente enthalten. + + This value should be positive. + Diese Zahl sollte positiv sein. + + + This value should be either positive or zero. + Diese Zahl sollte entweder positiv oder 0 sein. + + + This value should be negative. + Diese Zahl sollte negativ sein. + + + This value should be either negative or zero. + Diese Zahl sollte entweder negativ oder 0 sein. + + + This value is not a valid timezone. + Dieser Wert ist keine gültige Zeitzone. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Dieses Passwort ist Teil eines Datenlecks, es darf nicht verwendet werden. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index a2dc22f5cb8e9..d5d9d20997fc0 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -338,6 +338,30 @@ This collection should contain only unique elements. This collection should contain only unique elements. + + This value should be positive. + This value should be positive. + + + This value should be either positive or zero. + This value should be either positive or zero. + + + This value should be negative. + This value should be negative. + + + This value should be either negative or zero. + This value should be either negative or zero. + + + This value is not a valid timezone. + This value is not a valid timezone. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + This password has been leaked in a data breach, it must not be used. Please use another password. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf index 89c0ecc90dc98..95dd7d6665997 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.vi.xlf @@ -338,6 +338,30 @@ This collection should contain only unique elements. Danh sách này chỉ nên chứa các phần tử khác nhau. + + This value should be positive. + Giá trị này có thể thực hiện được. + + + This value should be either positive or zero. + Giá trị này có thể thực hiện được hoặc bằng không. + + + This value should be negative. + Giá trị này nên bị từ chối. + + + This value should be either negative or zero. + Giá trị này nên bị từ chối hoặc bằng không. + + + This value is not a valid timezone. + Giá trị này không phải là múi giờ hợp lệ. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Mật khẩu này đã bị rò rỉ dữ liệu, không được sử dụng nữa. Xin vui lòng sử dụng mật khẩu khác. + From a36c7315f42ce7c1ac4263b1b959727e601c470b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sun, 7 Apr 2019 14:43:17 +0200 Subject: [PATCH 38/77] [Debug] Fixed error handling when an error is already handled when another error is already handled (5) --- src/Symfony/Component/Debug/ExceptionHandler.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index 2f3d56601271c..6abe6bb7eb685 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -372,7 +372,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)) { @@ -388,7 +388,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 : ''); From fdb668e051ce45177cbb6eb7ee6f0e03c248e321 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 7 Apr 2019 14:41:46 +0200 Subject: [PATCH 39/77] prevent mixup of the object to populate --- .../Normalizer/AbstractNormalizer.php | 2 ++ .../Tests/Normalizer/ObjectNormalizerTest.php | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index f99c61a36bd32..ef1166a493497 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -330,6 +330,8 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref return $object; } + // clean up even if no match + unset($context[static::OBJECT_TO_POPULATE]); $constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes); if ($constructor) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 4068479c87001..4b72e2a4b5032 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -315,6 +315,30 @@ public function testGroupsDenormalizeWithNameConverter() ); } + public function testObjectToPopulateNoMatch() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $this->normalizer = new ObjectNormalizer($classMetadataFactory, null, null, new PhpDocExtractor()); + new Serializer([$this->normalizer]); + + $objectToPopulate = new ObjectInner(); + $objectToPopulate->foo = 'foo'; + + $outer = $this->normalizer->denormalize([ + 'foo' => 'foo', + 'inner' => [ + 'bar' => 'bar', + ], + ], ObjectOuter::class, null, [ObjectNormalizer::OBJECT_TO_POPULATE => $objectToPopulate]); + + $this->assertInstanceOf(ObjectOuter::class, $outer); + $inner = $outer->getInner(); + $this->assertInstanceOf(ObjectInner::class, $inner); + $this->assertNotSame($objectToPopulate, $inner); + $this->assertSame('bar', $inner->bar); + $this->assertNull($inner->foo); + } + /** * @dataProvider provideCallbacks */ @@ -936,6 +960,9 @@ class ObjectOuter { public $foo; public $bar; + /** + * @var ObjectInner + */ private $inner; private $date; From 934118b131f21fb03c6456d2bc0b089e744b4e04 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sun, 7 Apr 2019 14:44:08 +0200 Subject: [PATCH 40/77] Fix the configurability of CoreExtension deps in standalone usage --- .../Form/Extension/Core/CoreExtension.php | 2 +- .../Component/Form/FormFactoryBuilder.php | 27 +++++++++++++++++++ src/Symfony/Component/Form/Forms.php | 7 +---- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php index 4eb980bf9cdff..0db802501c7d0 100644 --- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php +++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -69,7 +69,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/FormFactoryBuilder.php b/src/Symfony/Component/Form/FormFactoryBuilder.php index bccb86f40791d..759affa3c02f1 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} */ @@ -144,6 +156,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/Forms.php b/src/Symfony/Component/Form/Forms.php index 7c61d640e21ca..b8e2d27fec3e8 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() */ public static function createFormFactoryBuilder() { - $builder = new FormFactoryBuilder(); - $builder->addExtension(new CoreExtension()); - - return $builder; + return new FormFactoryBuilder(true); } /** From 35418be8c40246dc8f1d07be80a12921242a0d3d Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 7 Apr 2019 13:44:37 +0200 Subject: [PATCH 41/77] [Form] Fix tests --- .../Form/Tests/Extension/Core/Type/FileTypeTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 73d39ace025db..777cb883a07b4 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->createMock(TranslatorInterface::class); + $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() { From 64fa13bf44ea838ab95afd673f6baa68203d76ab Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 7 Apr 2019 19:26:29 +0200 Subject: [PATCH 42/77] fix PHPUnit 4.8 compatibility --- .../Component/Form/Tests/Extension/Core/Type/FileTypeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 777cb883a07b4..c566786c8cf17 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -25,7 +25,7 @@ class FileTypeTest extends BaseTypeTest protected function getExtensions() { - $translator = $this->createMock(TranslatorInterface::class); + $translator = $this->getMockBuilder(TranslatorInterface::class)->getMock(); $translator->expects($this->any())->method('trans')->willReturnArgument(0); return array_merge(parent::getExtensions(), [new CoreExtension(null, null, $translator)]); From 27df966705d89c76c47d78c326cc0c78acb270c6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 7 Apr 2019 19:56:26 +0200 Subject: [PATCH 43/77] fix tests --- .../views/Form/bootstrap_3_layout.html.twig | 1 - .../Command/TranslationDebugCommandTest.php | 2 +- .../Extension/Core/Type/FileTypeTest.php | 24 ++++++++++++------- .../HttpFoundationRequestHandlerTest.php | 6 +++++ 4 files changed, 23 insertions(+), 10 deletions(-) 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 ad5011082f50a..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 @@ -114,7 +114,6 @@
{{- form_label(form) }} {# -#} {{ form_widget(form, widget_attr) }} {# -#} - {{ form_widget(form) }} {# -#} {{ form_errors(form) }} {# -#}
{# -#} {%- endblock form_row %} 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/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php index 3c0a38d1b05ee..087f5bf4b5a18 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -199,11 +199,12 @@ public function requestHandlerProvider() */ public function testFailedFileUploadIsTurnedIntoFormErrorUsingHttpFoundationRequestHandler($errorCode, $expectedErrorMessage) { + $requestHandler = new HttpFoundationRequestHandler(); $form = $this->factory ->createBuilder(static::TESTED_TYPE) - ->setRequestHandler(new HttpFoundationRequestHandler()) + ->setRequestHandler($requestHandler) ->getForm(); - $form->submit(new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'foo', null, null, $errorCode, true)); + $form->submit($this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo', $errorCode)); if (UPLOAD_ERR_OK === $errorCode) { $this->assertTrue($form->isValid()); @@ -243,15 +244,16 @@ public function testFailedFileUploadIsTurnedIntoFormErrorUsingNativeRequestHandl */ public function testMultipleSubmittedFailedFileUploadsAreTurnedIntoFormErrorUsingHttpFoundationRequestHandler($errorCode, $expectedErrorMessage) { + $requestHandler = new HttpFoundationRequestHandler(); $form = $this->factory ->createBuilder(static::TESTED_TYPE, null, [ 'multiple' => true, ]) - ->setRequestHandler(new HttpFoundationRequestHandler()) + ->setRequestHandler($requestHandler) ->getForm(); $form->submit([ - new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'foo', null, null, $errorCode, true), - new UploadedFile(__DIR__.'/../../../Fixtures/foo', 'bar', null, null, $errorCode, true), + $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'foo', $errorCode), + $this->createUploadedFile($requestHandler, __DIR__.'/../../../Fixtures/foo', 'bar', $errorCode), ]); if (UPLOAD_ERR_OK === $errorCode) { @@ -316,15 +318,21 @@ public function uploadFileErrorCodes() ]; } - private function createUploadedFile(RequestHandlerInterface $requestHandler, $path, $originalName) + 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 0e5389568e5ce..dc082505a1bb0 100644 --- a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php @@ -59,6 +59,12 @@ protected function getInvalidFile() 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); } } From 00883fc4099e7155f709a19b4d98085c282d8df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 7 Apr 2019 15:06:33 +0200 Subject: [PATCH 44/77] Make tests independent from each other Environment variables set in a test need to be restored to their previous values or unset if we want to be able to run tests independently. --- .../Component/Console/Tests/ApplicationTest.php | 10 ++++++++++ .../Console/Tests/Helper/ProgressBarTest.php | 13 +++++++++++++ .../Console/Tests/Style/SymfonyStyleTest.php | 4 +++- .../Component/Console/Tests/TerminalTest.php | 15 +++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index ae9d130f502ab..85484b5c5a7b4 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -41,6 +41,13 @@ class ApplicationTest extends TestCase { protected static $fixturesPath; + private $colSize; + + protected function setUp() + { + $this->colSize = getenv('COLUMNS'); + } + public static function setUpBeforeClass() { self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); @@ -383,6 +390,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); @@ -468,6 +476,7 @@ public function provideInvalidCommandNamesSingle() public function testFindAlternativeExceptionMessageMultiple() { + putenv('COLUMNS=120'); $application = new Application(); $application->add(new \FooCommand()); $application->add(new \Foo1Command()); @@ -1692,6 +1701,7 @@ public function testErrorIsRethrownIfNotHandledByConsoleErrorEventWithCatchingEn protected function tearDown() { + putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); putenv('SHELL_VERBOSITY'); unset($_ENV['SHELL_VERBOSITY']); unset($_SERVER['SHELL_VERBOSITY']); diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index e352311ccad09..214e943d7319e 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -21,6 +21,19 @@ */ class ProgressBarTest extends TestCase { + private $colSize; + + protected function setUp() + { + $this->colSize = getenv('COLUMNS'); + putenv('COLUMNS=120'); + } + + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); + } + public function testMultipleStart() { $bar = new ProgressBar($output = $this->getOutputStream()); diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php index e6e061f4416b6..a6feb122a12a2 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' : 'COLUMNS='.$this->colSize); $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..ca3b874374b61 100644 --- a/src/Symfony/Component/Console/Tests/TerminalTest.php +++ b/src/Symfony/Component/Console/Tests/TerminalTest.php @@ -16,6 +16,15 @@ class TerminalTest extends TestCase { + private $colSize; + private $lineSize; + + protected function setUp() + { + $this->colSize = getenv('COLUMNS'); + $this->lineSize = getenv('LINES'); + } + public function test() { putenv('COLUMNS=100'); @@ -31,6 +40,12 @@ public function test() $this->assertSame(60, $terminal->getHeight()); } + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); + putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize); + } + public function test_zero_values() { putenv('COLUMNS=0'); From d2f2e56472fa82ac8227cc04788fe1d2bd27aa1e Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Mon, 8 Apr 2019 09:06:46 +0200 Subject: [PATCH 45/77] Fix dark themed componnents --- .../Resources/views/Collector/messenger.html.twig | 1 + .../Resources/views/Profiler/profiler.css.twig | 2 ++ 2 files changed, 3 insertions(+) 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 faef05d963df2..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 %} 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 3cd09336221bd..3a5674ebbcc4b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -1020,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 ========================================================================= #} From 2ae2fd800d99123e1107d08afb1345f54932e8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9B=D0=B8?= Date: Mon, 8 Apr 2019 12:19:48 +0500 Subject: [PATCH 46/77] [FrameworkBundle] Fix Controller deprecated when using composer --optimized Update Controller.php Update Controller.php --- src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 */ From a0c66a399ddc04ede9c2f053044e2bb898df2c9e Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 6 Feb 2019 00:57:29 +0100 Subject: [PATCH 47/77] Fix TestRunner compatibility to PhpUnit 8 --- .../Bridge/PhpUnit/Legacy/CommandForV5.php | 20 +++++++- .../Bridge/PhpUnit/Legacy/CommandForV6.php | 22 ++++++++- .../Bridge/PhpUnit/Legacy/TestRunnerForV5.php | 48 ------------------ .../Bridge/PhpUnit/Legacy/TestRunnerForV6.php | 49 ------------------- .../Bridge/PhpUnit/Legacy/TestRunnerForV7.php | 49 ------------------- .../Bridge/PhpUnit/TextUI/TestRunner.php | 26 ---------- 6 files changed, 39 insertions(+), 175 deletions(-) delete mode 100644 src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php delete mode 100644 src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php delete mode 100644 src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php delete mode 100644 src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php 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 - { - } -} From 8fb2074972b720c5b5f64845870bfb475fe9fef3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 8 Apr 2019 11:27:09 +0200 Subject: [PATCH 48/77] fix resetting the COLUMN environment variable --- .../Component/Console/Tests/ApplicationTest.php | 16 ++++++++-------- .../Console/Tests/Helper/ProgressBarTest.php | 2 +- .../Console/Tests/Style/SymfonyStyleTest.php | 2 +- .../Component/Console/Tests/TerminalTest.php | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 85484b5c5a7b4..200568f0701d7 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -48,6 +48,14 @@ 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/'); @@ -1698,14 +1706,6 @@ public function testErrorIsRethrownIfNotHandledByConsoleErrorEventWithCatchingEn $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found'); } } - - protected function tearDown() - { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); - putenv('SHELL_VERBOSITY'); - unset($_ENV['SHELL_VERBOSITY']); - unset($_SERVER['SHELL_VERBOSITY']); - } } class CustomApplication extends Application diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index 214e943d7319e..a0be9b8a6d94d 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -31,7 +31,7 @@ protected function setUp() protected function tearDown() { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); } public function testMultipleStart() diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php index a6feb122a12a2..88d00c8a9926b 100644 --- a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php +++ b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php @@ -38,7 +38,7 @@ protected function setUp() protected function tearDown() { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); + 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 ca3b874374b61..93b8c44a78158 100644 --- a/src/Symfony/Component/Console/Tests/TerminalTest.php +++ b/src/Symfony/Component/Console/Tests/TerminalTest.php @@ -25,6 +25,12 @@ protected function setUp() $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'); @@ -40,12 +46,6 @@ public function test() $this->assertSame(60, $terminal->getHeight()); } - protected function tearDown() - { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); - putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize); - } - public function test_zero_values() { putenv('COLUMNS=0'); From 926d228877deb81c6edf86d3802894c4877b9e92 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sat, 6 Apr 2019 16:28:08 +0200 Subject: [PATCH 49/77] [Serializer] Respect ignored attributes in cache key of normalizer --- .../Normalizer/AbstractNormalizer.php | 9 ++-- .../Normalizer/AbstractObjectNormalizer.php | 48 +++++++++++++++---- .../Tests/Normalizer/ObjectNormalizerTest.php | 10 ++++ 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index ef1166a493497..c5bd3ffd97202 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -397,7 +397,7 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara } $parameterClass = $parameter->getClass()->getName(); - return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName)); + return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName, $format)); } return $parameterData; @@ -407,14 +407,15 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara } /** - * @param array $parentContext - * @param string $attribute + * @param array $parentContext + * @param string $attribute Attribute name + * @param string|null $format * * @return array * * @internal */ - protected function createChildContext(array $parentContext, $attribute) + protected function createChildContext(array $parentContext, $attribute/*, string $format = null */) { if (isset($parentContext[self::ATTRIBUTES][$attribute])) { $parentContext[self::ATTRIBUTES] = $parentContext[self::ATTRIBUTES][$attribute]; diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 17c31b132b29c..38908a9323ba1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -94,7 +94,7 @@ public function normalize($object, $format = null, array $context = []) throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer', $attribute)); } - $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute))); + $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute, $format))); } return $data; @@ -128,15 +128,13 @@ protected function getAttributes($object, $format = null, array $context) return $allowedAttributes; } - if (isset($context['attributes'])) { - return $this->extractAttributes($object, $format, $context); - } + $attributes = $this->extractAttributes($object, $format, $context); - if (isset($this->attributesCache[$class])) { - return $this->attributesCache[$class]; + if ($context['cache_key']) { + $this->attributesCache[$key] = $attributes; } - return $this->attributesCache[$class] = $this->extractAttributes($object, $format, $context); + return $attributes; } /** @@ -276,7 +274,7 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer', $attribute, $class)); } - $childContext = $this->createChildContext($context, $attribute); + $childContext = $this->createChildContext($context, $attribute, $format); if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) { return $this->serializer->denormalize($data, $class, $format, $childContext); } @@ -373,7 +371,32 @@ private function isMaxDepthReached(array $attributesMetadata, $class, $attribute } /** - * Gets the cache key to use. + * Overwritten to update the cache key for the child. + * + * We must not mix up the attribute cache between parent and children. + * + * {@inheritdoc} + */ + protected function createChildContext(array $parentContext, $attribute/*, string $format = null */) + { + if (\func_num_args() >= 3) { + $format = \func_get_arg(2); + } else { + // will be deprecated in version 4 + $format = null; + } + + $context = parent::createChildContext($parentContext, $attribute, $format); + // format is already included in the cache_key of the parent. + $context['cache_key'] = $this->getCacheKey($format, $context); + + return $context; + } + + /** + * Builds the cache key for the attributes cache. + * + * The key must be different for every option in the context that could change which attributes should be handled. * * @param string|null $format * @param array $context @@ -382,8 +405,13 @@ private function isMaxDepthReached(array $attributesMetadata, $class, $attribute */ private function getCacheKey($format, array $context) { + unset($context['cache_key']); // avoid artificially different keys try { - return md5($format.serialize($context)); + return md5($format.serialize([ + 'context' => $context, + 'ignored' => $this->ignoredAttributes, + 'camelized' => $this->camelizedAttributes, + ])); } catch (\Exception $exception) { // The context cannot be serialized, skip the cache return false; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 4b72e2a4b5032..b30739714b663 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -380,6 +380,16 @@ public function testIgnoredAttributes() ['fooBar' => 'foobar'], $this->normalizer->normalize($obj, 'any') ); + + $this->normalizer->setIgnoredAttributes(['foo', 'baz', 'camelCase', 'object']); + + $this->assertEquals( + [ + 'fooBar' => 'foobar', + 'bar' => 'bar', + ], + $this->normalizer->normalize($obj, 'any') + ); } public function testIgnoredAttributesDenormalize() From 4134be127ac52b370200e87412b93f19297a6ef6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 8 Apr 2019 13:47:38 +0200 Subject: [PATCH 50/77] take setIgnoredAttributes() deprecation into account --- .../Serializer/Tests/Normalizer/ObjectNormalizerTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 8ad705e2f7652..506fad940e954 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -497,7 +497,8 @@ private function doTestIgnoredAttributes(bool $legacy = false) $this->normalizer->normalize($obj, 'any') ); - $this->normalizer->setIgnoredAttributes(['foo', 'baz', 'camelCase', 'object']); + $ignoredAttributes = ['foo', 'baz', 'camelCase', 'object']; + $legacy ? $this->normalizer->setIgnoredAttributes($ignoredAttributes) : $this->createNormalizer([ObjectNormalizer::IGNORED_ATTRIBUTES => $ignoredAttributes]); $this->assertEquals( [ From 37891525f7e7b5ac49243790838361e47d50f9d1 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 7 Apr 2019 09:41:38 +0200 Subject: [PATCH 51/77] [serializer] validate that the specified callbacks and max_depth_handler are actually callable --- .../Normalizer/AbstractNormalizer.php | 8 ++++-- .../Normalizer/AbstractObjectNormalizer.php | 27 ++++++++++++++++++- .../Tests/Normalizer/ObjectNormalizerTest.php | 6 ++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index fe4174e08852f..79e18b7fc3e70 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -99,10 +99,14 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory $this->nameConverter = $nameConverter; $this->defaultContext = array_merge($this->defaultContext, $defaultContext); - if (\is_array($this->defaultContext[self::CALLBACKS] ?? null)) { + if (isset($this->defaultContext[self::CALLBACKS])) { + if (!\is_array($this->defaultContext[self::CALLBACKS])) { + throw new InvalidArgumentException(sprintf('The "%s" default context option must be an array of callables.', self::CALLBACKS)); + } + foreach ($this->defaultContext[self::CALLBACKS] as $attribute => $callback) { if (!\is_callable($callback)) { - throw new InvalidArgumentException(sprintf('The given callback for attribute "%s" is not callable.', $attribute)); + throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" default context option.', $attribute, self::CALLBACKS)); } } } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 5204990674141..ad1c4b166715a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -59,6 +59,11 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = []) { parent::__construct($classMetadataFactory, $nameConverter, $defaultContext); + + if (isset($this->defaultContext[self::MAX_DEPTH_HANDLER]) && !\is_callable($this->defaultContext[self::MAX_DEPTH_HANDLER])) { + throw new InvalidArgumentException(sprintf('The "%s" given in the default context is not callable.', self::MAX_DEPTH_HANDLER)); + } + $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS]; $this->propertyTypeExtractor = $propertyTypeExtractor; @@ -87,6 +92,18 @@ public function normalize($object, $format = null, array $context = []) $context['cache_key'] = $this->getCacheKey($format, $context); } + if (isset($context[self::CALLBACKS])) { + if (!\is_array($context[self::CALLBACKS])) { + throw new InvalidArgumentException(sprintf('The "%s" context option must be an array of callables.', self::CALLBACKS)); + } + + foreach ($context[self::CALLBACKS] as $attribute => $callback) { + if (!\is_callable($callback)) { + throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" context option.', $attribute, self::CALLBACKS)); + } + } + } + if ($this->isCircularReference($object, $context)) { return $this->handleCircularReference($object, $format, $context); } @@ -96,7 +113,15 @@ public function normalize($object, $format = null, array $context = []) $attributes = $this->getAttributes($object, $format, $context); $class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object); $attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null; - $maxDepthHandler = $context[self::MAX_DEPTH_HANDLER] ?? $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler; + if (isset($context[self::MAX_DEPTH_HANDLER])) { + $maxDepthHandler = $context[self::MAX_DEPTH_HANDLER]; + if (!\is_callable($maxDepthHandler)) { + throw new InvalidArgumentException(sprintf('The "%s" given in the context is not callable.', self::MAX_DEPTH_HANDLER)); + } + } else { + // already validated in constructor resp by type declaration of setMaxDepthHandler + $maxDepthHandler = $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler; + } foreach ($attributes as $attribute) { $maxDepthReached = false; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 8ad705e2f7652..0edf6f38aa2e4 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -815,7 +815,11 @@ private function createNormalizerWithMaxDepthHandler(callable $handler = null, b $this->normalizer->setMaxDepthHandler($handler); } } else { - $this->createNormalizer([ObjectNormalizer::MAX_DEPTH_HANDLER => $handler], $classMetadataFactory); + $context = []; + if (null !== $handler) { + $context[ObjectNormalizer::MAX_DEPTH_HANDLER] = $handler; + } + $this->createNormalizer($context, $classMetadataFactory); } $this->serializer = new Serializer([$this->normalizer]); $this->normalizer->setSerializer($this->serializer); From 1cf9659b5f14d2cd3167c9f8bfba6b440c247cfb Mon Sep 17 00:00:00 2001 From: Raulnet Date: Tue, 2 Apr 2019 16:37:37 +0200 Subject: [PATCH 52/77] fix buildTableRows when Colspan is use with content too long --- .../Console/Formatter/OutputFormatter.php | 8 ++- .../Component/Console/Helper/Table.php | 8 +-- .../Tests/Formatter/OutputFormatterTest.php | 3 ++ .../Console/Tests/Helper/TableTest.php | 52 +++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) 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/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 99aa83d4c8173..aa0fe1e9d9686 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -522,16 +522,18 @@ 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; } $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/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/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index 43b90fd073c91..c79d20c558aa5 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -1124,4 +1124,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)); + } } From e238c893e975598f66f66349a3b1e2c374cb8c0b Mon Sep 17 00:00:00 2001 From: Florian Morello Date: Mon, 8 Apr 2019 17:40:55 +0200 Subject: [PATCH 53/77] Fix missing $extraDirs when open_basedir returns --- src/Symfony/Component/Process/ExecutableFinder.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Process/ExecutableFinder.php b/src/Symfony/Component/Process/ExecutableFinder.php index a621fc6918e5d..11ef5c8b22813 100644 --- a/src/Symfony/Component/Process/ExecutableFinder.php +++ b/src/Symfony/Component/Process/ExecutableFinder.php @@ -51,7 +51,10 @@ public function addSuffix($suffix) public function find($name, $default = null, array $extraDirs = []) { if (ini_get('open_basedir')) { - $searchPath = explode(PATH_SEPARATOR, ini_get('open_basedir')); + $searchPath = array_merge( + explode(PATH_SEPARATOR, ini_get('open_basedir')), + $extraDirs + ); $dirs = []; foreach ($searchPath as $path) { // Silencing against https://bugs.php.net/69240 From b93d2bf9415c790347d677adee268865bc786fe1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 8 Apr 2019 18:15:54 +0200 Subject: [PATCH 54/77] fixed CS --- src/Symfony/Component/Process/ExecutableFinder.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Symfony/Component/Process/ExecutableFinder.php b/src/Symfony/Component/Process/ExecutableFinder.php index 11ef5c8b22813..cb4345e7bb424 100644 --- a/src/Symfony/Component/Process/ExecutableFinder.php +++ b/src/Symfony/Component/Process/ExecutableFinder.php @@ -51,10 +51,7 @@ public function addSuffix($suffix) public function find($name, $default = null, array $extraDirs = []) { if (ini_get('open_basedir')) { - $searchPath = array_merge( - explode(PATH_SEPARATOR, ini_get('open_basedir')), - $extraDirs - ); + $searchPath = array_merge(explode(PATH_SEPARATOR, ini_get('open_basedir')), $extraDirs); $dirs = []; foreach ($searchPath as $path) { // Silencing against https://bugs.php.net/69240 From e354d54e7edf13381056d5002b7807f476a9e8bc Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 8 Apr 2019 20:04:40 +0200 Subject: [PATCH 55/77] [VarExporter] fix exporting classes with private constructors --- .../Component/VarExporter/Internal/Registry.php | 4 ++-- .../Tests/Fixtures/private-constructor.php | 17 +++++++++++++++++ .../VarExporter/Tests/VarExporterTest.php | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/private-constructor.php diff --git a/src/Symfony/Component/VarExporter/Internal/Registry.php b/src/Symfony/Component/VarExporter/Internal/Registry.php index 629836b00bc0d..31ec4a0d790e1 100644 --- a/src/Symfony/Component/VarExporter/Internal/Registry.php +++ b/src/Symfony/Component/VarExporter/Internal/Registry.php @@ -65,14 +65,14 @@ public static function f($class) public static function getClassReflector($class, $instantiableWithoutConstructor = false, $cloneable = null) { - if (!\class_exists($class) && !\interface_exists($class, false) && !\trait_exists($class, false)) { + if (!($isClass = \class_exists($class)) && !\interface_exists($class, false) && !\trait_exists($class, false)) { throw new ClassNotFoundException($class); } $reflector = new \ReflectionClass($class); if ($instantiableWithoutConstructor) { $proto = $reflector->newInstanceWithoutConstructor(); - } elseif (!$reflector->isInstantiable()) { + } elseif (!$isClass || $reflector->isAbstract()) { throw new NotInstantiableTypeException($class); } elseif ($reflector->name !== $class) { $reflector = self::$reflectors[$name = $reflector->name] ?? self::getClassReflector($name, $instantiableWithoutConstructor, $cloneable); diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/private-constructor.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/private-constructor.php new file mode 100644 index 0000000000000..b29978b7ab91c --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/private-constructor.php @@ -0,0 +1,17 @@ + [ + 'prop' => [ + 'bar', + ], + ], + ], + $o[0], + [] +); diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index 5bb03d8ce1fc7..f2cd3ecd6de9d 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -197,6 +197,8 @@ public function provideExport() yield ['abstract-parent', new ConcreteClass()]; yield ['foo-serializable', new FooSerializable('bar')]; + + yield ['private-constructor', PrivateConstructor::create('bar')]; } } @@ -250,6 +252,21 @@ private function __clone() } } +class PrivateConstructor +{ + public $prop; + + public static function create($prop): self + { + return new self($prop); + } + + private function __construct($prop) + { + $this->prop = $prop; + } +} + class MyPrivateValue { protected $prot; From af1e136ca00c90e5c2d283c608a0b94ca2c23eb9 Mon Sep 17 00:00:00 2001 From: soyuka Date: Tue, 9 Apr 2019 10:07:40 +0200 Subject: [PATCH 56/77] MetadataAwareNameConverter: Do not assume that property names are strings --- .../NameConverter/MetadataAwareNameConverter.php | 8 ++++---- .../NameConverter/MetadataAwareNameConverterTest.php | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php b/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php index a6934f54b8181..e863e013e7582 100644 --- a/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php +++ b/src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php @@ -69,7 +69,7 @@ public function denormalize($propertyName, string $class = null, string $format return self::$denormalizeCache[$class][$propertyName] ?? $this->denormalizeFallback($propertyName, $class, $format, $context); } - private function getCacheValueForNormalization(string $propertyName, string $class): ?string + private function getCacheValueForNormalization($propertyName, string $class) { if (!$this->metadataFactory->hasMetadataFor($class)) { return null; @@ -83,12 +83,12 @@ private function getCacheValueForNormalization(string $propertyName, string $cla return $attributesMetadata[$propertyName]->getSerializedName() ?? null; } - private function normalizeFallback(string $propertyName, string $class = null, string $format = null, array $context = []): string + private function normalizeFallback($propertyName, string $class = null, string $format = null, array $context = []) { return $this->fallbackNameConverter ? $this->fallbackNameConverter->normalize($propertyName, $class, $format, $context) : $propertyName; } - private function getCacheValueForDenormalization(string $propertyName, string $class): ?string + private function getCacheValueForDenormalization($propertyName, string $class) { if (!isset(self::$attributesMetadataCache[$class])) { self::$attributesMetadataCache[$class] = $this->getCacheValueForAttributesMetadata($class); @@ -97,7 +97,7 @@ private function getCacheValueForDenormalization(string $propertyName, string $c return self::$attributesMetadataCache[$class][$propertyName] ?? null; } - private function denormalizeFallback(string $propertyName, string $class = null, string $format = null, array $context = []): string + private function denormalizeFallback($propertyName, string $class = null, string $format = null, array $context = []) { return $this->fallbackNameConverter ? $this->fallbackNameConverter->denormalize($propertyName, $class, $format, $context) : $propertyName; } diff --git a/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php b/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php index f4c5c5b3059b0..7e2552ea8ac63 100644 --- a/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php +++ b/src/Symfony/Component/Serializer/Tests/NameConverter/MetadataAwareNameConverterTest.php @@ -102,6 +102,7 @@ public function attributeProvider() ['foo', 'baz'], ['bar', 'qux'], ['quux', 'quux'], + [0, 0], ]; } @@ -111,6 +112,7 @@ public function fallbackAttributeProvider() ['foo', 'baz'], ['bar', 'qux'], ['quux', 'QUUX'], + [0, 0], ]; } } From 5790859275d2d4164eb7cc33ffdc52d79b60a52b Mon Sep 17 00:00:00 2001 From: Dmytro Date: Fri, 1 Mar 2019 19:12:11 +0200 Subject: [PATCH 57/77] Rework firewall access denied rule --- .../Http/Firewall/ExceptionListener.php | 4 +- .../Tests/Firewall/ExceptionListenerTest.php | 63 ++++++++++++++++--- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index d107721471533..b3b5ccefec783 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -131,8 +131,6 @@ private function handleAccessDeniedException(GetResponseForExceptionEvent $event } catch (\Exception $e) { $event->setException($e); } - - return; } if (null !== $this->logger) { @@ -150,7 +148,7 @@ private function handleAccessDeniedException(GetResponseForExceptionEvent $event $subRequest = $this->httpUtils->createRequest($event->getRequest(), $this->errorPage); $subRequest->attributes->set(Security::ACCESS_DENIED_ERROR, $exception); - $event->setResponse($event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true)); + $event->setResponse($event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST)); $event->allowCustomResponseCode(); } } catch (\Exception $e) { diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php index 53fedebcad705..3220e43e70e95 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php @@ -130,10 +130,8 @@ public function testAccessDeniedExceptionFullFledgedAndWithAccessDeniedHandlerAn { $event = $this->createEvent($exception); - $accessDeniedHandler = $this->getMockBuilder('Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface')->getMock(); - $accessDeniedHandler->expects($this->once())->method('handle')->will($this->returnValue(new Response('error'))); + $listener = $this->createExceptionListener(null, $this->createTrustResolver(true), null, null, null, $this->createCustomAccessDeniedHandler(new Response('error'))); - $listener = $this->createExceptionListener(null, $this->createTrustResolver(true), null, null, null, $accessDeniedHandler); $listener->onKernelException($event); $this->assertEquals('error', $event->getResponse()->getContent()); @@ -147,16 +145,51 @@ public function testAccessDeniedExceptionNotFullFledged(\Exception $exception, \ { $event = $this->createEvent($exception); - $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); - $tokenStorage->expects($this->once())->method('getToken')->will($this->returnValue($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); - - $listener = $this->createExceptionListener($tokenStorage, $this->createTrustResolver(false), null, $this->createEntryPoint()); + $listener = $this->createExceptionListener($this->createTokenStorage(), $this->createTrustResolver(false), null, $this->createEntryPoint()); $listener->onKernelException($event); $this->assertEquals('OK', $event->getResponse()->getContent()); $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious()); } + /** + * @dataProvider getAccessDeniedExceptionProvider + */ + public function testAccessDeniedExceptionNotFullFledgedAndWithAccessDeniedHandlerAndWithoutErrorPage(\Exception $exception, \Exception $eventException = null) + { + $event = $this->createEvent($exception); + + $listener = $this->createExceptionListener($this->createTokenStorage(), $this->createTrustResolver(false), null, $this->createEntryPoint(), null, $this->createCustomAccessDeniedHandler(new Response('denied', 403))); + $listener->onKernelException($event); + + $this->assertEquals('denied', $event->getResponse()->getContent()); + $this->assertEquals(403, $event->getResponse()->getStatusCode()); + $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious()); + } + + /** + * @dataProvider getAccessDeniedExceptionProvider + */ + public function testAccessDeniedExceptionNotFullFledgedAndWithoutAccessDeniedHandlerAndWithErrorPage(\Exception $exception, \Exception $eventException = null) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel->expects($this->once())->method('handle')->will($this->returnValue(new Response('Unauthorized', 401))); + + $event = $this->createEvent($exception, $kernel); + + $httpUtils = $this->getMockBuilder('Symfony\Component\Security\Http\HttpUtils')->getMock(); + $httpUtils->expects($this->once())->method('createRequest')->will($this->returnValue(Request::create('/error'))); + + $listener = $this->createExceptionListener($this->createTokenStorage(), $this->createTrustResolver(true), $httpUtils, null, '/error'); + $listener->onKernelException($event); + + $this->assertTrue($event->isAllowingCustomResponseCode()); + + $this->assertEquals('Unauthorized', $event->getResponse()->getContent()); + $this->assertEquals(401, $event->getResponse()->getStatusCode()); + $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious()); + } + public function getAccessDeniedExceptionProvider() { return [ @@ -168,6 +201,22 @@ public function getAccessDeniedExceptionProvider() ]; } + private function createTokenStorage() + { + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); + $tokenStorage->expects($this->once())->method('getToken')->will($this->returnValue($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); + + return $tokenStorage; + } + + private function createCustomAccessDeniedHandler(Response $response) + { + $accessDeniedHandler = $this->getMockBuilder('Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface')->getMock(); + $accessDeniedHandler->expects($this->once())->method('handle')->will($this->returnValue($response)); + + return $accessDeniedHandler; + } + private function createEntryPoint(Response $response = null) { $entryPoint = $this->getMockBuilder('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface')->getMock(); From c7a504c822a481dd1d1847d0f39bba832dcfecd3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 9 Apr 2019 19:38:59 +0200 Subject: [PATCH 58/77] [VarExporter] support PHP7.4 __serialize & __unserialize --- .../Component/VarExporter/Instantiator.php | 2 +- .../VarExporter/Internal/Exporter.php | 18 ++++++++- .../VarExporter/Internal/Hydrator.php | 8 +++- .../VarExporter/Internal/Registry.php | 6 +-- src/Symfony/Component/VarExporter/README.md | 3 +- .../Tests/Fixtures/php74-serializable.php | 16 ++++++++ .../VarExporter/Tests/VarExporterTest.php | 37 ++++++++++++++++++- .../Component/VarExporter/VarExporter.php | 24 ++++++++++-- 8 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/php74-serializable.php diff --git a/src/Symfony/Component/VarExporter/Instantiator.php b/src/Symfony/Component/VarExporter/Instantiator.php index 7eefc3c2d2821..06abbc75a6954 100644 --- a/src/Symfony/Component/VarExporter/Instantiator.php +++ b/src/Symfony/Component/VarExporter/Instantiator.php @@ -67,7 +67,7 @@ public static function instantiate(string $class, array $properties = [], array $wrappedInstance = [$reflector->newInstanceWithoutConstructor()]; } elseif (null === Registry::$prototypes[$class]) { throw new NotInstantiableTypeException($class); - } elseif ($reflector->implementsInterface('Serializable')) { + } elseif ($reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__unserialize'))) { $wrappedInstance = [unserialize('C:'.\strlen($class).':"'.$class.'":0:{}')]; } else { $wrappedInstance = [unserialize('O:'.\strlen($class).':"'.$class.'":0:{}')]; diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index fae9084ca040a..74a324691ea06 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -74,10 +74,23 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount } $class = \get_class($value); + $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + + if ($reflector->hasMethod('__serialize')) { + if (!$reflector->getMethod('__serialize')->isPublic()) { + throw new \Error(sprintf('Call to %s method %s::__serialize()', $reflector->getMethod('__serialize')->isProtected() ? 'protected' : 'private', $class)); + } + + if (!\is_array($properties = $value->__serialize())) { + throw new \Typerror($class.'::__serialize() must return an array'); + } + + goto prepare_value; + } + $properties = []; $sleep = null; $arrayValue = (array) $value; - $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); $proto = Registry::$prototypes[$class]; if (($value instanceof \ArrayIterator || $value instanceof \ArrayObject) && null !== $proto) { @@ -154,10 +167,11 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount } } + prepare_value: $objectsPool[$value] = [$id = \count($objectsPool)]; $properties = self::prepare($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); ++$objectsCount; - $objectsPool[$value] = [$id, $class, $properties, \method_exists($class, '__wakeup') ? $objectsCount : 0]; + $objectsPool[$value] = [$id, $class, $properties, \method_exists($class, '__unserialize') ? -$objectsCount : (\method_exists($class, '__wakeup') ? $objectsCount : 0)]; $value = new Reference($id); diff --git a/src/Symfony/Component/VarExporter/Internal/Hydrator.php b/src/Symfony/Component/VarExporter/Internal/Hydrator.php index 07721df428771..5f64adf96fb53 100644 --- a/src/Symfony/Component/VarExporter/Internal/Hydrator.php +++ b/src/Symfony/Component/VarExporter/Internal/Hydrator.php @@ -42,8 +42,12 @@ public static function hydrate($objects, $values, $properties, $value, $wakeups) foreach ($properties as $class => $vars) { (self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects); } - foreach ($wakeups as $i) { - $objects[$i]->__wakeup(); + foreach ($wakeups as $k => $v) { + if (\is_array($v)) { + $objects[-$k]->__unserialize($v); + } else { + $objects[$v]->__wakeup(); + } } return $value; diff --git a/src/Symfony/Component/VarExporter/Internal/Registry.php b/src/Symfony/Component/VarExporter/Internal/Registry.php index 31ec4a0d790e1..b5069dd16aa84 100644 --- a/src/Symfony/Component/VarExporter/Internal/Registry.php +++ b/src/Symfony/Component/VarExporter/Internal/Registry.php @@ -86,14 +86,14 @@ public static function getClassReflector($class, $instantiableWithoutConstructor $proto = $reflector->newInstanceWithoutConstructor(); $instantiableWithoutConstructor = true; } catch (\ReflectionException $e) { - $proto = $reflector->implementsInterface('Serializable') ? 'C:' : 'O:'; + $proto = $reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__unserialize')) ? 'C:' : 'O:'; if ('C:' === $proto && !$reflector->getMethod('unserialize')->isInternal()) { $proto = null; } elseif (false === $proto = @unserialize($proto.\strlen($class).':"'.$class.'":0:{}')) { throw new NotInstantiableTypeException($class); } } - if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !\method_exists($class, '__sleep')) { + if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !\method_exists($class, '__sleep') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__serialize'))) { try { serialize($proto); } catch (\Exception $e) { @@ -103,7 +103,7 @@ public static function getClassReflector($class, $instantiableWithoutConstructor } if (null === $cloneable) { - if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup'))) { + if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !\method_exists($proto, '__wakeup') && (\PHP_VERSION_ID < 70400 || !\method_exists($class, '__unserialize')))) { throw new NotInstantiableTypeException($class); } diff --git a/src/Symfony/Component/VarExporter/README.md b/src/Symfony/Component/VarExporter/README.md index c3a072127e4de..180554ed1a036 100644 --- a/src/Symfony/Component/VarExporter/README.md +++ b/src/Symfony/Component/VarExporter/README.md @@ -3,7 +3,8 @@ VarExporter Component The VarExporter component allows exporting any serializable PHP data structure to plain PHP code. While doing so, it preserves all the semantics associated with -the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`). +the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`, +`__serialize`, `__unserialize`). It also provides an instantiator that allows creating and populating objects without calling their constructor nor any other methods. diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/php74-serializable.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/php74-serializable.php new file mode 100644 index 0000000000000..06cfac10f55d3 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/php74-serializable.php @@ -0,0 +1,16 @@ +assertSame($staticValueExpected, $isStaticValue); - if ('var-on-sleep' !== $testName) { + if ('var-on-sleep' !== $testName && 'php74-serializable' !== $testName) { $this->assertDumpEquals($dumpedValue, $value); } @@ -199,6 +199,8 @@ public function provideExport() yield ['foo-serializable', new FooSerializable('bar')]; yield ['private-constructor', PrivateConstructor::create('bar')]; + + yield ['php74-serializable', new Php74Serializable()]; } } @@ -387,3 +389,36 @@ public function unserialize($str) list($this->foo) = unserialize($str); } } + +class Php74Serializable implements \Serializable +{ + public function __serialize() + { + return [$this->foo = new \stdClass()]; + } + + public function __unserialize(array $data) + { + list($this->foo) = $data; + } + + public function __sleep() + { + throw new \BadMethodCallException(); + } + + public function __wakeup() + { + throw new \BadMethodCallException(); + } + + public function serialize() + { + throw new \BadMethodCallException(); + } + + public function unserialize($ser) + { + throw new \BadMethodCallException(); + } +} diff --git a/src/Symfony/Component/VarExporter/VarExporter.php b/src/Symfony/Component/VarExporter/VarExporter.php index 45ef7446b12c9..da9a8d43736fa 100644 --- a/src/Symfony/Component/VarExporter/VarExporter.php +++ b/src/Symfony/Component/VarExporter/VarExporter.php @@ -69,14 +69,30 @@ public static function export($value, bool &$isStaticValue = null): string $classes = []; $values = []; - $wakeups = []; + $states = []; foreach ($objectsPool as $i => $v) { list(, $classes[], $values[], $wakeup) = $objectsPool[$v]; - if ($wakeup) { - $wakeups[$wakeup] = $i; + if (0 < $wakeup) { + $states[$wakeup] = $i; + } elseif (0 > $wakeup) { + $states[-$wakeup] = [$i, array_pop($values)]; + $values[] = []; } } - ksort($wakeups); + ksort($states); + + $wakeups = [null]; + foreach ($states as $k => $v) { + if (\is_array($v)) { + $wakeups[-$v[0]] = $v[1]; + } else { + $wakeups[] = $v; + } + } + + if (null === $wakeups[0]) { + unset($wakeups[0]); + } $properties = []; foreach ($values as $i => $vars) { From 0cf3227011c092c530628ff5ee4ca4f4ec392a0c Mon Sep 17 00:00:00 2001 From: Martijn Cuppens Date: Wed, 10 Apr 2019 11:35:05 +0200 Subject: [PATCH 59/77] Remove redundant `box-sizing` prefixes --- .../Resources/views/Profiler/toolbar.css.twig | 4 ---- 1 file changed, 4 deletions(-) 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 9c76e0ad8f33d..29702bb67a8a4 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; From 3655bcfaf7a0019025b70ae00ed7c1ec038ef94f Mon Sep 17 00:00:00 2001 From: Martijn Cuppens Date: Wed, 10 Apr 2019 12:58:43 +0200 Subject: [PATCH 60/77] Remove redundant animation prefixes CSS animations can be used safely without any prefixes --- .../Resources/views/Profiler/toolbar.css.twig | 13 ------------- 1 file changed, 13 deletions(-) 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 29702bb67a8a4..8953316de06f2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -363,21 +363,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; } From dd5b8f16f5b260124b0057fcd57ed32d271ce3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20DECOOL?= Date: Mon, 8 Apr 2019 23:10:42 +0200 Subject: [PATCH 61/77] [Serializer] Add default object class resolver --- .../Normalizer/ObjectNormalizer.php | 7 ++++-- .../Tests/Normalizer/ObjectNormalizerTest.php | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 88732bbd3e14f..f766286b2b11a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -43,7 +43,10 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext); $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); - $this->objectClassResolver = $objectClassResolver; + + $this->objectClassResolver = $objectClassResolver ?? function ($class) { + return \is_object($class) ? \get_class($class) : $class; + }; } /** @@ -63,7 +66,7 @@ protected function extractAttributes($object, $format = null, array $context = [ $attributes = []; // methods - $class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object); + $class = ($this->objectClassResolver)($object); $reflClass = new \ReflectionClass($class); foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index b85ec8b5e23d5..4c4448dfe4b0e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -1043,6 +1043,30 @@ public function denormalize($propertyName, string $class = null, string $format $this->assertArrayHasKey('foo-Symfony\Component\Serializer\Tests\Normalizer\ObjectDummy-json-bar', $normalizer->normalize(new ObjectDummy(), 'json', ['foo' => 'bar'])); } + public function testDefaultObjectClassResolver() + { + $normalizer = new ObjectNormalizer(); + + $obj = new ObjectDummy(); + $obj->setFoo('foo'); + $obj->bar = 'bar'; + $obj->setBaz(true); + $obj->setCamelCase('camelcase'); + $obj->unwantedProperty = 'notwanted'; + + $this->assertEquals( + [ + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => true, + 'fooBar' => 'foobar', + 'camelCase' => 'camelcase', + 'object' => null, + ], + $normalizer->normalize($obj, 'any') + ); + } + public function testObjectClassResolver() { $classResolver = function ($object) { From a56bf552ad5b844298fc27242ead879d3264f962 Mon Sep 17 00:00:00 2001 From: rubenrua Date: Wed, 10 Apr 2019 18:00:48 +0200 Subject: [PATCH 62/77] CS Fixes: Not double split with one array argument Keep to use the same CS in all the Symfony code base. Use: ```php $resolver->setDefaults([ 'compound' => false ]); ``` Instead of: ```php $resolver->setDefaults( [ 'compound' => false, ] ); ``` Keep the double split when the method has two or more arguments. I miss a PSR with this rule. --- .../UserPasswordEncoderCommandTest.php | 14 ++--- .../Extension/Core/Type/DateIntervalType.php | 62 +++++++++---------- .../Factory/DefaultChoiceListFactoryTest.php | 10 ++- .../Tests/OptionsResolverTest.php | 38 +++++------- .../Tests/Dumper/YamlFileDumperTest.php | 18 +++--- .../Tests/Constraints/ChoiceValidatorTest.php | 24 +++---- 6 files changed, 74 insertions(+), 92 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php index d53bb45461f78..c8f8013f3f8a0 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/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/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/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index abf0aa0155d23..d85bbe8fd8485 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -531,13 +531,11 @@ public function testResolveFailsWithCorrectLevelsButWrongScalar() $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[][]'); - $this->resolver->resolve( - [ - 'foo' => [ - [1.2], - ], - ] - ); + $this->resolver->resolve([ + 'foo' => [ + [1.2], + ], + ]); } /** @@ -1598,13 +1596,11 @@ public function testNestedArrays() 1, 2, ], ], - ], $this->resolver->resolve( - [ - 'foo' => [ - [1, 2], - ], - ] - )); + ], $this->resolver->resolve([ + 'foo' => [ + [1, 2], + ], + ])); } public function testNested2Arrays() @@ -1644,17 +1640,15 @@ public function testNestedArraysException() $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'float[][][][]'); - $this->resolver->resolve( - [ - 'foo' => [ + $this->resolver->resolve([ + 'foo' => [ + [ [ - [ - [1, 2], - ], + [1, 2], ], ], - ] - ); + ], + ]); } /** diff --git a/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php index 24bc65ba248fb..e46da5a7e8089 100644 --- a/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php +++ b/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php @@ -20,11 +20,10 @@ class YamlFileDumperTest extends TestCase public function testTreeFormatCatalogue() { $catalogue = new MessageCatalogue('en'); - $catalogue->add( - [ - 'foo.bar1' => 'value1', - 'foo.bar2' => 'value2', - ]); + $catalogue->add([ + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', + ]); $dumper = new YamlFileDumper(); @@ -34,11 +33,10 @@ public function testTreeFormatCatalogue() public function testLinearFormatCatalogue() { $catalogue = new MessageCatalogue('en'); - $catalogue->add( - [ - 'foo.bar1' => 'value1', - 'foo.bar2' => 'value2', - ]); + $catalogue->add([ + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', + ]); $dumper = new YamlFileDumper(); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php index 1f312cda17042..e83cb8997745a 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php @@ -55,12 +55,10 @@ public function testNullIsValid() { $this->validator->validate( null, - new Choice( - [ - 'choices' => ['foo', 'bar'], - 'strict' => true, - ] - ) + new Choice([ + 'choices' => ['foo', 'bar'], + 'strict' => true, + ]) ); $this->assertNoViolation(); @@ -102,14 +100,12 @@ public function testValidChoiceCallbackFunction() public function testValidChoiceCallbackClosure() { - $constraint = new Choice( - [ - 'strict' => true, - 'callback' => function () { - return ['foo', 'bar']; - }, - ] - ); + $constraint = new Choice([ + 'strict' => true, + 'callback' => function () { + return ['foo', 'bar']; + }, + ]); $this->validator->validate('bar', $constraint); From 326aa86d6a1975543d844295d8b3376bed6e92d4 Mon Sep 17 00:00:00 2001 From: Lynn Date: Wed, 10 Apr 2019 13:41:44 +0200 Subject: [PATCH 63/77] Show more accurate message in profiler when missing stopwatch --- .../Resources/views/Collector/time.html.twig | 6 +++++- src/Symfony/Bundle/WebProfilerBundle/composer.json | 2 +- .../HttpKernel/DataCollector/TimeDataCollector.php | 9 +++++++++ .../Tests/DataCollector/TimeDataCollectorTest.php | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) 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 e9a4a51b403f6..dd95b511b40ea 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -95,7 +95,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. Are you sure that debugging is enabled in the kernel?

diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index c1efe11a26bb1..241a5e350b414 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^5.5.9|>=7.0.8", - "symfony/http-kernel": "~3.3|~4.0", + "symfony/http-kernel": "~3.4.25|^4.2.6", "symfony/polyfill-php70": "~1.0", "symfony/routing": "~2.8|~3.0|~4.0", "symfony/twig-bridge": "~2.8|~3.0|~4.0", 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/Tests/DataCollector/TimeDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/TimeDataCollectorTest.php index cf6a86695d776..793fbd319f94d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/TimeDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/TimeDataCollectorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\TimeDataCollector; +use Symfony\Component\Stopwatch\Stopwatch; /** * @group time-sensitive @@ -51,5 +52,6 @@ public function testCollect() $c->collect($request, new Response()); $this->assertEquals(123456000, $c->getStartTime()); + $this->assertSame(\class_exists(Stopwatch::class, false), $c->isStopwatchInstalled()); } } From 9ee6fc15e1ed3187ff11e320cefe88daccec9c45 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 10 Apr 2019 21:42:23 +0200 Subject: [PATCH 64/77] fixed bad merge --- .../Tests/Constraints/ChoiceValidatorTest.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php index 728fa36c52b82..2f703b8fd5e46 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php @@ -98,22 +98,11 @@ public function testValidChoiceCallbackFunction() public function testValidChoiceCallbackClosure() { -<<<<<<< HEAD - $constraint = new Choice( - [ - 'callback' => function () { - return ['foo', 'bar']; - }, - ] - ); -======= $constraint = new Choice([ - 'strict' => true, 'callback' => function () { return ['foo', 'bar']; }, ]); ->>>>>>> 3.4 $this->validator->validate('bar', $constraint); From 13e2fb735d92742ab91aa04d7a262f28b3099ca4 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Thu, 11 Apr 2019 07:44:34 +0200 Subject: [PATCH 65/77] property normalizer should also pass format and context to isAllowedAttribute --- .../Component/Serializer/Normalizer/PropertyNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index ece267873f25f..84047e82c6483 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -102,7 +102,7 @@ protected function extractAttributes($object, $format = null, array $context = [ do { foreach ($reflectionObject->getProperties() as $property) { - if (!$this->isAllowedAttribute($reflectionObject->getName(), $property->name)) { + if (!$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context)) { continue; } From 93dabbc96a754f5e641e723023373159c14a49d0 Mon Sep 17 00:00:00 2001 From: Pablo Ogando Ferreira Date: Thu, 11 Apr 2019 09:45:22 +0200 Subject: [PATCH 66/77] CS Fixes: Not double split with one array argument --- .../ResolveNamedArgumentsPassTest.php | 18 +++++++-------- .../Tests/Command/DebugCommandTest.php | 22 +++++++++---------- .../Tests/Constraints/EmailValidatorTest.php | 10 ++++----- 3 files changed, 22 insertions(+), 28 deletions(-) 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/Messenger/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php index 727aefbfef20b..97f2b3e71859a 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php @@ -38,18 +38,16 @@ protected function tearDown() public function testOutput() { - $command = new DebugCommand( - [ - 'command_bus' => [ - DummyCommand::class => [DummyCommandHandler::class], - MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], - ], - 'query_bus' => [ - DummyQuery::class => [DummyQueryHandler::class], - MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], - ], - ] - ); + $command = new DebugCommand([ + 'command_bus' => [ + DummyCommand::class => [DummyCommandHandler::class], + MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], + ], + 'query_bus' => [ + DummyQuery::class => [DummyQueryHandler::class], + MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], + ], + ]); $tester = new CommandTester($command); $tester->execute([], ['decorated' => false]); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index 51b528d46fa9e..314a1bc1372cd 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -146,12 +146,10 @@ public function getInvalidEmails() */ public function testInvalidHtml5Emails($email) { - $constraint = new Email( - [ - 'message' => 'myMessage', - 'mode' => Email::VALIDATION_MODE_HTML5, - ] - ); + $constraint = new Email([ + 'message' => 'myMessage', + 'mode' => Email::VALIDATION_MODE_HTML5, + ]); $this->validator->validate($email, $constraint); From 4614cea9d275f03cfbefbd538106e67d93e79738 Mon Sep 17 00:00:00 2001 From: Martijn Cuppens Date: Thu, 11 Apr 2019 11:29:09 +0200 Subject: [PATCH 67/77] Optimize SVGs --- .../Resources/views/images/chevron-right.svg | 2 +- .../Resources/views/images/icon-plus-square.svg | 2 +- .../Resources/views/images/symfony-ghost.svg | 2 +- .../Resources/views/images/symfony-logo.svg | 2 +- .../Resources/views/Icon/ajax.svg | 7 +------ .../Resources/views/Icon/cache.svg | 4 +--- .../Resources/views/Icon/close.svg | 6 +----- .../Resources/views/Icon/config.svg | 4 +--- .../Resources/views/Icon/event.svg | 12 +----------- .../Resources/views/Icon/exception.svg | 16 +--------------- .../Resources/views/Icon/form.svg | 7 +------ .../Resources/views/Icon/forward.svg | 5 +---- .../Resources/views/Icon/logger.svg | 8 +------- .../Resources/views/Icon/memory.svg | 6 +----- .../Resources/views/Icon/menu.svg | 4 +--- .../Resources/views/Icon/no.svg | 6 +----- .../Resources/views/Icon/redirect.svg | 11 +---------- .../Resources/views/Icon/request.svg | 17 +---------------- .../Resources/views/Icon/router.svg | 7 +------ .../Resources/views/Icon/search.svg | 8 +------- .../Resources/views/Icon/symfony.svg | 13 +------------ .../Resources/views/Icon/time.svg | 6 +----- .../Resources/views/Icon/translation.svg | 14 +------------- .../Resources/views/Icon/twig.svg | 6 +----- .../Resources/views/Icon/validator.svg | 2 +- .../Resources/views/Icon/yes.svg | 6 +----- .../views/Profiler/toolbar_js.html.twig | 2 +- .../Tests/Resources/IconTest.php | 2 +- .../Component/Debug/ExceptionHandler.php | 2 +- .../HttpKernel/Resources/welcome.html.php | 16 +--------------- .../Component/VarDumper/Dumper/HtmlDumper.php | 8 ++------ 31 files changed, 32 insertions(+), 181 deletions(-) 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/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/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 bfb4e6dea1e09..8c6ad014e4783 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg @@ -1,5 +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/toolbar_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig index 2dc3eaff9635d..f13edfcfebf52 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig @@ -114,7 +114,7 @@ var sfwdt = document.getElementById('sfwdt{{ token }}'); sfwdt.innerHTML = '\
\ -
\ +
\ An error occurred while loading the web debug toolbar. Open the web profiler.\
\ '; 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/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index 6abe6bb7eb685..b79e83ea2c9bf 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -439,6 +439,6 @@ private function escapeHtml($str) private function getSymfonyGhostAsSvg() { - return ''; + return ''; } } 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 @@