diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bc64e2f797..efb1b16d81 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -15,21 +15,21 @@ Symfony is the result of the work of many people who made the code better - Victor Berchet (victor) - Kévin Dunglas (dunglas) - Maxime Steinhausser (ogizanagi) + - Ryan Weaver (weaverryan) - Jakub Zalas (jakubzalas) - Johannes S (johannes) - Javier Eguiluz (javier.eguiluz) - - Ryan Weaver (weaverryan) - Kris Wallsmith (kriswallsmith) - - Roland Franssen (ro0) - Grégoire Pineau (lyrixx) + - Roland Franssen (ro0) - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) - Romain Neutron (romain) - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) + - Samuel ROZE (sroze) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - - Samuel ROZE (sroze) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) - Jeremy Mikola (jmikola) @@ -42,8 +42,8 @@ Symfony is the result of the work of many people who made the code better - Guilhem Niot (energetick) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) - - Hamza Amrouche (simperfit) - Tobias Nyholm (tobias) + - Hamza Amrouche (simperfit) - Diego Saint Esteben (dosten) - Iltar van der Berg (kjarli) - Alexandre Salomé (alexandresalome) @@ -54,8 +54,8 @@ Symfony is the result of the work of many people who made the code better - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - Bulat Shakirzyanov (avalanche123) - - Matthias Pigulla (mpdude) - Jérémy DERUSSÉ (jderusse) + - Matthias Pigulla (mpdude) - Saša Stamenković (umpirsky) - Peter Rehm (rpet) - Kevin Bond (kbond) @@ -69,23 +69,24 @@ Symfony is the result of the work of many people who made the code better - Mathieu Piot (mpiot) - Florin Patan (florinpatan) - Gábor Egyed (1ed) + - Gabriel Ostrolucký (gadelat) - Titouan Galopin (tgalopin) - Vladimir Reznichenko (kalessil) - Jáchym Toušek (enumag) + - David Maicher (dmaicher) - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) - Konstantin Myakshin (koc) - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) - - David Maicher (dmaicher) - Christian Raue - Issei Murasawa (issei_m) + - Valentin Udaltsov (vudaltsov) - Arnout Boks (aboks) - Deni - Grégoire Paris (greg0ire) - Henrik Westphal (snc) - Dariusz Górecki (canni) - - Valentin Udaltsov (vudaltsov) - Douglas Greenshields (shieldo) - Dariusz Ruminski - Lee McDermott @@ -99,7 +100,6 @@ Symfony is the result of the work of many people who made the code better - Jérôme Tamarelle (gromnan) - John Wards (johnwards) - Fran Moreno (franmomu) - - gadelat (gadelat) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - Arnaud Le Blanc (arnaud-lb) @@ -108,13 +108,14 @@ Symfony is the result of the work of many people who made the code better - Tim Nagel (merk) - Brice BERNARD (brikou) - Baptiste Clavié (talus) + - Chris Wilkinson (thewilkybarkid) - marc.weistroff - Tomáš Votruba (tomas_votruba) - David Buchmann (dbu) - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) - - Chris Wilkinson (thewilkybarkid) + - Thomas Calvet (fancyweb) - Jérôme Vasseur (jvasseur) - Peter Kokot (maastermedia) - Jacob Dreesen (jdreesen) @@ -125,11 +126,10 @@ Symfony is the result of the work of many people who made the code better - Daniel Wehner (dawehner) - excelwebzone - Gordon Franke (gimler) - - Thomas Calvet (fancyweb) + - Sebastiaan Stok (sstok) - Javier Spagnoletti (phansys) - Fabien Pennequin (fabienpennequin) - Eric GELOEN (gelo) - - Sebastiaan Stok (sstok) - Lars Strojny (lstrojny) - Tugdual Saunier (tucksaun) - Théo FIDRY (theofidry) @@ -140,14 +140,18 @@ Symfony is the result of the work of many people who made the code better - Alex Pott - Vincent AUBERT (vincent) - Juti Noppornpitak (shiroyuki) + - Anthony MARTIN (xurudragon) + - Oskar Stark (oskarstark) - Tigran Azatyan (tigranazatyan) - Sebastian Hörl (blogsh) - Daniel Gomes (danielcsgomes) + - Joel Wurtz (brouznouf) - Gabriel Caruso - Hidenori Goto (hidenorigoto) - Arnaud Kleinpeter (nanocom) - Jannik Zschiesche (apfelbox) - Guilherme Blanco (guilhermeblanco) + - Teoh Han Hui (teohhanhui) - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) - Andréia Bohner (andreia) @@ -155,9 +159,7 @@ Symfony is the result of the work of many people who made the code better - Julien Falque (julienfalque) - Rafael Dohms (rdohms) - jwdeitch - - Teoh Han Hui (teohhanhui) - Mikael Pajunen - - Joel Wurtz (brouznouf) - Oleg Voronkovich - Vyacheslav Pavlov - Richard van Laak (rvanlaak) @@ -172,7 +174,6 @@ Symfony is the result of the work of many people who made the code better - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) - Dawid Nowak - - Gabriel Ostrolucký - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) - Artur Kotyrba @@ -181,12 +182,12 @@ Symfony is the result of the work of many people who made the code better - SpacePossum - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) - - Anthony MARTIN (xurudragon) - Matthieu Napoli (mnapoli) - Florent Mata (fmata) - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) + - Marek Štípek (maryo) - Daniel Espendiller - Possum - Dorian Villet (gnutix) @@ -196,7 +197,7 @@ Symfony is the result of the work of many people who made the code better - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) - DQNEO - - Oskar Stark (oskarstark) + - Samuel NELA (snela) - Benjamin Dulau (dbenjamin) - François-Xavier de Guillebon (de-gui_f) - Mathieu Lemoine (lemoinem) @@ -205,7 +206,6 @@ Symfony is the result of the work of many people who made the code better - Tom Van Looy (tvlooy) - Noel Guilbert (noel) - Yanick Witschi (toflar) - - Marek Štípek (maryo) - Stepan Anchugov (kix) - bronze1man - sun (sun) @@ -217,24 +217,28 @@ Symfony is the result of the work of many people who made the code better - apetitpa - Matthieu Bontemps (mbontemps) - apetitpa - - Samuel NELA (snela) - Pierre Minnieur (pminnieur) - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) - Michael Lee (zerustech) - Matthieu Auger (matthieuauger) + - Gregor Harlan (gharlan) - Leszek Prabucki (l3l0) - Fabien Bourigault (fbourigault) - François Zaninotto (fzaninotto) - Dustin Whittle (dustinwhittle) - jeff - John Kary (johnkary) + - Andreas Schempp (aschempp) - Justin Hileman (bobthecow) - Blanchon Vincent (blanchonvincent) - Michele Orselli (orso) - Sven Paulus (subsven) + - Maxime Veber (nek-) + - Gary PEGEOT (gary-p) - Rui Marinho (ruimarinho) + - Massimiliano Arione (garak) - Eugene Wissner - Pascal Montoya - Julien Brochet (mewt) @@ -242,15 +246,17 @@ Symfony is the result of the work of many people who made the code better - Tristan Darricau (nicofuma) - Marcel Beerta (mazen) - Pavel Batanov (scaytrase) + - Mantis Development - Loïc Faugeron - Hidde Wieringa (hiddewie) - Marco Pivetta (ocramius) + - Jan Schädlich (jschaedl) - Rob Frawley 2nd (robfrawley) - julien pauli (jpauli) - Lorenz Schori - Sébastien Lavoie (lavoiesl) - - Gregor Harlan (gharlan) - Dariusz + - Michael Babker (mbabker) - Francois Zaninotto - Alexander Kotynia (olden) - Daniel Tschinder @@ -265,7 +271,6 @@ Symfony is the result of the work of many people who made the code better - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) - Mickaël Andrieu (mickaelandrieu) - - Maxime Veber (nek-) - Xavier Perez - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA @@ -277,6 +282,7 @@ Symfony is the result of the work of many people who made the code better - Baptiste Lafontaine (magnetik) - Jakub Kucharovic (jkucharovic) - Edi Modrić (emodric) + - Alexander Schranz (alexander-schranz) - Uwe Jäger (uwej711) - Eugene Leonovich (rybakit) - Filippo Tessarotto @@ -290,10 +296,9 @@ Symfony is the result of the work of many people who made the code better - Viktor Bocharskyi (bocharsky_bw) - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - - Andreas Schempp (aschempp) - jdhoek - - Massimiliano Arione (garak) - Bob den Otter (bopp) + - Thomas Schulz (king2500) - Frank de Jonge (frenkynet) - Nikita Konstantinov - Wodor Wodorski @@ -301,7 +306,7 @@ Symfony is the result of the work of many people who made the code better - mcfedr (mcfedr) - Colin O'Dell (colinodell) - Giorgio Premi - - Jan Schädlich (jschaedl) + - Alex Rock (pierstoval) - Ben Davies (bendavies) - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) @@ -309,9 +314,9 @@ Symfony is the result of the work of many people who made the code better - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) + - Pascal Luna (skalpa) - Wouter Van Hecke - Jérôme Parmentier (lctrs) - - Michael Babker (mbabker) - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Remon van de Kamp (rpkamp) @@ -319,14 +324,13 @@ Symfony is the result of the work of many people who made the code better - Marc Weistroff (futurecat) - Christian Schmidt - MatTheCat - - Alexander Schranz (alexander-schranz) - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) - - Gary PEGEOT (gary-p) - Manuel Kiessling (manuelkiessling) - Atsuhiro KUBO (iteman) - rudy onfroy (ronfroy) + - Serkan Yildiz (srknyldz) - Andrew Moore (finewolf) - Bertrand Zuchuat (garfield-fr) - Sullivan SENECHAL (soullivaneuh) @@ -347,14 +351,16 @@ Symfony is the result of the work of many people who made the code better - Hidde Boomsma (hboomsma) - John Bafford (jbafford) - Raul Fraile (raulfraile) + - David Prévot - Adrian Rudnik (kreischweide) - Francesc Rosàs (frosas) - Romain Pierre (romain-pierre) - Julien Galenski (ruian) - Bongiraud Dominique - janschoenherr - - Thomas Schulz (king2500) + - Emanuele Gaspari (inmarelibero) - Dariusz Rumiński + - Vincent Touzet (vincenttouzet) - Berny Cantos (xphere81) - Thierry Thuon (lepiaf) - Ricard Clau (ricardclau) @@ -367,8 +373,9 @@ Symfony is the result of the work of many people who made the code better - Zan Baldwin (zanderbaldwin) - Thomas Royer (cydonia7) - alquerci + - Mateusz Sip (mateusz_sip) + - Andre Rømcke (andrerom) - Francesco Levorato - - Pascal Luna (skalpa) - Dmitrii Poddubnyi (karser) - Vitaliy Zakharov (zakharovvi) - Tobias Sjösten (tobiassjosten) @@ -384,6 +391,7 @@ Symfony is the result of the work of many people who made the code better - Yaroslav Kiliba - Terje Bråten - Robbert Klarenbeek (robbertkl) + - Eric Masoero (eric-masoero) - JhonnyL - David Badura (davidbadura) - hossein zolfi (ocean) @@ -394,6 +402,7 @@ Symfony is the result of the work of many people who made the code better - ShinDarth - Stéphane PY (steph_py) - Philipp Kräutli (pkraeutli) + - Anton Chernikov (anton_ch1989) - Grzegorz (Greg) Zdanowski (kiler129) - Iker Ibarguren (ikerib) - Kirill chEbba Chebunin (chebba) @@ -414,10 +423,10 @@ Symfony is the result of the work of many people who made the code better - Tobias Naumann (tna) - Daniel Beyer - Shein Alexey - - Alex Rock Ancelet (pierstoval) - Romain Gautier (mykiwi) - Joe Lencioni - Daniel Tschinder + - Emmanuel BORGES (eborges78) - vladimir.reznichenko - Kai - Lee Rowlands @@ -427,7 +436,6 @@ Symfony is the result of the work of many people who made the code better - Karoly Negyesi (chx) - Ivan Kurnosov - Xavier HAUSHERR - - David Prévot - Albert Jessurum (ajessu) - Laszlo Korte - Miha Vrhovnik @@ -440,7 +448,6 @@ Symfony is the result of the work of many people who made the code better - Karel Souffriau - Christophe L. (christophelau) - Anthon Pang (robocoder) - - Emanuele Gaspari (inmarelibero) - Sébastien Santoro (dereckson) - Brian King - Michel Salib (michelsalib) @@ -461,7 +468,7 @@ Symfony is the result of the work of many people who made the code better - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) - Alessandro Lai (jean85) - - Andre Rømcke (andrerom) + - Nicolas LEFEVRE (nicoweb) - Arturs Vonda - Josip Kruslin - Asmir Mustafic (goetas) @@ -473,7 +480,6 @@ Symfony is the result of the work of many people who made the code better - Vlad Gregurco (vgregurco) - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) - - Mateusz Sip (mateusz_sip) - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) @@ -528,6 +534,7 @@ Symfony is the result of the work of many people who made the code better - ondrowan - Barry vd. Heuvel (barryvdh) - Craig Duncan (duncan3dc) + - Patrick Landolt (scube) - Sébastien Alfaiate (seb33300) - Evan S Kaufman (evanskaufman) - mcben @@ -603,6 +610,7 @@ Symfony is the result of the work of many people who made the code better - Jan Behrens - Mantas Var (mvar) - Sebastian Krebs + - Piotr Stankowski - Baptiste Leduc (bleduc) - Laurent VOULLEMIER (lvo) - Jean-Christophe Cuvelier [Artack] @@ -617,6 +625,7 @@ Symfony is the result of the work of many people who made the code better - Renan - Ricky Su (ricky) - Gildas Quéméner (gquemener) + - Kyle Evans (kevans91) - Charles-Henri Bruyand - Max Rath (drak3) - Stéphane Escandell (sescandell) @@ -625,6 +634,8 @@ Symfony is the result of the work of many people who made the code better - Sinan Eldem - Alexandre Dupuy (satchette) - Malte Blättermann + - Kévin THERAGE (kevin_therage) + - Simeon Kolev (simeon_kolev9) - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) - Christophe Villeger (seragan) @@ -649,11 +660,13 @@ Symfony is the result of the work of many people who made the code better - Sebastian Marek (proofek) - Guilhem N (guilhemn) - Erkhembayar Gantulga (erheme318) + - zenmate - Michal Trojanowski - David Fuhr - Max Grigorian (maxakawizard) - DerManoMann - Rostyslav Kinash + - Dennis Fridrich (dfridrich) - Maciej Malarz (malarzm) - Daisuke Ohata - Vincent Simonin @@ -707,6 +720,7 @@ Symfony is the result of the work of many people who made the code better - Paweł Wacławczyk (pwc) - Oleg Zinchenko (cystbear) - Baptiste Meyer (meyerbaptiste) + - Tales Santos (tsantos84) - Johannes Klauss (cloppy) - Evan Villemez - fzerorubigd @@ -720,6 +734,7 @@ Symfony is the result of the work of many people who made the code better - flip111 - Greg Anderson - VJ + - RJ Garcia - Delf Tonder (leberknecht) - Mark Sonnabaum - Massimiliano Braglia (massimilianobraglia) @@ -738,15 +753,14 @@ Symfony is the result of the work of many people who made the code better - Julien DIDIER (juliendidier) - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) - - Serkan Yildiz (srknyldz) - Jeroen van den Enden (stoefke) + - nikos.sotiropoulos - Pascal Helfenstein - Anthony GRASSIOT (antograssiot) - Baldur Rensch (brensch) - Pierre Rineau - Vladyslav Petrovych - Alex Xandra Albert Sim - - Patrick Landolt (scube) - Carson Full - Sergey Yastrebov - Trent Steel (trsteel88) @@ -761,6 +775,7 @@ Symfony is the result of the work of many people who made the code better - Dave Marshall (davedevelopment) - Jakub Kulhan (jakubkulhan) - avorobiev + - Grégoire Penverne (gpenverne) - Venu - Lars Vierbergen - Jonatan Männchen @@ -774,6 +789,7 @@ Symfony is the result of the work of many people who made the code better - Jordan Deitch - Casper Valdemar Poulsen - Josiah (josiah) + - Greg ORIOL - Joschi Kuphal - John Bohn (jbohn) - Marc Morera (mmoreram) @@ -922,6 +938,7 @@ Symfony is the result of the work of many people who made the code better - Stéphane Delprat - Brian Freytag (brianfreytag) - Samuele Lilli (doncallisto) + - Marko Kaznovac (kaznovac) - Brunet Laurent (lbrunet) - Florent Viel (luxifer) - Mikhail Yurasov (mym) @@ -937,15 +954,17 @@ Symfony is the result of the work of many people who made the code better - Kyle - Daniel Alejandro Castro Arellano (lexcast) - sensio + - Thomas Jarrand - Sebastien Morel (plopix) - Patrick Kaufmann - - Piotr Stankowski - Anton Dyshkant - Reece Fowell (reecefowell) - Mátyás Somfai (smatyas) - stefan.r - Valérian Galliat - d-ph + - Renan Taranto (renan-taranto) + - Thomas Talbot (ioni) - Rikijs Murgs - Ben Ramsey (ramsey) - Amaury Leroux de Lens (amo__) @@ -954,12 +973,14 @@ Symfony is the result of the work of many people who made the code better - The Whole Life to Learn - ergiegonzaga - Farhad Safarov + - Alexis Lefebvre - Liverbool (liverbool) - Sam Malone - Phan Thanh Ha (haphan) - Chris Jones (leek) - xaav - Mahmoud Mostafa (mahmoud) + - Ahmed Abdou - Pieter - Michael Tibben - Billie Thompson @@ -992,21 +1013,23 @@ Symfony is the result of the work of many people who made the code better - Irmantas Šiupšinskas (irmantas) - Danilo Silva - Arnaud PETITPAS (apetitpa) + - Ken Stanley - Zachary Tong (polyfractal) - Ashura - Hryhorii Hrebiniuk - johnstevenson - - Dennis Fridrich (dfridrich) - hamza - dantleech - Bastien DURAND (deamon) - Xavier Leune - Rudy Onfroy - Tero Alén (tero) + - Stanislav Kocanda - DerManoMann - Guillaume Royer - Artem (digi) - boite + - Silvio Ginter - MGDSoft - Vadim Tyukov (vatson) - David Wolter (davewww) @@ -1015,6 +1038,7 @@ Symfony is the result of the work of many people who made the code better - Wojciech Sznapka - Gavin Staniforth - Ariel J. Birnbaum + - Danijel Obradović - Pablo Borowicz - Mathieu Santostefano - Arjan Keeman @@ -1065,7 +1089,6 @@ Symfony is the result of the work of many people who made the code better - Jean-Guilhem Rouel (jean-gui) - jfcixmedia - Dominic Tubach - - Tales Santos (tsantos84) - Nikita Konstantinov - Martijn Evers - Vitaliy Ryaboy (vitaliy) @@ -1094,6 +1117,7 @@ Symfony is the result of the work of many people who made the code better - Tadas Gliaubicas (tadcka) - Thanos Polymeneas (thanos) - Benoit Garret + - Maximilian Ruta (deltachaos) - Jakub Sacha - Olaf Klischat - orlovv @@ -1131,7 +1155,6 @@ Symfony is the result of the work of many people who made the code better - rpg600 - Péter Buri (burci) - kaiwa - - RJ Garcia - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) - Neil Katin @@ -1148,7 +1171,6 @@ Symfony is the result of the work of many people who made the code better - rchoquet - gitlost - Taras Girnyk - - nikos.sotiropoulos - Eduardo García Sanz (coma) - Sergio (deverad) - James Gilliland @@ -1207,7 +1229,6 @@ Symfony is the result of the work of many people who made the code better - Mario Young - Ilia (aliance) - Chris McCafferty (cilefen) - - Grégoire Penverne (gpenverne) - Mo Di (modi) - Pablo Schläpfer - Gert de Pagter @@ -1215,6 +1236,7 @@ Symfony is the result of the work of many people who made the code better - Quique Porta (quiqueporta) - stoccc - Tomasz Szymczyk (karion) + - Alex Vasilchenko - Xavier Coureau - ConneXNL - Aharon Perkel @@ -1232,8 +1254,10 @@ Symfony is the result of the work of many people who made the code better - Sebastian Göttschkes (sgoettschkes) - Tatsuya Tsuruoka - Ross Tuck + - Andreas Erhard - Kévin Gomez (kevin) - Mihai Nica (redecs) + - Soufian EZ-ZANTAR (soezz) - Andrei Igna - azine - Dawid Sajdak @@ -1249,6 +1273,7 @@ Symfony is the result of the work of many people who made the code better - Saem Ghani - Clément LEFEBVRE - Conrad Kleinespel + - Matthias Althaus - Zacharias Luiten - Sebastian Utz - Adrien Gallou (agallou) @@ -1288,6 +1313,7 @@ Symfony is the result of the work of many people who made the code better - Markus Fasselt (digilist) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) + - Fleuv - Sandro Hopf - Łukasz Makuch - George Giannoulopoulos @@ -1311,11 +1337,11 @@ Symfony is the result of the work of many people who made the code better - Daniel Kay (danielkay-cp) - Matt Daum (daum) - Alberto Pirovano (geezmo) - - Nicolas LEFEVRE (nicoweb) - Pete Mitchell (peterjmit) - Tom Corrigan (tomcorrigan) - Luis Galeas - Martin Pärtel + - Frédéric Bouchery (fbouchery) - Patrick Daley (padrig) - Xavier Briand (xavierbriand) - Max Summe @@ -1336,6 +1362,7 @@ Symfony is the result of the work of many people who made the code better - Emmanuel Vella (emmanuel.vella) - Guillaume BRETOU (guiguiboy) - Jonathan Johnson (jrjohnson) + - Dāvis Zālītis (k0d3r1s) - Carsten Nielsen (phreaknerd) - Roger Guasch (rogerguasch) - Mathieu Rochette @@ -1371,6 +1398,7 @@ Symfony is the result of the work of many people who made the code better - mlively - Amine Matmati - caalholm + - Nouhail AL FIDI (alfidi) - Fabian Steiner (fabstei) - Felipy Tavares Amorim (felipyamorim) - Klaus Silveira (klaussilveira) @@ -1420,7 +1448,6 @@ Symfony is the result of the work of many people who made the code better - Hoffmann András - LubenZA - Olivier - - Anton Chernikov - Cyril PASCAL - pscheit - Wybren Koelmans @@ -1432,6 +1459,7 @@ Symfony is the result of the work of many people who made the code better - Antonio Peric-Mazar (antonioperic) - César Suárez (csuarez) - Bjorn Twachtmann (dotbjorn) + - Tobias Genberg (lorceroth) - Luis Tacón (lutacon) - Nicolas Badey (nico-b) - Shane Preece (shane) @@ -1465,10 +1493,9 @@ Symfony is the result of the work of many people who made the code better - Przemysław Piechota (kibao) - Leonid Terentyev (li0n) - Martynas Sudintas (martiis) - - Gabriel Ostrolucký - ryunosuke - - zenmate - victoria + - Dmytro Borysovskyi (dmytr0) - Francisco Facioni (fran6co) - Iwan van Staveren (istaveren) - Povilas S. (povilas) @@ -1488,6 +1515,7 @@ Symfony is the result of the work of many people who made the code better - Ikhsan Agustian - Arnau González (arnaugm) - Simon Bouland (bouland) + - Jibé Barth (jibbarth) - Matthew Foster (mfoster) - Reyo Stallenberg (reyostallenberg) - Paul Seiffert (seiffert) @@ -1531,7 +1559,6 @@ Symfony is the result of the work of many people who made the code better - Gunther Konig - Mickael GOETZ - Maciej Schmidt - - Greg ORIOL - Dennis Væversted - nuncanada - flack @@ -1652,6 +1679,7 @@ Symfony is the result of the work of many people who made the code better - Trevor Suarez - gedrox - Alan Bondarchuk + - Joe Bennett - dropfen - Andrey Chernykh - Edvinas Klovas @@ -1671,6 +1699,7 @@ Symfony is the result of the work of many people who made the code better - AmsTaFF (amstaff) - Simon Müller (boscho) - Yannick Bensacq (cibou) + - Damien (damien_vauchel) - Frédéric G. Marand (fgm) - Freek Van der Herten (freekmurze) - Luca Genuzio (genuzio) @@ -1735,6 +1764,7 @@ Symfony is the result of the work of many people who made the code better - timaschew - Jochen Mandl - Marin Nicolae + - soyuka - Alessandro Loffredo - Ian Phillips - Marco Lipparini @@ -1810,6 +1840,7 @@ Symfony is the result of the work of many people who made the code better - Kevin Verschaeve (keversc) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) + - Mehdi Mabrouk (mehdidev) - Bart Reunes (metalarend) - Muriel (metalmumu) - Michael Pohlers (mick_the_big) @@ -1861,6 +1892,7 @@ Symfony is the result of the work of many people who made the code better - Ramon Henrique Ornelas (ramonornela) - Ricardo de Vries (ricknox) - Markus S. (staabm) + - Thomas Dutrion (theocrite) - Till Klampaeckel (till) - Tobias Weinert (tweini) - Ulf Reimers (ureimers) @@ -2011,7 +2043,6 @@ Symfony is the result of the work of many people who made the code better - phc - Дмитрий Пацура - ilyes kooli - - Marko Kaznovac - Matthias Althaus - Michaël VEROUX - Julia @@ -2073,6 +2104,7 @@ Symfony is the result of the work of many people who made the code better - samuel laulhau (lalop) - Laurent Bachelier (laurentb) - Luís Cobucci (lcobucci) + - Marcos Gómez Vilches (markitosgv) - Matthieu Mota (matthieumota) - Matthieu Moquet (mattketmo) - Moritz Borgmann (mborgmann) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index cf6447a4bc..b8b76833a6 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -49,7 +49,13 @@ class QuestionHelper extends Helper if (!$input->isInteractive()) { $default = $question->getDefault(); - if (null !== $default && $question instanceof ChoiceQuestion) { + if (null === $default) { + return $default; + } + + if ($validator = $question->getValidator()) { + return \call_user_func($question->getValidator(), $default); + } elseif ($question instanceof ChoiceQuestion) { $choices = $question->getChoices(); if (!$question->isMultiselect()) { @@ -215,6 +221,7 @@ class QuestionHelper extends Helper // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { + shell_exec(sprintf('stty %s', $sttyMode)); throw new RuntimeException('Aborted.'); } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index e3edbed029..69d5470b8c 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -137,6 +137,9 @@ class QuestionHelperTest extends AbstractQuestionHelperTest $question->setMultiselect(true); $this->assertNull($questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); + $question = new ChoiceQuestion('Who are your favorite superheros?', ['a' => 'Batman', 'b' => 'Superman'], 'a'); + $this->assertSame('a', $questionHelper->ask($this->createStreamableInputInterfaceMock('', false), $this->createOutputInterface(), $question), 'ChoiceQuestion validator returns the key if it\'s a string'); + try { $question = new ChoiceQuestion('Who are your favorite superheros?', $heroes, ''); $question->setMultiselect(true); diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 513d87c7e8..9ec9a34696 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -304,10 +304,6 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface if (null !== $this->logger) { $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context); } - - if (!isset($this->called[$eventName])) { - $this->called[$eventName] = new \SplObjectStorage(); - } } else { $this->callStack->detach($listener); } diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index 9217cb7190..5eedf29581 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -883,6 +883,10 @@ class FinderTest extends Iterator\RealIteratorTestCase public function testInWithGlobBrace() { + if (!\defined('GLOB_BRACE')) { + $this->markTestSkipped('Glob brace is not supported on this system.'); + } + $finder = $this->buildFinder(); $finder->in([__DIR__.'/Fixtures/{A,copy/A}/B/C'])->getIterator(); diff --git a/src/Symfony/Component/Form/AbstractExtension.php b/src/Symfony/Component/Form/AbstractExtension.php index 5b437344e8..c49edb7c70 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 @@ abstract class AbstractExtension implements FormExtensionInterface /** * 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 5eebb20ac1..e0954c9537 100644 --- a/src/Symfony/Component/Form/AbstractRendererEngine.php +++ b/src/Symfony/Component/Form/AbstractRendererEngine.php @@ -131,7 +131,7 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface * 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 8ae4946c56..ed1106b467 100644 --- a/src/Symfony/Component/Form/Button.php +++ b/src/Symfony/Component/Form/Button.php @@ -22,7 +22,7 @@ use Symfony\Component\Form\Exception\BadMethodCallException; class Button implements \IteratorAggregate, FormInterface { /** - * @var FormInterface|null + * @var FormInterface */ private $parent; @@ -111,6 +111,8 @@ class Button implements \IteratorAggregate, FormInterface } $this->parent = $parent; + + return $this; } /** @@ -199,11 +201,13 @@ class Button implements \IteratorAggregate, FormInterface * 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 @@ class Button implements \IteratorAggregate, FormInterface */ public function getData() { + return null; } /** @@ -218,6 +223,7 @@ class Button implements \IteratorAggregate, FormInterface */ public function getNormData() { + return null; } /** @@ -225,6 +231,7 @@ class Button implements \IteratorAggregate, FormInterface */ public function getViewData() { + return null; } /** @@ -240,7 +247,7 @@ class Button implements \IteratorAggregate, FormInterface /** * Returns the button's configuration. * - * @return FormConfigInterface The configuration + * @return FormConfigInterface The configuration instance */ public function getConfig() { @@ -272,6 +279,7 @@ class Button implements \IteratorAggregate, FormInterface */ public function getPropertyPath() { + return null; } /** @@ -309,11 +317,11 @@ class Button implements \IteratorAggregate, FormInterface */ 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 @@ class Button implements \IteratorAggregate, FormInterface */ public function getTransformationFailure() { + return null; } /** @@ -368,7 +377,7 @@ class Button implements \IteratorAggregate, FormInterface /** * 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 27d8e70f2b..2373b248b7 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -22,9 +22,6 @@ use Symfony\Component\Form\Exception\InvalidArgumentException; */ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface { - /** - * @var bool - */ protected $locked = false; /** @@ -518,6 +515,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getEventDispatcher() { + return null; } /** @@ -533,6 +531,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getPropertyPath() { + return null; } /** @@ -600,6 +599,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getDataMapper() { + return null; } /** @@ -637,6 +637,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getEmptyData() { + return null; } /** @@ -679,6 +680,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getData() { + return null; } /** @@ -686,6 +688,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getDataClass() { + return null; } /** @@ -703,6 +706,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getFormFactory() { + throw new BadMethodCallException('Buttons do not support adding children.'); } /** @@ -710,6 +714,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getAction() { + return null; } /** @@ -717,6 +722,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getMethod() { + return null; } /** @@ -724,6 +730,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function getRequestHandler() { + return null; } /** diff --git a/src/Symfony/Component/Form/CallbackTransformer.php b/src/Symfony/Component/Form/CallbackTransformer.php index 919efabd10..5125214ff9 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 @@ class CallbackTransformer implements DataTransformerInterface } /** - * 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 @@ class CallbackTransformer implements DataTransformerInterface } /** - * 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 bb262e7b8e..dee8f78491 100644 --- a/src/Symfony/Component/Form/DataMapperInterface.php +++ b/src/Symfony/Component/Form/DataMapperInterface.php @@ -17,22 +17,46 @@ namespace Symfony\Component\Form; 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. * - * @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances - * @param mixed $data Structured data + * 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 $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 deb073c812..e5ac599294 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 @@ interface DataTransformerInterface * 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 2b60f4f31e..b05dcc018d 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 @@ class ButtonType extends BaseType implements ButtonTypeInterface { 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 8d2d109699..5a0986d89f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -311,8 +311,8 @@ class ChoiceType extends AbstractType '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 17ecfd6f47..f54bd73d48 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -21,6 +21,7 @@ use Symfony\Component\Form\Util\FormUtil; 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 @@ use Symfony\Component\PropertyAccess\PropertyPath; class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterface { /** - * The form's configuration. - * * @var FormConfigInterface */ private $config; /** - * The parent of this form. - * - * @var FormInterface + * @var FormInterface|null */ private $parent; /** - * The children of this form. - * - * @var FormInterface[] A map of FormInterface instances + * @var FormInterface[]|OrderedHashMap A map of FormInterface instances */ private $children; /** - * The errors of this form. - * * @var FormError[] An array of FormError instances */ private $errors = []; /** - * Whether this form was submitted. - * * @var bool */ private $submitted = false; /** - * The button that was used to submit the form. - * - * @var Button + * @var ClickableInterface|null The button that was used to submit the form */ private $clickedButton; /** - * The form data in model format. - * * @var mixed */ private $modelData; /** - * The form data in normalized format. - * * @var mixed */ private $normData; /** - * The form data in view format. - * * @var mixed */ private $viewData; /** - * The submitted values that don't belong to any children. - * - * @var array + * @var array The submitted values that don't belong to any children */ private $extraData = []; /** - * Returns the transformation failure generated during submission, if any. - * - * @var TransformationFailedException|null + * @var TransformationFailedException|null The transformation failure generated during submission, if any */ private $transformationFailure; @@ -161,8 +140,21 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac private $lockSetData = false; /** - * Creates a new form based on the given configuration. - * + * @var string|int|null + */ + private $name; + + /** + * @var bool Whether the form inherits its underlying data from its parent + */ + private $inheritData; + + /** + * @var PropertyPathInterface|null + */ + private $propertyPath; + + /** * @throws LogicException if a data mapper is not provided for a compound form */ public function __construct(FormConfigInterface $config) @@ -176,12 +168,13 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac // 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac */ public function getName() { - return $this->config->getName(); + return $this->name; } /** @@ -214,11 +207,11 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac */ 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac } 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac // 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac } // 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac */ 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac */ 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac */ 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac // 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac // 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 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac // the erroneous data is accessible on the form. // Forms that inherit data never set any data, because the getters // forward to the parent form's getters anyway. - if (null === $viewData && !$this->config->getInheritData()) { + if (null === $viewData && !$this->inheritData) { $viewData = $submittedData; } } @@ -755,8 +747,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac /** * Returns the button that was used to submit the form. * - * @return Button|null The clicked button or NULL if the form was not - * submitted + * @return ClickableInterface|null */ public function getClickedButton() { @@ -845,29 +836,6 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac throw new LogicException('You cannot add children to a simple form. Maybe you should set the option "compound" to true?'); } - // Obtain the view data - $viewData = null; - - // If setData() is currently being called, there is no need to call - // mapDataToForms() here, as mapDataToForms() is called at the end - // of setData() anyway. Not doing this check leads to an endless - // recursion when initializing the form lazily and an event listener - // (such as ResizeFormListener) adds fields depending on the data: - // - // * setData() is called, the form is not initialized yet - // * add() is called by the listener (setData() is not complete, so - // the form is still not initialized) - // * getViewData() is called - // * setData() is called since the form is not initialized yet - // * ... endless recursion ... - // - // Also skip data mapping if setData() has not been called yet. - // setData() will be called upon form initialization and data mapping - // will take place by then. - if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { - $viewData = $this->getViewData(); - } - if (!$child instanceof FormInterface) { if (!\is_string($child) && !\is_int($child)) { throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface'); @@ -897,10 +865,28 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $child->setParent($this); - if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { - $iterator = new InheritDataAwareIterator(new \ArrayIterator([$child->getName() => $child])); - $iterator = new \RecursiveIteratorIterator($iterator); - $this->config->getDataMapper()->mapDataToForms($viewData, $iterator); + // If setData() is currently being called, there is no need to call + // mapDataToForms() here, as mapDataToForms() is called at the end + // of setData() anyway. Not doing this check leads to an endless + // recursion when initializing the form lazily and an event listener + // (such as ResizeFormListener) adds fields depending on the data: + // + // * setData() is called, the form is not initialized yet + // * add() is called by the listener (setData() is not complete, so + // the form is still not initialized) + // * getViewData() is called + // * setData() is called since the form is not initialized yet + // * ... endless recursion ... + // + // Also skip data mapping if setData() has not been called yet. + // setData() will be called upon form initialization and data mapping + // will take place by then. + if (!$this->lockSetData && $this->defaultDataSet && !$this->inheritData) { + $viewData = $this->getViewData(); + $this->config->getDataMapper()->mapDataToForms( + $viewData, + new \RecursiveIteratorIterator(new InheritDataAwareIterator(new \ArrayIterator([$child->getName() => $child]))) + ); } return $this; @@ -1049,13 +1035,13 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac } /** - * Normalizes the value if a model transformer is set. + * Normalizes the underlying data if a model transformer is set. * * @param mixed $value The value to transform * * @return mixed * - * @throws TransformationFailedException If the value cannot be transformed to "normalized" format + * @throws TransformationFailedException If the underlying data cannot be transformed to "normalized" format */ private function modelToNorm($value) { @@ -1064,7 +1050,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException('Unable to transform value for property path "'.$this->getPropertyPath().'": '.$exception->getMessage(), $exception->getCode(), $exception); + throw new TransformationFailedException('Unable to transform data for property path "'.$this->getPropertyPath().'": '.$exception->getMessage(), $exception->getCode(), $exception); } return $value; @@ -1101,7 +1087,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac * * @return mixed * - * @throws TransformationFailedException If the value cannot be transformed to "view" format + * @throws TransformationFailedException If the normalized value cannot be transformed to "view" format */ private function normToView($value) { @@ -1110,12 +1096,12 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac // Only do this for simple forms, as the resulting value in // compound forms is passed to the data mapper and thus should // not be converted to a string before. - if (!$this->config->getViewTransformers() && !$this->config->getCompound()) { + if (!($transformers = $this->config->getViewTransformers()) && !$this->config->getCompound()) { return null === $value || is_scalar($value) ? (string) $value : $value; } try { - foreach ($this->config->getViewTransformers() as $transformer) { + foreach ($transformers as $transformer) { $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { @@ -1132,13 +1118,11 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac * * @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 a795a1e5fa..56cdbb6f14 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -72,10 +72,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB // Add to "children" to maintain order $this->children[$child] = null; - $this->unresolvedChildren[$child] = [ - 'type' => $type, - 'options' => $options, - ]; + $this->unresolvedChildren[$child] = [$type, $options]; return $this; } @@ -143,15 +140,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - if (isset($this->unresolvedChildren[$name])) { - return true; - } - - if (isset($this->children[$name])) { - return true; - } - - return false; + return isset($this->unresolvedChildren[$name]) || isset($this->children[$name]); } /** @@ -223,7 +212,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB /** * {@inheritdoc} * - * @return FormBuilderInterface[] + * @return FormBuilderInterface[]|\Traversable */ public function getIterator() { @@ -239,12 +228,11 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB */ private function resolveChild(string $name): FormBuilderInterface { - $info = $this->unresolvedChildren[$name]; - $child = $this->create($name, $info['type'], $info['options']); - $this->children[$name] = $child; + list($type, $options) = $this->unresolvedChildren[$name]; + unset($this->unresolvedChildren[$name]); - return $child; + return $this->children[$name] = $this->create($name, $type, $options); } /** @@ -253,7 +241,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB 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 1a581d9b8e..4a0f26784b 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -37,41 +37,18 @@ class FormConfigBuilder implements FormConfigBuilderInterface /** * @var bool */ - protected $locked = false; - /** - * @var EventDispatcherInterface - */ private $dispatcher; - - /** - * @var string - */ private $name; /** - * @var PropertyPathInterface + * @var PropertyPathInterface|string|null */ private $propertyPath; - /** - * @var bool - */ private $mapped = true; - - /** - * @var bool - */ private $byReference = true; - - /** - * @var bool - */ private $inheritData = false; - - /** - * @var bool - */ private $compound = false; /** @@ -79,34 +56,16 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $type; - /** - * @var array - */ private $viewTransformers = []; - - /** - * @var array - */ private $modelTransformers = []; /** - * @var DataMapperInterface + * @var DataMapperInterface|null */ private $dataMapper; - /** - * @var bool - */ private $required = true; - - /** - * @var bool - */ private $disabled = false; - - /** - * @var bool - */ private $errorBubbling = false; /** @@ -114,9 +73,6 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $emptyData; - /** - * @var array - */ private $attributes = []; /** @@ -129,39 +85,26 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $dataClass; - /** - * @var bool - */ - private $dataLocked; + private $dataLocked = false; /** - * @var FormFactoryInterface + * @var FormFactoryInterface|null */ private $formFactory; /** - * @var string + * @var string|null */ private $action; - /** - * @var string - */ private $method = 'POST'; /** - * @var RequestHandlerInterface + * @var RequestHandlerInterface|null */ private $requestHandler; - /** - * @var bool - */ private $autoInitialize = false; - - /** - * @var array - */ private $options; /** @@ -603,7 +546,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->errorBubbling = null === $errorBubbling ? null : (bool) $errorBubbling; + $this->errorBubbling = (bool) $errorBubbling; return $this; } @@ -649,7 +592,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->mapped = $mapped; + $this->mapped = (bool) $mapped; return $this; } @@ -663,7 +606,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->byReference = $byReference; + $this->byReference = (bool) $byReference; return $this; } @@ -677,7 +620,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->inheritData = $inheritData; + $this->inheritData = (bool) $inheritData; return $this; } @@ -691,7 +634,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->compound = $compound; + $this->compound = (bool) $compound; return $this; } @@ -733,7 +676,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - $this->dataLocked = $locked; + $this->dataLocked = (bool) $locked; return $this; } @@ -761,7 +704,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface throw new BadMethodCallException('The config builder cannot be modified anymore.'); } - $this->action = $action; + $this->action = (string) $action; return $this; } @@ -827,7 +770,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface /** * Validates whether the given variable is a valid form name. * - * @param string|int $name The tested form name + * @param string|int|null $name The tested form name * * @throws UnexpectedTypeException if the name is not a string or an integer * @throws InvalidArgumentException if the name contains invalid characters @@ -853,7 +796,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface * * 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 f422840a82..d516e41056 100644 --- a/src/Symfony/Component/Form/FormConfigBuilderInterface.php +++ b/src/Symfony/Component/Form/FormConfigBuilderInterface.php @@ -108,7 +108,7 @@ interface FormConfigBuilderInterface extends FormConfigInterface 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 @@ interface FormConfigBuilderInterface extends FormConfigInterface /** * 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 @@ interface FormConfigBuilderInterface extends FormConfigInterface public function setCompound($compound); /** - * Set the types. + * Sets the resolved type. * * @return $this The configuration object */ @@ -203,7 +202,7 @@ interface FormConfigBuilderInterface extends FormConfigInterface /** * 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 @@ interface FormConfigBuilderInterface extends FormConfigInterface * * 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 ce9171f3d4..7dbda33033 100644 --- a/src/Symfony/Component/Form/FormConfigInterface.php +++ b/src/Symfony/Component/Form/FormConfigInterface.php @@ -70,15 +70,17 @@ interface FormConfigInterface * 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 @@ interface FormConfigInterface 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 @@ interface FormConfigInterface 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 @@ interface FormConfigInterface 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 6724ddc03c..44cf040ae1 100644 --- a/src/Symfony/Component/Form/FormError.php +++ b/src/Symfony/Component/Form/FormError.php @@ -49,7 +49,7 @@ class FormError implements \Serializable */ public function __construct(?string $message, string $messageTemplate = null, array $messageParameters = [], int $messagePluralization = null, $cause = null) { - $this->message = $message; + $this->message = (string) $message; $this->messageTemplate = $messageTemplate ?: $message; $this->messageParameters = $messageParameters; $this->messagePluralization = $messagePluralization; diff --git a/src/Symfony/Component/Form/FormErrorIterator.php b/src/Symfony/Component/Form/FormErrorIterator.php index 2cc53c0923..db1d311a30 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 @@ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \Array 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 @@ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \Array /** * 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 c688a19566..3b6d484e75 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 c9b01736d4..27e61498e2 100644 --- a/src/Symfony/Component/Form/FormEvents.php +++ b/src/Symfony/Component/Form/FormEvents.php @@ -34,21 +34,35 @@ final class FormEvents const PRE_SUBMIT = 'form.pre_submit'; /** - * The SUBMIT event is dispatched just before the Form::submit() method - * transforms back the normalized data to the model and view data. + * The SUBMIT event is dispatched after the Form::submit() method + * has changed the view data by the request data, or submitted and mapped + * the children if the form is compound, and after reverse transformation + * to normalized representation. * - * It can be used to change data from the normalized representation of the data. + * It's also dispatched just before the Form::submit() method transforms back + * the normalized data to the model and view data. + * + * So at this stage children of compound forms are submitted and synchronized, unless + * their transformation failed, but a parent would still be at the PRE_SUBMIT level. + * + * Since the current form is not synchronized yet, it is still possible to add and + * remove fields. * * @Event("Symfony\Component\Form\FormEvent") */ const SUBMIT = 'form.submit'; /** - * The FormEvents::POST_SUBMIT event is dispatched after the Form::submit() - * once the model and view data have been denormalized. + * The FormEvents::POST_SUBMIT event is dispatched at the very end of the Form::submit(). + * + * It this stage the model and view data may have been denormalized. Otherwise the form + * is desynchronized because transformation failed during submission. * * It can be used to fetch data after denormalization. * + * The event attaches the current view data. To know whether this is the renormalized data + * or the invalid request data, call Form::isSynchronized() first. + * * @Event("Symfony\Component\Form\FormEvent") */ const POST_SUBMIT = 'form.post_submit'; @@ -58,7 +72,7 @@ final class FormEvents * * It can be used to: * - Modify the data given during pre-population; - * - Modify a form depending on the pre-populated data (adding or removing fields dynamically). + * - Keep synchronized the form depending on the data (adding or removing fields dynamically). * * @Event("Symfony\Component\Form\FormEvent") */ @@ -67,7 +81,8 @@ final class FormEvents /** * The FormEvents::POST_SET_DATA event is dispatched at the end of the Form::setData() method. * - * This event is mostly here for reading data after having pre-populated the form. + * This event can be used to modify the form depending on the final state of the underlying data + * accessible in every representation: model, normalized and view. * * @Event("Symfony\Component\Form\FormEvent") */ diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 565b79af39..5ed0376ed6 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable * @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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable /** * Returns the form's configuration. * - * @return FormConfigInterface The configuration + * @return FormConfigInterface The configuration instance */ public function getConfig(); @@ -164,6 +192,8 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable /** * 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable /** * 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable * 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable * 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable /** * 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 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable 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 1446976191..cbb1d7a417 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 6635a63b94..4743ffea3f 100644 --- a/src/Symfony/Component/Form/FormRendererEngineInterface.php +++ b/src/Symfony/Component/Form/FormRendererEngineInterface.php @@ -74,7 +74,7 @@ interface FormRendererEngineInterface * 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 @@ interface FormRendererEngineInterface * 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 68097dc98b..d84ef4c174 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 6521ea47ca..3be9a0c9f8 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserInterface.php +++ b/src/Symfony/Component/Form/FormTypeGuesserInterface.php @@ -49,8 +49,8 @@ interface FormTypeGuesserInterface /** * 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 cfccf180b9..29283c7720 100644 --- a/src/Symfony/Component/Form/NativeRequestHandler.php +++ b/src/Symfony/Component/Form/NativeRequestHandler.php @@ -41,6 +41,8 @@ class NativeRequestHandler implements RequestHandlerInterface /** * {@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 aa3268c262..642b25ee12 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -450,6 +450,31 @@ class CompoundFormTest extends AbstractFormTest $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 3b8afe415e..fd8716d7b8 100644 --- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php +++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -54,6 +54,26 @@ class SimpleFormTest_Traversable implements \IteratorAggregate 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 @@ class SimpleFormTest extends AbstractFormTest /** * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException - * @expectedExceptionMessage Unable to transform value for property path "name": No mapping for value "arg" + * @expectedExceptionMessage Unable to transform data for property path "name": No mapping for value "arg" */ public function testDataTransformationFailure() { diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index a51d9ecb88..6659e21793 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\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\HttpKernel\Event\KernelEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -40,13 +41,13 @@ class DebugHandlersListener implements EventSubscriberInterface private $hasTerminatedWithException; /** - * @param callable|null $exceptionHandler A handler that will be called on Exception - * @param LoggerInterface|null $logger A PSR-3 logger - * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants - * @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value - * @param bool $scream Enables/disables screaming mode, where even silenced errors are logged - * @param string|array $fileLinkFormat The format for links to source files - * @param bool $scope Enables/disables scoping mode + * @param callable|null $exceptionHandler A handler that will be called on Exception + * @param LoggerInterface|null $logger A PSR-3 logger + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value + * @param bool $scream Enables/disables screaming mode, where even silenced errors are logged + * @param string|FileLinkFormatter|null $fileLinkFormat The format for links to source files + * @param bool $scope Enables/disables scoping mode */ public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true) { diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php index 3f8b803f78..349698eb1a 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -232,12 +232,13 @@ abstract class Constraint * * Override this method to define a default option. * - * @return string + * @return string|null * * @see __construct() */ public function getDefaultOption() { + return null; } /**